Gekidou - Sidebar UI components (#5955)
* Squash, rebase, test fixes * PR Feedback addressed * fix snapshot tests with SafeAreaProvider * Addresses feedback * Adds Category & Channel error * Tests fixed * MyChannel count * finalize DM and GM avatars and icon as well as other small fixes Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
parent
bd7ce77197
commit
066775ca82
43 changed files with 1335 additions and 956 deletions
|
|
@ -14,6 +14,7 @@ import NetworkManager from '@init/network_manager';
|
|||
import {prepareMyChannelsForTeam, queryChannelById, queryChannelByName, queryMyChannel} from '@queries/servers/channel';
|
||||
import {queryCommonSystemValues, queryCurrentTeamId, queryCurrentUserId} from '@queries/servers/system';
|
||||
import {prepareMyTeams, queryNthLastChannelFromTeam, queryMyTeamById, queryTeamById, queryTeamByName} from '@queries/servers/team';
|
||||
import {queryCurrentUser} from '@queries/servers/user';
|
||||
import {getDirectChannelName} from '@utils/channel';
|
||||
import {PERMALINK_GENERIC_TEAM_NAME_REDIRECT} from '@utils/url';
|
||||
import {displayGroupMessageName, displayUsername} from '@utils/user';
|
||||
|
|
@ -267,9 +268,9 @@ export const fetchMyChannel = async (serverUrl: string, teamId: string, channelI
|
|||
}
|
||||
};
|
||||
|
||||
export const fetchMissingSidebarInfo = async (serverUrl: string, directChannels: Channel[], locale?: string, teammateDisplayNameSetting?: string, exludeUserId?: string, fetchOnly = false) => {
|
||||
export const fetchMissingSidebarInfo = async (serverUrl: string, directChannels: Channel[], locale?: string, teammateDisplayNameSetting?: string, currentUserId?: string, fetchOnly = false) => {
|
||||
const channelIds = directChannels.sort((a, b) => b.last_post_at - a.last_post_at).map((dc) => dc.id);
|
||||
const result = await fetchProfilesPerChannels(serverUrl, channelIds, exludeUserId, false);
|
||||
const result = await fetchProfilesPerChannels(serverUrl, channelIds, currentUserId, false);
|
||||
if (result.error) {
|
||||
return {error: result.error};
|
||||
}
|
||||
|
|
@ -280,7 +281,7 @@ export const fetchMissingSidebarInfo = async (serverUrl: string, directChannels:
|
|||
result.data.forEach((data) => {
|
||||
if (data.users) {
|
||||
if (data.users.length > 1) {
|
||||
displayNameByChannel[data.channelId] = displayGroupMessageName(data.users, locale, teammateDisplayNameSetting, exludeUserId);
|
||||
displayNameByChannel[data.channelId] = displayGroupMessageName(data.users, locale, teammateDisplayNameSetting, currentUserId);
|
||||
} else {
|
||||
displayNameByChannel[data.channelId] = displayUsername(data.users[0], locale, teammateDisplayNameSetting);
|
||||
}
|
||||
|
|
@ -295,6 +296,15 @@ export const fetchMissingSidebarInfo = async (serverUrl: string, directChannels:
|
|||
}
|
||||
});
|
||||
|
||||
if (currentUserId) {
|
||||
const ownDirectChannel = directChannels.find((dm) => dm.name === getDirectChannelName(currentUserId, currentUserId));
|
||||
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
|
||||
if (ownDirectChannel && database) {
|
||||
const currentUser = await queryCurrentUser(database);
|
||||
ownDirectChannel.display_name = displayUsername(currentUser, locale, teammateDisplayNameSetting);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fetchOnly) {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (operator) {
|
||||
|
|
|
|||
37
app/components/channel_icon/dm_avatar/dm_avatar.tsx
Normal file
37
app/components/channel_icon/dm_avatar/dm_avatar.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import ProfilePicture from '@components/profile_picture';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
type Props = {
|
||||
author?: UserModel;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
status: {
|
||||
backgroundColor: theme.sidebarBg,
|
||||
borderWidth: 0,
|
||||
},
|
||||
}));
|
||||
|
||||
const DmAvatar = ({author}: Props) => {
|
||||
const theme = useTheme();
|
||||
const style = getStyleSheet(theme);
|
||||
return (
|
||||
<ProfilePicture
|
||||
author={author}
|
||||
size={24}
|
||||
showStatus={true}
|
||||
statusSize={12}
|
||||
statusStyle={style.status}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DmAvatar;
|
||||
45
app/components/channel_icon/dm_avatar/index.ts
Normal file
45
app/components/channel_icon/dm_avatar/index.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Q} from '@nozbe/watermelondb';
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import {getUserIdFromChannelName} from '@utils/user';
|
||||
|
||||
import DmAvatar from './dm_avatar';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type SystemModel from '@typings/database/models/servers/system';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
const {SERVER: {USER, SYSTEM}} = MM_TABLES;
|
||||
const {CURRENT_USER_ID} = SYSTEM_IDENTIFIERS;
|
||||
|
||||
const enhance = withObservables(['channelName'], ({channelName, database}: {channelName: string} & WithDatabaseArgs) => {
|
||||
const currentUserId = database.get<SystemModel>(SYSTEM).findAndObserve(CURRENT_USER_ID).pipe(
|
||||
switchMap(({value}) => of$(value)),
|
||||
);
|
||||
|
||||
const authorId = currentUserId.pipe(
|
||||
switchMap((userId) => of$(getUserIdFromChannelName(userId, channelName))),
|
||||
);
|
||||
|
||||
const author = authorId.pipe(
|
||||
switchMap((id) => {
|
||||
return database.get<UserModel>(USER).query(Q.where('id', id)).observe().pipe(
|
||||
// eslint-disable-next-line max-nested-callbacks
|
||||
switchMap((users) => (users[0] ? of$(users[0]) : of$(undefined))),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
author,
|
||||
};
|
||||
});
|
||||
|
||||
export default withDatabase(enhance(DmAvatar));
|
||||
|
|
@ -8,6 +8,9 @@ import CompassIcon from '@components/compass_icon';
|
|||
import General from '@constants/general';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import DmAvatar from './dm_avatar';
|
||||
|
||||
type ChannelIconProps = {
|
||||
hasDraft?: boolean;
|
||||
|
|
@ -16,6 +19,7 @@ type ChannelIconProps = {
|
|||
isInfo?: boolean;
|
||||
isUnread?: boolean;
|
||||
membersCount?: number;
|
||||
name: string;
|
||||
shared: boolean;
|
||||
size?: number;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
|
|
@ -58,8 +62,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
|||
},
|
||||
group: {
|
||||
color: theme.sidebarText,
|
||||
fontSize: 10,
|
||||
fontFamily: 'OpenSans-SemiBold',
|
||||
...typography('Body', 75, 'SemiBold'),
|
||||
},
|
||||
groupActive: {
|
||||
color: theme.sidebarTextActiveColor,
|
||||
|
|
@ -80,6 +83,7 @@ const ChannelIcon = ({
|
|||
isInfo = false,
|
||||
isUnread = false,
|
||||
membersCount = 0,
|
||||
name,
|
||||
shared,
|
||||
size = 12,
|
||||
style,
|
||||
|
|
@ -159,10 +163,9 @@ const ChannelIcon = ({
|
|||
);
|
||||
} else if (type === General.GM_CHANNEL) {
|
||||
const fontSize = size - 12;
|
||||
const boxSize = size - 4;
|
||||
icon = (
|
||||
<View
|
||||
style={[styles.groupBox, unreadGroupBox, activeGroupBox, {width: boxSize, height: boxSize}]}
|
||||
style={[styles.groupBox, unreadGroupBox, activeGroupBox, {width: size, height: size}]}
|
||||
>
|
||||
<Text
|
||||
style={[styles.group, unreadGroup, activeGroup, {fontSize}]}
|
||||
|
|
@ -173,7 +176,7 @@ const ChannelIcon = ({
|
|||
</View>
|
||||
);
|
||||
} else if (type === General.DM_CHANNEL) {
|
||||
//todo: Implement ProfilePicture component
|
||||
icon = (<DmAvatar channelName={name}/>);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/channel_list should match snapshot 1`] = `
|
||||
exports[`components/channel_list should render channels error 1`] = `
|
||||
<View
|
||||
animatedStyle={
|
||||
Object {
|
||||
|
|
@ -15,7 +15,8 @@ exports[`components/channel_list should match snapshot 1`] = `
|
|||
"backgroundColor": "#1e325c",
|
||||
"flex": 1,
|
||||
"maxWidth": "100%",
|
||||
"paddingHorizontal": 20,
|
||||
"paddingLeft": 18,
|
||||
"paddingRight": 20,
|
||||
"paddingVertical": 10,
|
||||
}
|
||||
}
|
||||
|
|
@ -70,7 +71,299 @@ exports[`components/channel_list should match snapshot 1`] = `
|
|||
]
|
||||
}
|
||||
>
|
||||
Couldn't load
|
||||
Couldn't load Test Team!
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"marginTop": 4,
|
||||
"textAlign": "center",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
There was a problem loading content for this team.
|
||||
</Text>
|
||||
<View
|
||||
onMoveShouldSetResponder={[Function]}
|
||||
onMoveShouldSetResponderCapture={[Function]}
|
||||
onResponderEnd={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderReject={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderStart={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponderCapture={[Function]}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"marginTop": 24,
|
||||
},
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"borderRadius": 4,
|
||||
"flex": 0,
|
||||
"justifyContent": "center",
|
||||
},
|
||||
Object {
|
||||
"height": 48,
|
||||
"paddingHorizontal": 24,
|
||||
"paddingVertical": 14,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontWeight": "600",
|
||||
"justifyContent": "center",
|
||||
"padding": 1,
|
||||
"textAlignVertical": "center",
|
||||
},
|
||||
Object {
|
||||
"fontSize": 16,
|
||||
"lineHeight": 16,
|
||||
"marginTop": 1,
|
||||
},
|
||||
Object {
|
||||
"color": "#1c58d9",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
Retry
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`components/channel_list should render team error 1`] = `
|
||||
<View
|
||||
animatedStyle={
|
||||
Object {
|
||||
"value": Object {
|
||||
"maxWidth": "100%",
|
||||
},
|
||||
}
|
||||
}
|
||||
collapsable={false}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#1e325c",
|
||||
"flex": 1,
|
||||
"maxWidth": "100%",
|
||||
"paddingLeft": 18,
|
||||
"paddingRight": 20,
|
||||
"paddingVertical": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 8,
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"height": 40,
|
||||
"justifyContent": "flex-start",
|
||||
"marginVertical": 20,
|
||||
"maxHeight": 40,
|
||||
"padding": 8,
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="magnify"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"width": 24,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Find Channels"
|
||||
placeholderTextColor="rgba(255,255,255,0.72)"
|
||||
style={
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"textAlignVertical": "center",
|
||||
},
|
||||
],
|
||||
Object {
|
||||
"alignContent": "center",
|
||||
"alignItems": "center",
|
||||
"color": "#ffffff",
|
||||
"flex": 1,
|
||||
"height": 40,
|
||||
"marginLeft": 5,
|
||||
"marginTop": -2,
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
onMoveShouldSetResponder={[Function]}
|
||||
onMoveShouldSetResponderCapture={[Function]}
|
||||
onResponderEnd={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderReject={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderStart={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponderCapture={[Function]}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"display": "flex",
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="message-text-outline"
|
||||
style={
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
],
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
Threads
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"justifyContent": "center",
|
||||
"padding": 20,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "rgba(255,255,255,0.08)",
|
||||
"borderRadius": 60,
|
||||
"height": 120,
|
||||
"justifyContent": "center",
|
||||
"width": 120,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="alert-circle-outline"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.48)",
|
||||
"fontSize": 72,
|
||||
"lineHeight": 72,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "Metropolis-SemiBold",
|
||||
"fontSize": 20,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 28,
|
||||
},
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"marginTop": 20,
|
||||
"textAlign": "center",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
Couldn't load categories for
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
|
|
|
|||
|
|
@ -1,430 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Category List Component should match snapshot 1`] = `
|
||||
<RCTScrollView
|
||||
ListHeaderComponent={[Function]}
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"channels": Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"name": "Just a channel",
|
||||
},
|
||||
Object {
|
||||
"highlight": true,
|
||||
"id": "2",
|
||||
"name": "Highlighted!!!",
|
||||
},
|
||||
],
|
||||
"id": "1",
|
||||
"title": "My first Category",
|
||||
},
|
||||
Object {
|
||||
"channels": Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"name": "Just a channel",
|
||||
},
|
||||
Object {
|
||||
"highlight": true,
|
||||
"id": "2",
|
||||
"name": "Highlighted!!!",
|
||||
},
|
||||
],
|
||||
"id": "2",
|
||||
"title": "Another cat",
|
||||
},
|
||||
]
|
||||
}
|
||||
getItem={[Function]}
|
||||
getItemCount={[Function]}
|
||||
keyExtractor={[Function]}
|
||||
onContentSizeChange={[Function]}
|
||||
onLayout={[Function]}
|
||||
onMomentumScrollBegin={[Function]}
|
||||
onMomentumScrollEnd={[Function]}
|
||||
onScroll={[Function]}
|
||||
onScrollBeginDrag={[Function]}
|
||||
onScrollEndDrag={[Function]}
|
||||
removeClippedSubviews={false}
|
||||
renderItem={[Function]}
|
||||
scrollEventThrottle={50}
|
||||
stickyHeaderIndices={Array []}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
viewabilityConfigCallbackPairs={Array []}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
>
|
||||
<View
|
||||
onMoveShouldSetResponder={[Function]}
|
||||
onMoveShouldSetResponderCapture={[Function]}
|
||||
onResponderEnd={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderReject={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderStart={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponderCapture={[Function]}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"display": "flex",
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="message-text-outline"
|
||||
style={
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
],
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
Threads
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"marginTop": 12,
|
||||
"paddingVertical": 8,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.64)",
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 16,
|
||||
}
|
||||
}
|
||||
>
|
||||
MY FIRST CATEGORY
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"name": "Just a channel",
|
||||
},
|
||||
Object {
|
||||
"highlight": true,
|
||||
"id": "2",
|
||||
"name": "Highlighted!!!",
|
||||
},
|
||||
]
|
||||
}
|
||||
getItem={[Function]}
|
||||
getItemCount={[Function]}
|
||||
keyExtractor={[Function]}
|
||||
onContentSizeChange={[Function]}
|
||||
onLayout={[Function]}
|
||||
onMomentumScrollBegin={[Function]}
|
||||
onMomentumScrollEnd={[Function]}
|
||||
onScroll={[Function]}
|
||||
onScrollBeginDrag={[Function]}
|
||||
onScrollEndDrag={[Function]}
|
||||
removeClippedSubviews={false}
|
||||
renderItem={[Function]}
|
||||
scrollEventThrottle={50}
|
||||
stickyHeaderIndices={Array []}
|
||||
viewabilityConfigCallbackPairs={Array []}
|
||||
>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"marginTop": 1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
Just a channel
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"marginTop": 1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
Highlighted!!!
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"marginTop": 12,
|
||||
"paddingVertical": 8,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.64)",
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 16,
|
||||
}
|
||||
}
|
||||
>
|
||||
ANOTHER CAT
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"name": "Just a channel",
|
||||
},
|
||||
Object {
|
||||
"highlight": true,
|
||||
"id": "2",
|
||||
"name": "Highlighted!!!",
|
||||
},
|
||||
]
|
||||
}
|
||||
getItem={[Function]}
|
||||
getItemCount={[Function]}
|
||||
keyExtractor={[Function]}
|
||||
onContentSizeChange={[Function]}
|
||||
onLayout={[Function]}
|
||||
onMomentumScrollBegin={[Function]}
|
||||
onMomentumScrollEnd={[Function]}
|
||||
onScroll={[Function]}
|
||||
onScrollBeginDrag={[Function]}
|
||||
onScrollEndDrag={[Function]}
|
||||
removeClippedSubviews={false}
|
||||
renderItem={[Function]}
|
||||
scrollEventThrottle={50}
|
||||
stickyHeaderIndices={Array []}
|
||||
viewabilityConfigCallbackPairs={Array []}
|
||||
>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"marginTop": 1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
Just a channel
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"marginTop": 1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
Highlighted!!!
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</RCTScrollView>
|
||||
`;
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/channel_list/categories/body should match snapshot 1`] = `
|
||||
Object {
|
||||
"children": Array [
|
||||
<View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<RNGestureHandlerButton
|
||||
collapsable={false}
|
||||
exclusive={true}
|
||||
onGestureEvent={[Function]}
|
||||
onGestureHandlerEvent={[Function]}
|
||||
onGestureHandlerStateChange={[Function]}
|
||||
onHandlerStateChange={[Function]}
|
||||
rippleColor={0}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"marginBottom": 8,
|
||||
"paddingLeft": 2,
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"justifyContent": "center",
|
||||
},
|
||||
Object {
|
||||
"height": 24,
|
||||
"width": 24,
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.4)",
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
Object {
|
||||
"fontSize": 24,
|
||||
"left": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
testID="undefined.public"
|
||||
/>
|
||||
</View>
|
||||
<Text
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"flex": 1,
|
||||
"marginTop": -1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
false,
|
||||
]
|
||||
}
|
||||
>
|
||||
Channel
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</RNGestureHandlerButton>
|
||||
</View>
|
||||
</View>,
|
||||
],
|
||||
"props": Object {
|
||||
"data": Anything,
|
||||
"getItem": [Function],
|
||||
"getItemCount": [Function],
|
||||
"invertStickyHeaders": undefined,
|
||||
"keyExtractor": [Function],
|
||||
"onContentSizeChange": [Function],
|
||||
"onLayout": [Function],
|
||||
"onMomentumScrollBegin": [Function],
|
||||
"onMomentumScrollEnd": [Function],
|
||||
"onScroll": [Function],
|
||||
"onScrollBeginDrag": [Function],
|
||||
"onScrollEndDrag": [Function],
|
||||
"removeClippedSubviews": false,
|
||||
"renderItem": [Function],
|
||||
"scrollEventThrottle": 50,
|
||||
"stickyHeaderIndices": Array [],
|
||||
"style": undefined,
|
||||
"viewabilityConfigCallbackPairs": Array [],
|
||||
},
|
||||
"type": "RCTScrollView",
|
||||
}
|
||||
`;
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Category Body Component should match snapshot 1`] = `
|
||||
<RCTScrollView
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"id": "1",
|
||||
"name": "Just a channel",
|
||||
},
|
||||
Object {
|
||||
"highlight": true,
|
||||
"id": "2",
|
||||
"name": "Highlighted!!!",
|
||||
},
|
||||
]
|
||||
}
|
||||
getItem={[Function]}
|
||||
getItemCount={[Function]}
|
||||
keyExtractor={[Function]}
|
||||
onContentSizeChange={[Function]}
|
||||
onLayout={[Function]}
|
||||
onMomentumScrollBegin={[Function]}
|
||||
onMomentumScrollEnd={[Function]}
|
||||
onScroll={[Function]}
|
||||
onScrollBeginDrag={[Function]}
|
||||
onScrollEndDrag={[Function]}
|
||||
removeClippedSubviews={false}
|
||||
renderItem={[Function]}
|
||||
scrollEventThrottle={50}
|
||||
stickyHeaderIndices={Array []}
|
||||
viewabilityConfigCallbackPairs={Array []}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"marginTop": 1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
Just a channel
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"marginTop": 1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
Highlighted!!!
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</RCTScrollView>
|
||||
`;
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Database, Q} from '@nozbe/watermelondb';
|
||||
import React from 'react';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import CategoryBody from './category_body';
|
||||
|
||||
import type CategoryModel from '@typings/database/models/servers/category';
|
||||
import type CategoryChannelModel from '@typings/database/models/servers/category_channel';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type MyChannelModel from '@typings/database/models/servers/my_channel';
|
||||
|
||||
const {SERVER: {CATEGORY, CATEGORY_CHANNEL, CHANNEL, MY_CHANNEL}} = MM_TABLES;
|
||||
|
||||
describe('components/channel_list/categories/body', () => {
|
||||
let database: Database;
|
||||
let category: CategoryModel;
|
||||
let categoryChannels: CategoryChannelModel[];
|
||||
let myChannels: MyChannelModel[];
|
||||
let channels: ChannelModel[];
|
||||
|
||||
beforeAll(async () => {
|
||||
const server = await TestHelper.setupServerDatabase();
|
||||
database = server.database;
|
||||
|
||||
const categories = await database.get<CategoryModel>(CATEGORY).query(
|
||||
Q.take(1),
|
||||
).fetch();
|
||||
|
||||
category = categories[0];
|
||||
|
||||
categoryChannels = await database.get<CategoryChannelModel>(CATEGORY_CHANNEL).query(
|
||||
Q.where('category_id', category.id),
|
||||
).fetch();
|
||||
|
||||
const channelIds = await database.get<ChannelModel>(CHANNEL).query(
|
||||
Q.on(CATEGORY_CHANNEL, 'category_id', category.id),
|
||||
).fetchIds();
|
||||
|
||||
myChannels = await database.get<MyChannelModel>(MY_CHANNEL).query(
|
||||
Q.where('id', Q.oneOf(channelIds)),
|
||||
).fetch();
|
||||
});
|
||||
|
||||
it('should match snapshot', () => {
|
||||
const wrapper = renderWithEverything(
|
||||
<CategoryBody
|
||||
category={category}
|
||||
myChannels={myChannels}
|
||||
categoryChannels={categoryChannels}
|
||||
channels={channels}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
expect(wrapper.toJSON()).toMatchSnapshot({
|
||||
props: {data: expect.anything()},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useMemo} from 'react';
|
||||
import {FlatList} from 'react-native';
|
||||
|
||||
import ChannelListItem from './channel';
|
||||
|
||||
import type CategoryModel from '@typings/database/models/servers/category';
|
||||
import type CategoryChannelModel from '@typings/database/models/servers/category_channel';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type MyChannelModel from '@typings/database/models/servers/my_channel';
|
||||
|
||||
type Props = {
|
||||
category: CategoryModel;
|
||||
channels: ChannelModel[];
|
||||
myChannels: MyChannelModel[];
|
||||
categoryChannels: CategoryChannelModel[];
|
||||
};
|
||||
|
||||
const ChannelItem = ({item}: {item: string}) => {
|
||||
return (
|
||||
<ChannelListItem channelId={item}/>
|
||||
);
|
||||
};
|
||||
|
||||
const CategoryBody = ({category, categoryChannels, channels, myChannels}: Props) => {
|
||||
const data: string[] = useMemo(() => {
|
||||
switch (category.sorting) {
|
||||
case 'alpha':
|
||||
return channels.map((c) => c.id);
|
||||
case 'manual':
|
||||
return categoryChannels.map((cc) => cc.channelId);
|
||||
default:
|
||||
return myChannels.map((m) => m.id);
|
||||
}
|
||||
}, [category.sorting, categoryChannels, channels, myChannels]);
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={data}
|
||||
renderItem={ChannelItem}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CategoryBody;
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/channel_list/categories/body/channel/item should match snapshot 1`] = `
|
||||
<RNGestureHandlerButton
|
||||
collapsable={false}
|
||||
exclusive={true}
|
||||
onGestureEvent={[Function]}
|
||||
onGestureHandlerEvent={[Function]}
|
||||
onGestureHandlerStateChange={[Function]}
|
||||
onHandlerStateChange={[Function]}
|
||||
rippleColor={0}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"marginBottom": 8,
|
||||
"paddingLeft": 2,
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"justifyContent": "center",
|
||||
},
|
||||
Object {
|
||||
"height": 24,
|
||||
"width": 24,
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "rgba(255,255,255,0.16)",
|
||||
"borderRadius": 4,
|
||||
"justifyContent": "center",
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
Object {
|
||||
"height": 24,
|
||||
"width": 24,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 16,
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
Object {
|
||||
"fontSize": 12,
|
||||
},
|
||||
]
|
||||
}
|
||||
testID="undefined.gm_member_count"
|
||||
>
|
||||
1
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Text
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"flex": 1,
|
||||
"marginTop": -1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
false,
|
||||
]
|
||||
}
|
||||
>
|
||||
Hello!
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</RNGestureHandlerButton>
|
||||
`;
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Category Channel List Item should match snapshot 1`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="globe"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"fontSize": 24,
|
||||
"lineHeight": 28,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.72)",
|
||||
"marginTop": 1,
|
||||
"paddingLeft": 12,
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
Channel Name
|
||||
</Text>
|
||||
</View>
|
||||
`;
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Database, Q} from '@nozbe/watermelondb';
|
||||
import React from 'react';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import {renderWithIntlAndTheme} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import ChannelListItem from './channel_list_item';
|
||||
|
||||
import type MyChannelModel from '@typings/database/models/servers/my_channel';
|
||||
|
||||
describe('components/channel_list/categories/body/channel/item', () => {
|
||||
let database: Database;
|
||||
let myChannel: MyChannelModel;
|
||||
|
||||
beforeAll(async () => {
|
||||
const server = await TestHelper.setupServerDatabase();
|
||||
database = server.database;
|
||||
|
||||
const myChannels = await database.get<MyChannelModel>(MM_TABLES.SERVER.MY_CHANNEL).query(
|
||||
Q.take(1),
|
||||
).fetch();
|
||||
|
||||
myChannel = myChannels[0];
|
||||
});
|
||||
|
||||
it('should match snapshot', () => {
|
||||
const wrapper = renderWithIntlAndTheme(
|
||||
<ChannelListItem
|
||||
channel={{displayName: 'Hello!', type: 'G', shared: false, name: 'hello'}}
|
||||
isOwnDirectMessage={false}
|
||||
myChannel={myChannel}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useMemo} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {StyleSheet, Text, View} from 'react-native';
|
||||
import {TouchableOpacity} from 'react-native-gesture-handler';
|
||||
|
||||
import {switchToChannelById} from '@actions/remote/channel';
|
||||
import ChannelIcon from '@components/channel_icon';
|
||||
import {General} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type MyChannelModel from '@typings/database/models/servers/my_channel';
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
marginBottom: 8,
|
||||
paddingLeft: 2,
|
||||
paddingVertical: 4,
|
||||
},
|
||||
icon: {
|
||||
fontSize: 24,
|
||||
lineHeight: 28,
|
||||
color: changeOpacity(theme.sidebarText, 0.72),
|
||||
},
|
||||
text: {
|
||||
marginTop: -1,
|
||||
color: changeOpacity(theme.sidebarText, 0.72),
|
||||
paddingLeft: 12,
|
||||
flex: 1,
|
||||
},
|
||||
highlight: {
|
||||
color: theme.sidebarText,
|
||||
},
|
||||
}));
|
||||
|
||||
const textStyle = StyleSheet.create({
|
||||
bright: typography('Body', 200, 'SemiBold'),
|
||||
regular: typography('Body', 200, 'Regular'),
|
||||
});
|
||||
|
||||
type Props = {
|
||||
channel: Pick<ChannelModel, 'displayName' | 'name' | 'shared' | 'type'>;
|
||||
isOwnDirectMessage: boolean;
|
||||
myChannel: MyChannelModel;
|
||||
}
|
||||
|
||||
const ChannelListItem = ({channel, isOwnDirectMessage, myChannel}: Props) => {
|
||||
const {formatMessage} = useIntl();
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
const serverUrl = useServerUrl();
|
||||
|
||||
// Make it brighter if it's highlighted, or has unreads
|
||||
const bright = myChannel.isUnread || myChannel.mentionsCount > 0;
|
||||
|
||||
const switchChannels = () => switchToChannelById(serverUrl, myChannel.id);
|
||||
const membersCount = useMemo(() => {
|
||||
if (channel.type === General.GM_CHANNEL) {
|
||||
return channel.displayName?.split(',').length;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}, [channel.type, channel.displayName]);
|
||||
|
||||
const textStyles = useMemo(() => [
|
||||
bright ? textStyle.bright : textStyle.regular,
|
||||
styles.text,
|
||||
bright && styles.highlight,
|
||||
], [bright, styles]);
|
||||
|
||||
let displayName = channel.displayName;
|
||||
if (isOwnDirectMessage) {
|
||||
displayName = formatMessage({id: 'channel_header.directchannel.you', defaultMessage: '{displayName} (you)'}, {displayName});
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={switchChannels}>
|
||||
<View style={styles.container}>
|
||||
<ChannelIcon
|
||||
membersCount={membersCount}
|
||||
name={channel.name}
|
||||
shared={channel.shared}
|
||||
size={24}
|
||||
type={channel.type}
|
||||
/>
|
||||
<Text
|
||||
ellipsizeMode='tail'
|
||||
numberOfLines={1}
|
||||
style={textStyles}
|
||||
>
|
||||
{displayName}
|
||||
</Text>
|
||||
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChannelListItem;
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {renderWithIntlAndTheme} from '@test/intl-test-helper';
|
||||
|
||||
import ChannelItem from './index';
|
||||
|
||||
test('Category Channel List Item should match snapshot', () => {
|
||||
const {toJSON} = renderWithIntlAndTheme(
|
||||
<ChannelItem
|
||||
icon={'globe'}
|
||||
name='Channel Name'
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(toJSON()).toMatchSnapshot();
|
||||
});
|
||||
54
app/components/channel_list/categories/body/channel/index.ts
Normal file
54
app/components/channel_list/categories/body/channel/index.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {combineLatest, of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {General} from '@constants';
|
||||
import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import {getUserIdFromChannelName} from '@utils/user';
|
||||
|
||||
import ChannelListItem from './channel_list_item';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type MyChannelModel from '@typings/database/models/servers/my_channel';
|
||||
import type SystemModel from '@typings/database/models/servers/system';
|
||||
|
||||
const {SERVER: {MY_CHANNEL, SYSTEM}} = MM_TABLES;
|
||||
const {CURRENT_USER_ID} = SYSTEM_IDENTIFIERS;
|
||||
|
||||
const enhance = withObservables(['channelId'], ({channelId, database}: {channelId: string} & WithDatabaseArgs) => {
|
||||
const myChannel = database.get<MyChannelModel>(MY_CHANNEL).findAndObserve(channelId);
|
||||
const currentUserId = database.get<SystemModel>(SYSTEM).findAndObserve(CURRENT_USER_ID).pipe(
|
||||
switchMap(({value}) => of$(value)),
|
||||
);
|
||||
|
||||
const channel = myChannel.pipe(switchMap((my) => my.channel.observe()));
|
||||
const isOwnDirectMessage = combineLatest([currentUserId, channel]).pipe(
|
||||
switchMap(([userId, ch]) => {
|
||||
if (ch?.type === General.DM_CHANNEL) {
|
||||
const teammateId = getUserIdFromChannelName(userId, ch.name);
|
||||
return of$(userId === teammateId);
|
||||
}
|
||||
|
||||
return of$(false);
|
||||
}),
|
||||
);
|
||||
return {
|
||||
isOwnDirectMessage,
|
||||
myChannel,
|
||||
channel: channel.pipe(
|
||||
switchMap((c: ChannelModel) => of$({
|
||||
displayName: c.displayName,
|
||||
name: c.name,
|
||||
shared: c.shared,
|
||||
type: c.type,
|
||||
})),
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
export default withDatabase(enhance(ChannelListItem));
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {StyleSheet, Text, View} from 'react-native';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
container: {
|
||||
paddingVertical: 4,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
icon: {
|
||||
fontSize: 24,
|
||||
lineHeight: 28,
|
||||
color: changeOpacity(theme.sidebarText, 0.72),
|
||||
},
|
||||
text: {
|
||||
marginTop: 1,
|
||||
color: changeOpacity(theme.sidebarText, 0.72),
|
||||
paddingLeft: 12,
|
||||
},
|
||||
highlight: {
|
||||
color: theme.sidebarText,
|
||||
},
|
||||
}));
|
||||
|
||||
const textStyle = StyleSheet.create({
|
||||
bright: typography('Body', 200, 'SemiBold'),
|
||||
regular: typography('Body', 200, 'Regular'),
|
||||
});
|
||||
|
||||
type Props = {
|
||||
unreadCount?: number;
|
||||
highlight?: boolean;
|
||||
name: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
const ChannelListItem = (props: Props) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
const {unreadCount, highlight, name, icon} = props;
|
||||
|
||||
// Make it brighter if it's highlighted, or has unreads
|
||||
const bright = highlight || (unreadCount && unreadCount > 0);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{icon && (
|
||||
<CompassIcon
|
||||
name={icon}
|
||||
style={styles.icon}
|
||||
/>
|
||||
)}
|
||||
<Text
|
||||
style={[
|
||||
bright ? textStyle.bright : textStyle.regular,
|
||||
styles.text,
|
||||
bright && styles.highlight,
|
||||
]}
|
||||
>
|
||||
{name}
|
||||
</Text>
|
||||
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChannelListItem;
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {renderWithIntlAndTheme} from '@test/intl-test-helper';
|
||||
|
||||
import CategoryBody from './index';
|
||||
|
||||
const channels: TempoChannel[] = [
|
||||
{id: '1', name: 'Just a channel'},
|
||||
{id: '2', name: 'Highlighted!!!', highlight: true},
|
||||
];
|
||||
|
||||
test('Category Body Component should match snapshot', () => {
|
||||
const {toJSON} = renderWithIntlAndTheme(
|
||||
<CategoryBody channels={channels}/>,
|
||||
);
|
||||
|
||||
expect(toJSON()).toMatchSnapshot();
|
||||
});
|
||||
17
app/components/channel_list/categories/body/index.ts
Normal file
17
app/components/channel_list/categories/body/index.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
|
||||
import CategoryBody from './category_body';
|
||||
|
||||
import type CategoryModel from '@typings/database/models/servers/category';
|
||||
|
||||
const withCategory = withObservables(['category'], ({category}: {category: CategoryModel}) => ({
|
||||
category,
|
||||
categoryChannels: category.categoryChannelsBySortOrder.observeWithColumns(['sort_order']),
|
||||
myChannels: category.myChannels.observeWithColumns(['last_post_at']),
|
||||
channels: category.channels.observeWithColumns(['display_name']),
|
||||
}));
|
||||
|
||||
export default withCategory(CategoryBody);
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {FlatList} from 'react-native';
|
||||
|
||||
import ChannelListItem from './channel';
|
||||
|
||||
const renderChannelItem = (data: { item: TempoChannel }) => {
|
||||
return (
|
||||
<ChannelListItem
|
||||
icon={'globe'}
|
||||
name={data.item.name}
|
||||
highlight={data.item.highlight}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type Props = {
|
||||
channels: TempoChannel[];
|
||||
};
|
||||
|
||||
const CategoryBody = (props: Props) => {
|
||||
return (
|
||||
<FlatList
|
||||
data={props.channels}
|
||||
renderItem={renderChannelItem}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CategoryBody;
|
||||
27
app/components/channel_list/categories/categories.test.tsx
Normal file
27
app/components/channel_list/categories/categories.test.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import Database from '@nozbe/watermelondb/Database';
|
||||
import React from 'react';
|
||||
|
||||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import Categories from './';
|
||||
|
||||
describe('components/channel_list/categories', () => {
|
||||
let database: Database;
|
||||
beforeAll(async () => {
|
||||
const server = await TestHelper.setupServerDatabase();
|
||||
database = server.database;
|
||||
});
|
||||
|
||||
it('render without error', () => {
|
||||
const wrapper = renderWithEverything(
|
||||
<Categories currentTeamId={TestHelper.basicTeam!.id}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(wrapper.toJSON()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
51
app/components/channel_list/categories/categories.tsx
Normal file
51
app/components/channel_list/categories/categories.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {FlatList, StyleSheet} from 'react-native';
|
||||
|
||||
import CategoryBody from './body';
|
||||
import LoadCategoriesError from './error';
|
||||
import CategoryHeader from './header';
|
||||
|
||||
import type {CategoryModel} from '@database/models/server';
|
||||
|
||||
type Props = {
|
||||
categories: CategoryModel[];
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flex: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const renderCategory = (data: {item: CategoryModel}) => {
|
||||
return (
|
||||
<>
|
||||
<CategoryHeader category={data.item}/>
|
||||
<CategoryBody category={data.item}/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Categories = (props: Props) => {
|
||||
if (!props.categories.length) {
|
||||
return <LoadCategoriesError/>;
|
||||
}
|
||||
|
||||
// Sort Categories
|
||||
props.categories.sort((a, b) => a.sortOrder - b.sortOrder);
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={props.categories}
|
||||
renderItem={renderCategory}
|
||||
style={styles.flex}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
showsVerticalScrollIndicator={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Categories;
|
||||
36
app/components/channel_list/categories/error.tsx
Normal file
36
app/components/channel_list/categories/error.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback, useState} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
|
||||
import {retryInitialTeamAndChannel} from '@actions/remote/retry';
|
||||
import LoadingError from '@components/channel_list/loading_error';
|
||||
import {useServerDisplayName, useServerUrl} from '@context/server';
|
||||
|
||||
const LoadCategoriesError = () => {
|
||||
const {formatMessage} = useIntl();
|
||||
const serverUrl = useServerUrl();
|
||||
const serverName = useServerDisplayName();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const onRetryTeams = useCallback(async () => {
|
||||
setLoading(true);
|
||||
const {error} = await retryInitialTeamAndChannel(serverUrl);
|
||||
|
||||
if (error) {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<LoadingError
|
||||
loading={loading}
|
||||
message={formatMessage({id: 'load_categories_error.message', defaultMessage: 'There was a problem loading content for this server.'})}
|
||||
onRetry={onRetryTeams}
|
||||
title={formatMessage({id: 'load_categories_error.title', defaultMessage: "Couldn't load categories for {serverName}"}, {serverName})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadCategoriesError;
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Category Header Component should match snapshot 1`] = `
|
||||
exports[`components/channel_list/categories/header should match snapshot 1`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"marginTop": 12,
|
||||
"paddingLeft": 2,
|
||||
"paddingVertical": 8,
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +21,7 @@ exports[`Category Header Component should match snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
CAT HEADING
|
||||
TEST CATEGORY
|
||||
</Text>
|
||||
</View>
|
||||
`;
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Database, Q} from '@nozbe/watermelondb';
|
||||
import React from 'react';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import {renderWithIntlAndTheme} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import CategoryHeader from './header';
|
||||
|
||||
import type CategoryModel from '@typings/database/models/servers/category';
|
||||
|
||||
describe('components/channel_list/categories/header', () => {
|
||||
let database: Database;
|
||||
let category: CategoryModel;
|
||||
beforeAll(async () => {
|
||||
const server = await TestHelper.setupServerDatabase();
|
||||
|
||||
database = server.database;
|
||||
const categories = await database.get<CategoryModel>(MM_TABLES.SERVER.CATEGORY).query(
|
||||
Q.take(1),
|
||||
).fetch();
|
||||
|
||||
category = categories[0];
|
||||
});
|
||||
|
||||
it('should match snapshot', () => {
|
||||
const wrapper = renderWithIntlAndTheme(
|
||||
<CategoryHeader
|
||||
category={category}
|
||||
hasChannels={true}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -8,10 +8,13 @@ import {useTheme} from '@context/theme';
|
|||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import type CategoryModel from '@typings/database/models/servers/category';
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
container: {
|
||||
paddingVertical: 8,
|
||||
marginTop: 12,
|
||||
paddingLeft: 2,
|
||||
},
|
||||
heading: {
|
||||
color: changeOpacity(theme.sidebarText, 0.64),
|
||||
|
|
@ -20,17 +23,23 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
|||
}));
|
||||
|
||||
type Props = {
|
||||
heading: string;
|
||||
category: CategoryModel;
|
||||
hasChannels: boolean;
|
||||
}
|
||||
|
||||
const CategoryHeader = (props: Props) => {
|
||||
const CategoryHeader = ({category, hasChannels}: Props) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
// Hide favs if empty
|
||||
if (!hasChannels && category.type === 'favorites') {
|
||||
return (null);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.heading}>
|
||||
{props.heading.toUpperCase()}
|
||||
{category.displayName.toUpperCase()}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {renderWithIntlAndTheme} from '@test/intl-test-helper';
|
||||
|
||||
import Header from './index';
|
||||
|
||||
test('Category Header Component should match snapshot', () => {
|
||||
const {toJSON} = renderWithIntlAndTheme(
|
||||
<Header heading='Cat Heading'/>,
|
||||
);
|
||||
|
||||
expect(toJSON()).toMatchSnapshot();
|
||||
});
|
||||
15
app/components/channel_list/categories/header/index.ts
Normal file
15
app/components/channel_list/categories/header/index.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
|
||||
import CategoryHeader from './header';
|
||||
|
||||
import type CategoryModel from '@typings/database/models/servers/category';
|
||||
|
||||
const enhanced = withObservables(['category'], ({category}: {category: CategoryModel}) => ({
|
||||
category,
|
||||
hasChannels: category.hasChannels,
|
||||
}));
|
||||
|
||||
export default enhanced(CategoryHeader);
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import Category from './index';
|
||||
|
||||
import type Database from '@nozbe/watermelondb/Database';
|
||||
|
||||
const channels: TempoChannel[] = [
|
||||
{id: '1', name: 'Just a channel'},
|
||||
{id: '2', name: 'Highlighted!!!', highlight: true},
|
||||
];
|
||||
|
||||
const categories: TempoCategory[] = [
|
||||
{id: '1', title: 'My first Category', channels},
|
||||
{id: '2', title: 'Another cat', channels},
|
||||
];
|
||||
|
||||
describe('Category List Component ', () => {
|
||||
let database: Database | undefined;
|
||||
beforeAll(async () => {
|
||||
const server = await TestHelper.setupServerDatabase();
|
||||
database = server.database;
|
||||
});
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const {toJSON} = renderWithEverything(
|
||||
<Category categories={categories}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
31
app/components/channel_list/categories/index.ts
Normal file
31
app/components/channel_list/categories/index.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Q} from '@nozbe/watermelondb';
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
|
||||
import Categories from './categories';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type CategoryModel from '@typings/database/models/servers/category';
|
||||
|
||||
const {SERVER: {CATEGORY}} = MM_TABLES;
|
||||
|
||||
type WithDatabaseProps = {currentTeamId: string } & WithDatabaseArgs
|
||||
|
||||
const enhanced = withObservables(
|
||||
['currentTeamId'],
|
||||
({currentTeamId, database}: WithDatabaseProps) => {
|
||||
const categories = database.get<CategoryModel>(CATEGORY).query(
|
||||
Q.where('team_id', currentTeamId),
|
||||
).observeWithColumns(['sort_order']);
|
||||
|
||||
return {
|
||||
categories,
|
||||
};
|
||||
});
|
||||
|
||||
export default withDatabase(enhanced(Categories));
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {FlatList, StyleSheet} from 'react-native';
|
||||
|
||||
import ThreadsButton from '../threads';
|
||||
|
||||
import CategoryBody from './body';
|
||||
import CategoryHeader from './header';
|
||||
|
||||
type Props = {
|
||||
categories: TempoCategory[];
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flex: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const renderCategory = (data: {item: TempoCategory}) => (
|
||||
<>
|
||||
<CategoryHeader heading={data.item.title}/>
|
||||
<CategoryBody channels={data.item.channels}/>
|
||||
</>
|
||||
);
|
||||
|
||||
const Categories = (props: Props) => {
|
||||
return (
|
||||
<FlatList
|
||||
data={props.categories}
|
||||
renderItem={renderCategory}
|
||||
ListHeaderComponent={ThreadsButton}
|
||||
style={styles.flex}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Categories;
|
||||
|
|
@ -10,7 +10,7 @@ import {TeamModel} from '@database/models/server';
|
|||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import ChannelsList from './index';
|
||||
import ChannelsList from './';
|
||||
|
||||
describe('components/channel_list', () => {
|
||||
let database: Database;
|
||||
|
|
@ -26,12 +26,44 @@ describe('components/channel_list', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should match snapshot', () => {
|
||||
it('should render', () => {
|
||||
const wrapper = renderWithEverything(
|
||||
<SafeAreaProvider>
|
||||
<ChannelsList
|
||||
isTablet={false}
|
||||
teamsCount={1}
|
||||
currentTeamId={TestHelper.basicTeam!.id}
|
||||
channelsCount={1}
|
||||
/>
|
||||
</SafeAreaProvider>,
|
||||
{database},
|
||||
);
|
||||
expect(wrapper.toJSON()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render team error', () => {
|
||||
const wrapper = renderWithEverything(
|
||||
<SafeAreaProvider>
|
||||
<ChannelsList
|
||||
isTablet={false}
|
||||
teamsCount={0}
|
||||
currentTeamId='TestHelper.basicTeam!.id'
|
||||
channelsCount={1}
|
||||
/>
|
||||
</SafeAreaProvider>,
|
||||
{database},
|
||||
);
|
||||
expect(wrapper.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render channels error', () => {
|
||||
const wrapper = renderWithEverything(
|
||||
<SafeAreaProvider>
|
||||
<ChannelsList
|
||||
isTablet={false}
|
||||
teamsCount={1}
|
||||
currentTeamId={TestHelper.basicTeam!.id}
|
||||
channelsCount={0}
|
||||
/>
|
||||
</SafeAreaProvider>,
|
||||
{database},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import React, {useEffect} from 'react';
|
||||
import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||
|
||||
import {TABLET_SIDEBAR_WIDTH, TEAM_SIDEBAR_WIDTH} from '@constants/view';
|
||||
|
|
@ -13,38 +13,28 @@ import ChannelListHeader from './header';
|
|||
import LoadChannelsError from './load_channels_error';
|
||||
import LoadTeamsError from './load_teams_error';
|
||||
import SearchField from './search';
|
||||
|
||||
// import Loading from '@components/loading';
|
||||
|
||||
const channels: TempoChannel[] = [
|
||||
{id: '1', name: 'Just a channel'},
|
||||
{id: '2', name: 'A Highlighted Channel!!!', highlight: true},
|
||||
{id: '3', name: 'And a longer channel name.'},
|
||||
];
|
||||
|
||||
const categories: TempoCategory[] = [
|
||||
{id: '1', title: 'My first Category', channels},
|
||||
{id: '2', title: 'Another Cat', channels},
|
||||
];
|
||||
import Threads from './threads';
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.sidebarBg,
|
||||
paddingHorizontal: 20,
|
||||
paddingLeft: 18,
|
||||
paddingRight: 20,
|
||||
paddingVertical: 10,
|
||||
},
|
||||
|
||||
}));
|
||||
|
||||
type ChannelListProps = {
|
||||
channelsCount: number;
|
||||
currentTeamId?: string;
|
||||
iconPad?: boolean;
|
||||
isTablet: boolean;
|
||||
teamsCount: number;
|
||||
}
|
||||
|
||||
const ChannelList = ({currentTeamId, iconPad, isTablet, teamsCount}: ChannelListProps) => {
|
||||
const ChannelList = ({channelsCount, currentTeamId, iconPad, isTablet, teamsCount}: ChannelListProps) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
const tabletWidth = useSharedValue(TABLET_SIDEBAR_WIDTH);
|
||||
|
|
@ -64,26 +54,28 @@ const ChannelList = ({currentTeamId, iconPad, isTablet, teamsCount}: ChannelList
|
|||
}
|
||||
}, [isTablet, teamsCount]);
|
||||
|
||||
const [showCats, setShowCats] = useState<boolean>(true);
|
||||
let content;
|
||||
|
||||
if (!currentTeamId) {
|
||||
content = (<LoadTeamsError/>);
|
||||
} else if (showCats) {
|
||||
} else if (channelsCount < 1) {
|
||||
content = (<LoadChannelsError teamId={currentTeamId}/>);
|
||||
} else {
|
||||
content = (
|
||||
<>
|
||||
<SearchField/>
|
||||
<Categories categories={categories}/>
|
||||
<Threads/>
|
||||
<Categories
|
||||
currentTeamId={currentTeamId}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
content = (<LoadChannelsError teamId={currentTeamId}/>);
|
||||
}
|
||||
|
||||
return (
|
||||
<Animated.View style={[styles.container, tabletStyle]}>
|
||||
<ChannelListHeader
|
||||
iconPad={iconPad}
|
||||
onHeaderPress={() => setShowCats(!showCats)}
|
||||
/>
|
||||
{content}
|
||||
</Animated.View>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {StyleSheet, TextInput, View} from 'react-native';
|
||||
import {TextInput, StyleSheet, View} from 'react-native';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {useTheme} from '@context/theme';
|
||||
|
|
|
|||
16
app/components/channel_list/tempo-types.d.ts
vendored
16
app/components/channel_list/tempo-types.d.ts
vendored
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// THIS IS TEMPORARY POC/MVP - WILL BE REMOVEd
|
||||
// @to-do - Wire up actual models
|
||||
type TempoChannel = {
|
||||
id: string;
|
||||
name: string;
|
||||
highlight?: boolean;
|
||||
};
|
||||
|
||||
type TempoCategory = {
|
||||
id: string;
|
||||
title: string;
|
||||
channels: TempoChannel[];
|
||||
}
|
||||
|
|
@ -3,26 +3,14 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
import {renderWithIntlAndTheme} from '@test/intl-test-helper';
|
||||
|
||||
import Threads from './index';
|
||||
|
||||
import type Database from '@nozbe/watermelondb/Database';
|
||||
test('Threads Component should match snapshot', () => {
|
||||
const {toJSON} = renderWithIntlAndTheme(
|
||||
<Threads/>,
|
||||
);
|
||||
|
||||
describe('Threads Component', () => {
|
||||
let database: Database | undefined;
|
||||
beforeAll(async () => {
|
||||
const server = await TestHelper.setupServerDatabase();
|
||||
database = server.database;
|
||||
});
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const {toJSON} = renderWithEverything(
|
||||
<Threads/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(toJSON()).toMatchSnapshot();
|
||||
});
|
||||
expect(toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,28 +1,17 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Q} from '@nozbe/watermelondb';
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import React from 'react';
|
||||
import {StyleSheet, Text, View} from 'react-native';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {switchToChannelById} from '@actions/remote/channel';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import TouchableWithFeedback from '@components/touchable_with_feedback';
|
||||
import {General} from '@constants';
|
||||
import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {Screens} from '@constants';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {goToScreen} from '@screens/navigation';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type SystemModel from '@typings/database/models/servers/system';
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
container: {
|
||||
display: 'flex',
|
||||
|
|
@ -41,19 +30,18 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
|||
|
||||
const textStyle = StyleSheet.create([typography('Body', 200, 'SemiBold')]);
|
||||
|
||||
const ThreadsButton = ({channelId}: {channelId?: string}) => {
|
||||
const ThreadsButton = () => {
|
||||
const theme = useTheme();
|
||||
const serverUrl = useServerUrl();
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
/*
|
||||
* @to-do:
|
||||
* - Check if there are threads, else return null (think of doing this before mounting this component)
|
||||
* - Change to button, navigate to threads view instead of the current team Town Square
|
||||
* - Check if there are threads, else return null
|
||||
* - Change to button, navigate to threads view
|
||||
* - Add right-side number badge
|
||||
*/
|
||||
return (
|
||||
<TouchableWithFeedback onPress={() => (channelId ? switchToChannelById(serverUrl, channelId) : true)} >
|
||||
<TouchableWithFeedback onPress={() => goToScreen(Screens.CHANNEL, 'Channel', {}, {topBar: {visible: false}})} >
|
||||
<View style={styles.container}>
|
||||
<CompassIcon
|
||||
name='message-text-outline'
|
||||
|
|
@ -65,19 +53,4 @@ const ThreadsButton = ({channelId}: {channelId?: string}) => {
|
|||
);
|
||||
};
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const currentTeamId = database.get<SystemModel>(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID);
|
||||
const channelId = currentTeamId.pipe(
|
||||
switchMap((model) => database.get<ChannelModel>(MM_TABLES.SERVER.CHANNEL).query(
|
||||
Q.where('team_id', model.value),
|
||||
Q.where('name', General.DEFAULT_CHANNEL),
|
||||
).observe().pipe(
|
||||
// eslint-disable-next-line max-nested-callbacks
|
||||
switchMap((channels) => (channels.length ? of$(channels[0].id) : of$(undefined))),
|
||||
)),
|
||||
);
|
||||
|
||||
return {channelId};
|
||||
});
|
||||
|
||||
export default withDatabase(enhanced(ThreadsButton));
|
||||
export default ThreadsButton;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ const Status = ({author, statusSize, statusStyle, theme}: Props) => {
|
|||
styles.statusWrapper,
|
||||
statusStyle,
|
||||
{borderRadius: statusSize / 2},
|
||||
]), []);
|
||||
]), [statusStyle]);
|
||||
|
||||
if (author?.status && !author.isBot) {
|
||||
return (
|
||||
<View
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {makeStyleSheetFromTheme} from '@utils/theme';
|
|||
import Servers from './servers';
|
||||
|
||||
type ChannelProps = {
|
||||
channelsCount: number;
|
||||
currentTeamId?: string;
|
||||
teamsCount: number;
|
||||
time?: number;
|
||||
|
|
@ -91,10 +92,11 @@ const ChannelListScreen = (props: ChannelProps) => {
|
|||
teamsCount={props.teamsCount}
|
||||
/>
|
||||
<ChannelList
|
||||
currentTeamId={props.currentTeamId}
|
||||
iconPad={canAddOtherServers && props.teamsCount <= 1}
|
||||
isTablet={isTablet}
|
||||
teamsCount={props.teamsCount}
|
||||
channelsCount={props.channelsCount}
|
||||
currentTeamId={props.currentTeamId}
|
||||
/>
|
||||
{isTablet && Boolean(props.currentTeamId) &&
|
||||
<Channel/>
|
||||
|
|
|
|||
|
|
@ -7,14 +7,15 @@ import {catchError, of as of$} from 'rxjs';
|
|||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import MyTeamModel from '@typings/database/models/servers/my_team';
|
||||
|
||||
import ChannelsList from './channel_list';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type MyChannelModel from '@typings/database/models/servers/my_channel';
|
||||
import type MyTeamModel from '@typings/database/models/servers/my_team';
|
||||
import type SystemModel from '@typings/database/models/servers/system';
|
||||
|
||||
const {SERVER: {MY_TEAM, SYSTEM}} = MM_TABLES;
|
||||
const {SERVER: {MY_CHANNEL, MY_TEAM, SYSTEM}} = MM_TABLES;
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({
|
||||
currentTeamId: database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe(
|
||||
|
|
@ -22,6 +23,7 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({
|
|||
catchError(() => of$(undefined)),
|
||||
),
|
||||
teamsCount: database.get<MyTeamModel>(MY_TEAM).query().observeCount(),
|
||||
channelsCount: database.get<MyChannelModel>(MY_CHANNEL).query().observeCount(),
|
||||
}));
|
||||
|
||||
export default withDatabase(enhanced(ChannelsList));
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
"camera_type.video.option": "Record Video",
|
||||
"center_panel.archived.closeChannel": "Close Channel",
|
||||
"channel": "{count, plural, one {# member} other {# members}}",
|
||||
"channel_header.directchannel.you": "{displayname} (you)",
|
||||
"channel_header.directchannel.you": "{displayName} (you)",
|
||||
"channel_loader.someone": "Someone",
|
||||
"channel_modal.optional": "(optional)",
|
||||
"combined_system_message.added_to_channel.many_expanded": "{users} and {lastUser} were **added to the channel** by {actor}.",
|
||||
|
|
@ -150,6 +150,8 @@
|
|||
"last_users_message.others": "{numOthers} others ",
|
||||
"last_users_message.removed_from_channel.type": "were **removed from the channel**.",
|
||||
"last_users_message.removed_from_team.type": "were **removed from the team**.",
|
||||
"load_categories_error.message": "There was a problem loading content for this server.",
|
||||
"load_categories_error.title": "Couldn't load categories for {serverName}",
|
||||
"load_channels_error.message": "There was a problem loading content for this team.",
|
||||
"load_channels_error.title": "Couldn't load {teamDisplayName}",
|
||||
"load_teams_error.message": "There was a problem loading content for this server.",
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@
|
|||
"build:ios-sim": "./scripts/build.sh ipa simulator",
|
||||
"build:ios-unsigned": "./scripts/build.sh ipa unsigned",
|
||||
"check": "npm run lint && npm run tsc",
|
||||
"check-test": "npm run lint && npm run tsc && npm run test",
|
||||
"clean": "./scripts/clean.sh",
|
||||
"e2e:android": "cd detox && npm run e2e:android-build && npm run e2e:android-test && cd ..",
|
||||
"e2e:ios": "cd detox && npm run e2e:ios-test && cd ..",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import assert from 'assert';
|
||||
|
||||
import {random} from 'lodash';
|
||||
import nock from 'nock';
|
||||
|
||||
import Config from '@assets/config.json';
|
||||
|
|
@ -22,8 +23,11 @@ class TestHelper {
|
|||
this.basicUser = null;
|
||||
this.basicTeam = null;
|
||||
this.basicTeamMember = null;
|
||||
this.basicCategory = null;
|
||||
this.basicCategoryChannel = null;
|
||||
this.basicChannel = null;
|
||||
this.basicChannelMember = null;
|
||||
this.basicMyChannel = null;
|
||||
this.basicPost = null;
|
||||
this.basicRoles = null;
|
||||
this.basicScheme = null;
|
||||
|
|
@ -52,6 +56,25 @@ class TestHelper {
|
|||
prepareRecordsOnly: false,
|
||||
});
|
||||
|
||||
// Add a category and associated channel entities
|
||||
await operator.handleCategories({
|
||||
categories: [this.basicCategory],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
await operator.handleCategoryChannels({
|
||||
categoryChannels: [this.basicCategoryChannel],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
await operator.handleChannel({
|
||||
channels: [this.basicChannel],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
await operator.handleMyChannel({
|
||||
prepareRecordsOnly: false,
|
||||
channels: [this.basicChannel],
|
||||
myChannels: [this.basicMyChannel],
|
||||
});
|
||||
|
||||
const systems = await prepareCommonSystemValues(operator, {
|
||||
config: {},
|
||||
license: {},
|
||||
|
|
@ -93,13 +116,53 @@ class TestHelper {
|
|||
return new Client(mockApiClient, mockApiClient.baseUrl);
|
||||
};
|
||||
|
||||
fakeCategory = (teamId) => {
|
||||
return {
|
||||
display_name: 'Test Category',
|
||||
type: 'custom',
|
||||
sort_order: 0,
|
||||
sorting: 'manual',
|
||||
muted: false,
|
||||
collapsed: false,
|
||||
team_id: teamId,
|
||||
};
|
||||
};
|
||||
|
||||
fakeCategoryWithId = (teamId) => {
|
||||
return {
|
||||
...this.fakeCategory(teamId),
|
||||
id: this.generateId(),
|
||||
};
|
||||
};
|
||||
|
||||
fakeCategoryChannel = (categoryId, channelId) => {
|
||||
return {
|
||||
category_id: categoryId,
|
||||
channel_id: channelId,
|
||||
sort_order: random(0, 10, false),
|
||||
};
|
||||
};
|
||||
|
||||
fakeCategoryChannelWithId = (teamId, categoryId, channelId) => {
|
||||
return {
|
||||
id: teamId + channelId,
|
||||
category_id: categoryId,
|
||||
channel_id: channelId,
|
||||
sort_order: random(0, 10, false),
|
||||
};
|
||||
};
|
||||
|
||||
fakeChannel = (teamId) => {
|
||||
const name = this.generateId();
|
||||
|
||||
return {
|
||||
name,
|
||||
team_id: teamId,
|
||||
display_name: `Unit Test ${name}`,
|
||||
|
||||
// @to-do: Make tests more detriministic;
|
||||
// https://jestjs.io/docs/snapshot-testing#2-tests-should-be-deterministic
|
||||
// display_name: `Unit Test ${name}`,
|
||||
display_name: 'Channel',
|
||||
type: 'O',
|
||||
delete_at: 0,
|
||||
total_msg_count: 0,
|
||||
|
|
@ -144,6 +207,21 @@ class TestHelper {
|
|||
};
|
||||
};
|
||||
|
||||
fakeMyChannel = (channelId) => {
|
||||
return {
|
||||
id: channelId,
|
||||
channel_id: channelId,
|
||||
last_post_at: 0,
|
||||
last_viewed_at: 0,
|
||||
manually_unread: false,
|
||||
mentions_count: 0,
|
||||
message_count: 0,
|
||||
is_unread: false,
|
||||
roles: '',
|
||||
viewed_at: 0,
|
||||
};
|
||||
};
|
||||
|
||||
fakeEmail = () => {
|
||||
return 'success' + this.generateId() + '@simulator.amazonses.com';
|
||||
};
|
||||
|
|
@ -318,8 +396,11 @@ class TestHelper {
|
|||
this.basicUser.roles = 'system_user system_admin';
|
||||
this.basicTeam = this.fakeTeamWithId();
|
||||
this.basicTeamMember = this.fakeTeamMember(this.basicUser.id, this.basicTeam.id);
|
||||
this.basicCategory = this.fakeCategoryWithId(this.basicTeam.id);
|
||||
this.basicChannel = this.fakeChannelWithId(this.basicTeam.id);
|
||||
this.basicCategoryChannel = this.fakeCategoryChannelWithId(this.basicTeam.id, this.basicCategory.id, this.basicChannel.id);
|
||||
this.basicChannelMember = this.fakeChannelMember(this.basicUser.id, this.basicChannel.id);
|
||||
this.basicMyChannel = this.fakeMyChannel(this.basicChannel.id);
|
||||
this.basicPost = {...this.fakePostWithId(this.basicChannel.id), create_at: 1507841118796};
|
||||
this.basicRoles = {
|
||||
system_admin: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue