diff --git a/app/components/autocomplete/at_mention_group/at_mention_group.tsx b/app/components/autocomplete/at_mention_group/at_mention_group.tsx
index 1bd9880c89..7e25af9552 100644
--- a/app/components/autocomplete/at_mention_group/at_mention_group.tsx
+++ b/app/components/autocomplete/at_mention_group/at_mention_group.tsx
@@ -20,7 +20,6 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
height: 40,
paddingVertical: 8,
paddingTop: 4,
- paddingHorizontal: 16,
flexDirection: 'row',
alignItems: 'center',
},
diff --git a/app/components/autocomplete/at_mention_item/index.tsx b/app/components/autocomplete/at_mention_item/index.tsx
index f4b9684a03..1b73b6a386 100644
--- a/app/components/autocomplete/at_mention_item/index.tsx
+++ b/app/components/autocomplete/at_mention_item/index.tsx
@@ -2,12 +2,8 @@
// See LICENSE.txt for license information.
import React, {useCallback} from 'react';
-import {useSafeAreaInsets} from 'react-native-safe-area-context';
-import TouchableWithFeedback from '@components/touchable_with_feedback';
import UserItem from '@components/user_item';
-import {useTheme} from '@context/theme';
-import {changeOpacity} from '@utils/theme';
import type UserModel from '@typings/database/models/servers/user';
@@ -22,26 +18,16 @@ const AtMentionItem = ({
onPress,
testID,
}: AtMentionItemProps) => {
- const insets = useSafeAreaInsets();
- const theme = useTheme();
-
- const completeMention = useCallback(() => {
- onPress?.(user.username);
- }, [user.username]);
+ const completeMention = useCallback((u: UserModel | UserProfile) => {
+ onPress?.(u.username);
+ }, []);
return (
-
-
-
+
);
};
diff --git a/app/components/autocomplete/autocomplete.tsx b/app/components/autocomplete/autocomplete.tsx
index 2f7998d71e..64b7f48e6a 100644
--- a/app/components/autocomplete/autocomplete.tsx
+++ b/app/components/autocomplete/autocomplete.tsx
@@ -43,6 +43,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
listStyle: {
backgroundColor: theme.centerChannelBg,
borderRadius: 4,
+ paddingHorizontal: 16,
},
};
});
diff --git a/app/components/autocomplete/autocomplete_section_header.tsx b/app/components/autocomplete/autocomplete_section_header.tsx
index 581e254985..00be90073b 100644
--- a/app/components/autocomplete/autocomplete_section_header.tsx
+++ b/app/components/autocomplete/autocomplete_section_header.tsx
@@ -8,16 +8,15 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context';
import FormattedText from '@components/formatted_text';
import {useTheme} from '@context/theme';
import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme';
+import {typography} from '@utils/typography';
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
return {
section: {
flexDirection: 'row',
- paddingHorizontal: 16,
},
sectionText: {
- fontSize: 12,
- fontWeight: '600',
+ ...typography('Body', 75, 'SemiBold'),
textTransform: 'uppercase',
color: changeOpacity(theme.centerChannelColor, 0.56),
paddingTop: 16,
diff --git a/app/components/autocomplete/channel_mention/channel_mention.tsx b/app/components/autocomplete/channel_mention/channel_mention.tsx
index 84b01652f5..e742d0749d 100644
--- a/app/components/autocomplete/channel_mention/channel_mention.tsx
+++ b/app/components/autocomplete/channel_mention/channel_mention.tsx
@@ -7,13 +7,16 @@ import {Platform, SectionList, SectionListData, SectionListRenderItemInfo, Style
import {searchChannels} from '@actions/remote/channel';
import AutocompleteSectionHeader from '@components/autocomplete/autocomplete_section_header';
-import ChannelMentionItem from '@components/autocomplete/channel_mention_item';
+import ChannelItem from '@components/channel_item';
import {General} from '@constants';
import {CHANNEL_MENTION_REGEX, CHANNEL_MENTION_SEARCH_REGEX} from '@constants/autocomplete';
import {useServerUrl} from '@context/server';
+import DatabaseManager from '@database/manager';
import useDidUpdate from '@hooks/did_update';
import {t} from '@i18n';
+import {getUserById} from '@queries/servers/user';
import {hasTrailingSpaces} from '@utils/helpers';
+import {getUserIdFromChannelName} from '@utils/user';
import type ChannelModel from '@typings/database/models/servers/channel';
import type MyChannelModel from '@typings/database/models/servers/my_channel';
@@ -138,6 +141,7 @@ type Props = {
matchTerm: string;
localChannels: ChannelModel[];
teamId: string;
+ currentUserId: string;
}
const emptySections: Array> = [];
@@ -155,6 +159,7 @@ const ChannelMention = ({
matchTerm,
localChannels,
teamId,
+ currentUserId,
}: Props) => {
const serverUrl = useServerUrl();
@@ -193,7 +198,22 @@ const ChannelMention = ({
setLoading(false);
};
- const completeMention = useCallback((mention: string) => {
+ const completeMention = useCallback(async (c: ChannelModel | Channel) => {
+ let mention = c.name;
+ const teammateId = getUserIdFromChannelName(currentUserId, c.name);
+
+ if (c.type === General.DM_CHANNEL && teammateId) {
+ try {
+ const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
+ const user = await getUserById(database, teammateId);
+ if (user) {
+ mention = `@${user.username}`;
+ }
+ } catch (err) {
+ // Do nothing
+ }
+ }
+
const mentionPart = value.substring(0, localCursorPosition);
let completedDraft: string;
@@ -231,14 +251,16 @@ const ChannelMention = ({
setSections(emptySections);
setRemoteChannels(emptyChannels);
latestSearchAt.current = Date.now();
- }, [value, localCursorPosition, isSearch]);
+ }, [value, localCursorPosition, isSearch, currentUserId]);
const renderItem = useCallback(({item}: SectionListRenderItemInfo) => {
return (
-
);
}, [completeMention]);
diff --git a/app/components/autocomplete/channel_mention/index.ts b/app/components/autocomplete/channel_mention/index.ts
index 17eb57b252..316d42c06a 100644
--- a/app/components/autocomplete/channel_mention/index.ts
+++ b/app/components/autocomplete/channel_mention/index.ts
@@ -8,7 +8,7 @@ import {switchMap} from 'rxjs/operators';
import {CHANNEL_MENTION_REGEX, CHANNEL_MENTION_SEARCH_REGEX} from '@constants/autocomplete';
import {observeChannel, queryAllMyChannel, queryChannelsForAutocomplete} from '@queries/servers/channel';
-import {observeCurrentTeamId} from '@queries/servers/system';
+import {observeCurrentTeamId, observeCurrentUserId} from '@queries/servers/system';
import ChannelMention from './channel_mention';
@@ -58,6 +58,7 @@ const emptyChannelList: ChannelModel[] = [];
const withMembers = withObservables([], ({database}: WithDatabaseArgs) => {
return {
myMembers: queryAllMyChannel(database).observe(),
+ currentUserId: observeCurrentUserId(database),
};
});
diff --git a/app/components/autocomplete/channel_mention_item/channel_mention_item.tsx b/app/components/autocomplete/channel_mention_item/channel_mention_item.tsx
deleted file mode 100644
index 9ded73355c..0000000000
--- a/app/components/autocomplete/channel_mention_item/channel_mention_item.tsx
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-import React, {useMemo} from 'react';
-import {Text, View} from 'react-native';
-import {useSafeAreaInsets} from 'react-native-safe-area-context';
-
-import ChannelIcon from '@components/channel_icon';
-import {BotTag, GuestTag} from '@components/tag';
-import TouchableWithFeedback from '@components/touchable_with_feedback';
-import {useTheme} from '@context/theme';
-import {isDMorGM} from '@utils/channel';
-import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme';
-
-import type ChannelModel from '@typings/database/models/servers/channel';
-
-const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => {
- return {
- icon: {
- marginRight: 11,
- opacity: 0.56,
- },
- row: {
- paddingHorizontal: 16,
- height: 40,
- flexDirection: 'row',
- alignItems: 'center',
- },
- rowDisplayName: {
- flex: 1,
- fontSize: 15,
- color: theme.centerChannelColor,
- },
- rowName: {
- fontSize: 15,
- color: theme.centerChannelColor,
- opacity: 0.56,
- },
- };
-});
-
-type Props = {
- channel: Channel | ChannelModel;
- displayName?: string;
- isBot: boolean;
- isGuest: boolean;
- onPress: (name?: string) => void;
- testID?: string;
-};
-
-const ChannelMentionItem = ({
- channel,
- displayName,
- isBot,
- isGuest,
- onPress,
- testID,
-}: Props) => {
- const insets = useSafeAreaInsets();
- const theme = useTheme();
-
- const completeMention = () => {
- if (isDMorGM(channel)) {
- onPress('@' + displayName?.replace(/ /g, ''));
- } else {
- onPress(channel.name);
- }
- };
-
- const style = getStyleFromTheme(theme);
- const margins = useMemo(() => {
- return {marginLeft: insets.left, marginRight: insets.right};
- }, [insets]);
- const rowStyle = useMemo(() => {
- return [style.row, margins];
- }, [margins, style]);
-
- let component;
-
- const isArchived = ('delete_at' in channel ? channel.delete_at : channel.deleteAt) > 0;
- const channelMentionItemTestId = `${testID}.${channel.name}`;
-
- if (isDMorGM(channel)) {
- if (!displayName) {
- return null;
- }
-
- component = (
-
-
- {'@' + displayName}
-
-
-
-
- );
- } else {
- component = (
-
-
-
-
- {displayName}
-
- {` ~${channel.name}`}
-
-
-
-
- );
- }
-
- return component;
-};
-
-export default ChannelMentionItem;
diff --git a/app/components/autocomplete/channel_mention_item/index.ts b/app/components/autocomplete/channel_mention_item/index.ts
deleted file mode 100644
index 662cbb6928..0000000000
--- a/app/components/autocomplete/channel_mention_item/index.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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 {of as of$} from 'rxjs';
-import {switchMap} from 'rxjs/operators';
-
-import {General} from '@constants';
-import {observeUser} from '@queries/servers/user';
-
-import ChannelMentionItem from './channel_mention_item';
-
-import type {WithDatabaseArgs} from '@typings/database/database';
-import type ChannelModel from '@typings/database/models/servers/channel';
-import type UserModel from '@typings/database/models/servers/user';
-
-type OwnProps = {
- channel: Channel | ChannelModel;
-}
-
-const enhanced = withObservables([], ({database, channel}: WithDatabaseArgs & OwnProps) => {
- let user = of$(undefined);
- const teammateId = 'teammate_id' in channel ? channel.teammate_id : '';
- const channelDisplayName = 'display_name' in channel ? channel.display_name : channel.displayName;
- if (channel.type === General.DM_CHANNEL && teammateId) {
- user = observeUser(database, teammateId!);
- }
-
- const isBot = user.pipe(switchMap((u) => of$(u ? u.isBot : false)));
- const isGuest = user.pipe(switchMap((u) => of$(u ? u.isGuest : false)));
- const displayName = user.pipe(switchMap((u) => of$(u ? u.username : channelDisplayName)));
-
- return {
- isBot,
- isGuest,
- displayName,
- };
-});
-
-export default withDatabase(enhanced(ChannelMentionItem));
diff --git a/app/components/autocomplete/emoji_suggestion/emoji_suggestion.tsx b/app/components/autocomplete/emoji_suggestion/emoji_suggestion.tsx
index 1b38f442d9..d07c79b063 100644
--- a/app/components/autocomplete/emoji_suggestion/emoji_suggestion.tsx
+++ b/app/components/autocomplete/emoji_suggestion/emoji_suggestion.tsx
@@ -54,7 +54,6 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
alignItems: 'center',
overflow: 'hidden',
paddingBottom: 8,
- paddingHorizontal: 16,
height: 40,
},
};
diff --git a/app/components/autocomplete/slash_suggestion/app_slash_suggestion/app_slash_suggestion.tsx b/app/components/autocomplete/slash_suggestion/app_slash_suggestion/app_slash_suggestion.tsx
index 3abae6636d..680b961217 100644
--- a/app/components/autocomplete/slash_suggestion/app_slash_suggestion/app_slash_suggestion.tsx
+++ b/app/components/autocomplete/slash_suggestion/app_slash_suggestion/app_slash_suggestion.tsx
@@ -7,7 +7,7 @@ import {useIntl} from 'react-intl';
import {FlatList, Platform, StyleProp, ViewStyle} from 'react-native';
import AtMentionItem from '@components/autocomplete/at_mention_item';
-import ChannelMentionItem from '@components/autocomplete/channel_mention_item';
+import ChannelItem from '@components/channel_item';
import {COMMAND_SUGGESTION_CHANNEL, COMMAND_SUGGESTION_USER} from '@constants/apps';
import {useServerUrl} from '@context/server';
import analytics from '@managers/analytics';
@@ -98,7 +98,7 @@ const AppSlashSuggestion = ({
}
}, [serverUrl, updateValue]);
- const completeIgnoringSuggestion = useCallback((base: string): (toIgnore: string) => void => {
+ const completeIgnoringSuggestion = useCallback((base: string): () => void => {
return () => {
completeSuggestion(base);
};
@@ -127,10 +127,12 @@ const AppSlashSuggestion = ({
}
return (
-
);
}
diff --git a/app/components/autocomplete/slash_suggestion/slash_suggestion_item.tsx b/app/components/autocomplete/slash_suggestion/slash_suggestion_item.tsx
index 6dbd4ec54f..c1b01c136f 100644
--- a/app/components/autocomplete/slash_suggestion/slash_suggestion_item.tsx
+++ b/app/components/autocomplete/slash_suggestion/slash_suggestion_item.tsx
@@ -37,7 +37,6 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => {
flexDirection: 'row',
alignItems: 'center',
paddingBottom: 8,
- paddingHorizontal: 16,
overflow: 'hidden',
},
slashIcon: {
diff --git a/app/components/autocomplete/special_mention_item.tsx b/app/components/autocomplete/special_mention_item.tsx
index 479dee291b..1a9f4279b3 100644
--- a/app/components/autocomplete/special_mention_item.tsx
+++ b/app/components/autocomplete/special_mention_item.tsx
@@ -18,12 +18,11 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
row: {
height: 40,
paddingVertical: 8,
- paddingHorizontal: 9,
flexDirection: 'row',
alignItems: 'center',
},
rowPicture: {
- marginHorizontal: 8,
+ marginRight: 8,
width: 20,
alignItems: 'center',
justifyContent: 'center',
diff --git a/app/components/autocomplete_selector/index.tsx b/app/components/autocomplete_selector/index.tsx
index fa786087e0..361ddcc7ac 100644
--- a/app/components/autocomplete_selector/index.tsx
+++ b/app/components/autocomplete_selector/index.tsx
@@ -143,7 +143,7 @@ function AutoCompleteSelector({
const goToSelectorScreen = useCallback(preventDoubleTap(() => {
const screen = Screens.INTEGRATION_SELECTOR;
- goToScreen(screen, title, {dataSource, handleSelect, options, getDynamicOptions, selected, isMultiselect, teammateNameDisplay});
+ goToScreen(screen, title, {dataSource, handleSelect, options, getDynamicOptions, selected, isMultiselect});
}), [dataSource, options, getDynamicOptions]);
const handleSelect = useCallback((newSelection?: Selection) => {
diff --git a/app/components/channel_icon/dm_avatar/dm_avatar.tsx b/app/components/channel_icon/dm_avatar/dm_avatar.tsx
index 992013326d..04db4f3c00 100644
--- a/app/components/channel_icon/dm_avatar/dm_avatar.tsx
+++ b/app/components/channel_icon/dm_avatar/dm_avatar.tsx
@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import React from 'react';
-import {View} from 'react-native';
import CompassIcon from '@components/compass_icon';
import ProfilePicture from '@components/profile_picture';
@@ -10,54 +9,55 @@ import {useTheme} from '@context/theme';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import type UserModel from '@typings/database/models/servers/user';
+import type {StyleProp, ViewStyle} from 'react-native';
type Props = {
author?: UserModel;
- isInfo?: boolean;
+ isOnCenterBg?: boolean;
+ style: StyleProp;
+ size: number;
}
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
- container: {marginLeft: 4},
status: {
backgroundColor: theme.sidebarBg,
borderWidth: 0,
},
- statusInfo: {
+ statusOnCenterBg: {
backgroundColor: theme.centerChannelBg,
},
icon: {
color: changeOpacity(theme.sidebarText, 0.4),
left: 1,
},
- iconInfo: {
+ iconOnCenterBg: {
color: changeOpacity(theme.centerChannelColor, 0.72),
},
}));
-const DmAvatar = ({author, isInfo}: Props) => {
+const DmAvatar = ({author, isOnCenterBg, style, size}: Props) => {
const theme = useTheme();
- const style = getStyleSheet(theme);
+ const styles = getStyleSheet(theme);
if (author?.deleteAt) {
return (
);
}
return (
-
-
-
+
);
};
diff --git a/app/components/channel_icon/index.tsx b/app/components/channel_icon/index.tsx
index 96386c5f1d..2c2170bed2 100644
--- a/app/components/channel_icon/index.tsx
+++ b/app/components/channel_icon/index.tsx
@@ -16,7 +16,7 @@ type ChannelIconProps = {
hasDraft?: boolean;
isActive?: boolean;
isArchived?: boolean;
- isInfo?: boolean;
+ isOnCenterBg?: boolean;
isUnread?: boolean;
isMuted?: boolean;
membersCount?: number;
@@ -43,10 +43,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
iconUnread: {
color: theme.sidebarUnreadText,
},
- iconInfo: {
+ iconOnCenterBg: {
color: changeOpacity(theme.centerChannelColor, 0.72),
},
- iconInfoUnread: {
+ iconUnreadOnCenterBg: {
color: theme.centerChannelColor,
},
groupBox: {
@@ -61,7 +61,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
groupBoxUnread: {
backgroundColor: changeOpacity(theme.sidebarUnreadText, 0.3),
},
- groupBoxInfo: {
+ groupBoxOnCenterBg: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.3),
},
group: {
@@ -74,10 +74,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
groupUnread: {
color: theme.sidebarUnreadText,
},
- groupInfo: {
+ groupOnCenterBg: {
color: changeOpacity(theme.centerChannelColor, 0.72),
},
- groupInfoUnread: {
+ groupUnreadOnCenterBg: {
color: theme.centerChannelColor,
},
muted: {
@@ -88,7 +88,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
const ChannelIcon = ({
hasDraft = false, isActive = false, isArchived = false,
- isInfo = false, isUnread = false, isMuted = false,
+ isOnCenterBg = false, isUnread = false, isMuted = false,
membersCount = 0, name,
shared, size = 12, style, testID, type,
}: ChannelIconProps) => {
@@ -115,22 +115,38 @@ const ChannelIcon = ({
activeGroup = styles.groupActive;
}
- if (isInfo) {
- activeIcon = isUnread && !isMuted ? styles.iconInfoUnread : styles.iconInfo;
- activeGroupBox = styles.groupBoxInfo;
- activeGroup = isUnread ? styles.groupInfoUnread : styles.groupInfo;
+ if (isOnCenterBg) {
+ activeIcon = isUnread && !isMuted ? styles.iconUnreadOnCenterBg : styles.iconOnCenterBg;
+ activeGroupBox = styles.groupBoxOnCenterBg;
+ activeGroup = isUnread ? styles.groupUnreadOnCenterBg : styles.groupOnCenterBg;
}
if (isMuted) {
mutedStyle = styles.muted;
}
- let icon;
+ const commonStyles = [
+ style,
+ mutedStyle,
+ ];
+
+ const commonIconStyles = [
+ styles.icon,
+ unreadIcon,
+ activeIcon,
+ commonStyles,
+ {fontSize: size},
+ ];
+
+ let icon = null;
if (isArchived) {
icon = (
);
@@ -138,7 +154,10 @@ const ChannelIcon = ({
icon = (
);
@@ -148,7 +167,10 @@ const ChannelIcon = ({
icon = (
);
@@ -156,7 +178,10 @@ const ChannelIcon = ({
icon = (
);
@@ -164,7 +189,10 @@ const ChannelIcon = ({
icon = (
);
@@ -172,7 +200,7 @@ const ChannelIcon = ({
const fontSize = size - 12;
icon = (
);
}
- return (
-
- {icon}
-
- );
+ return icon;
};
export default React.memo(ChannelIcon);
diff --git a/app/components/channel_item/__snapshots__/channel_item.test.tsx.snap b/app/components/channel_item/__snapshots__/channel_item.test.tsx.snap
index ea25aef92e..4db607bcde 100644
--- a/app/components/channel_item/__snapshots__/channel_item.test.tsx.snap
+++ b/app/components/channel_item/__snapshots__/channel_item.test.tsx.snap
@@ -41,11 +41,10 @@ exports[`components/channel_list/categories/body/channel_item should match snaps
{
"alignItems": "center",
"flexDirection": "row",
- "minHeight": 40,
- "paddingHorizontal": 20,
},
false,
- undefined,
+ false,
+ false,
{
"minHeight": 40,
},
@@ -53,79 +52,71 @@ exports[`components/channel_list/categories/body/channel_item should match snaps
}
testID="channel_item.hello"
>
+
+
+ Hello!
+
-
-
-
-
-
- Hello!
-
-
-
+ />
`;
@@ -171,11 +162,10 @@ exports[`components/channel_list/categories/body/channel_item should match snaps
{
"alignItems": "center",
"flexDirection": "row",
- "minHeight": 40,
- "paddingHorizontal": 20,
},
false,
- undefined,
+ false,
+ false,
{
"minHeight": 40,
},
@@ -183,103 +173,93 @@ exports[`components/channel_list/categories/body/channel_item should match snaps
}
testID="channel_item.hello"
>
+
+
+ Hello!
+
-
-
-
-
-
- Hello!
-
-
-
+ />
+
+
+ Hello!
+
-
-
-
-
-
- Hello!
-
-
-
+ />
`;
diff --git a/app/components/channel_item/channel_body.tsx b/app/components/channel_item/channel_body.tsx
new file mode 100644
index 0000000000..1146f9b60f
--- /dev/null
+++ b/app/components/channel_item/channel_body.tsx
@@ -0,0 +1,125 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+import React from 'react';
+import {StyleProp, Text, TextStyle, View} from 'react-native';
+
+import {useTheme} from '@context/theme';
+import {useIsTablet} from '@hooks/device';
+import {nonBreakingString} from '@utils/strings';
+import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
+import {typography} from '@utils/typography';
+
+import CustomStatus from './custom_status';
+
+type Props = {
+ displayName: string;
+ teamDisplayName: string;
+ teammateId?: string;
+ isMuted: boolean;
+ textStyles: StyleProp;
+ testId: string;
+ channelName: string;
+}
+
+const getStyleSheet = makeStyleSheetFromTheme((theme) => {
+ return {
+ teamName: {
+ color: changeOpacity(theme.centerChannelColor, 0.64),
+ ...typography('Body', 75),
+ },
+ teamNameMuted: {
+ color: changeOpacity(theme.centerChannelColor, 0.32),
+ },
+ flex: {
+ flex: 0,
+ flexShrink: 1,
+ },
+ channelName: {
+ ...typography('Body', 200),
+ color: changeOpacity(theme.centerChannelColor, 0.64),
+ },
+ customStatus: {
+ marginLeft: 4,
+ },
+ };
+});
+export const ChannelBody = ({
+ displayName,
+ channelName,
+ teamDisplayName,
+ teammateId,
+ isMuted,
+ textStyles,
+ testId,
+}: Props) => {
+ const theme = useTheme();
+ const styles = getStyleSheet(theme);
+ const isTablet = useIsTablet();
+ const nonBreakingDisplayName = nonBreakingString(displayName);
+ const channelText = (
+
+ {nonBreakingDisplayName}
+ {Boolean(channelName) && (
+
+ {nonBreakingString(` ~${channelName}`)}
+
+ )}
+
+ );
+
+ if (teamDisplayName) {
+ const teamText = (
+
+ {nonBreakingString(`${isTablet ? ' ' : ''}${teamDisplayName}`)}
+
+ );
+
+ if (isTablet) {
+ return (
+
+ {nonBreakingDisplayName}
+ {teamText}
+
+ );
+ }
+
+ return (
+
+ {channelText}
+ {teamText}
+
+ );
+ }
+
+ if (teammateId) {
+ const customStatus = (
+
+ );
+ return (
+ <>
+ {channelText}
+ {customStatus}
+ >
+ );
+ }
+
+ return channelText;
+};
diff --git a/app/components/channel_item/channel_item.test.tsx b/app/components/channel_item/channel_item.test.tsx
index 3f4ff215f7..bd42f43bf5 100644
--- a/app/components/channel_item/channel_item.test.tsx
+++ b/app/components/channel_item/channel_item.test.tsx
@@ -41,7 +41,6 @@ describe('components/channel_list/categories/body/channel_item', () => {
onPress={() => undefined}
isUnread={myChannel.isUnread}
mentionsCount={myChannel.mentionsCount}
- hasMember={Boolean(myChannel)}
hasCall={false}
/>,
);
@@ -62,7 +61,6 @@ describe('components/channel_list/categories/body/channel_item', () => {
onPress={() => undefined}
isUnread={myChannel.isUnread}
mentionsCount={myChannel.mentionsCount}
- hasMember={Boolean(myChannel)}
hasCall={false}
/>,
);
@@ -83,7 +81,6 @@ describe('components/channel_list/categories/body/channel_item', () => {
onPress={() => undefined}
isUnread={myChannel.isUnread}
mentionsCount={myChannel.mentionsCount}
- hasMember={Boolean(myChannel)}
hasCall={true}
/>,
);
diff --git a/app/components/channel_item/channel_item.tsx b/app/components/channel_item/channel_item.tsx
index 89a68985a1..e8f3f19b9f 100644
--- a/app/components/channel_item/channel_item.tsx
+++ b/app/components/channel_item/channel_item.tsx
@@ -3,85 +3,79 @@
import React, {useCallback, useMemo} from 'react';
import {useIntl} from 'react-intl';
-import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
+import {StyleSheet, TouchableOpacity, View} from 'react-native';
import Badge from '@components/badge';
import ChannelIcon from '@components/channel_icon';
import CompassIcon from '@components/compass_icon';
import {General} from '@constants';
+import {HOME_PADDING} from '@constants/view';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
+import {isDMorGM} from '@utils/channel';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
import {getUserIdFromChannelName} from '@utils/user';
-import CustomStatus from './custom_status';
+import {ChannelBody} from './channel_body';
import type ChannelModel from '@typings/database/models/servers/channel';
type Props = {
- channel: ChannelModel;
+ channel: ChannelModel | Channel;
currentUserId: string;
hasDraft: boolean;
isActive: boolean;
- isInfo?: boolean;
isMuted: boolean;
membersCount: number;
isUnread: boolean;
mentionsCount: number;
- onPress: (channelId: string) => void;
- hasMember: boolean;
+ onPress: (channel: ChannelModel | Channel) => void;
teamDisplayName?: string;
testID?: string;
hasCall: boolean;
+ isOnCenterBg?: boolean;
+ showChannelName?: boolean;
+ isOnHome?: boolean;
}
+export const ROW_HEIGHT = 40;
+export const ROW_HEIGHT_WITH_TEAM = 58;
+
export const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
container: {
flexDirection: 'row',
- paddingHorizontal: 20,
- minHeight: 40,
alignItems: 'center',
},
- infoItem: {
- paddingHorizontal: 0,
- },
- wrapper: {
- flex: 1,
- flexDirection: 'row',
- },
icon: {
- fontSize: 24,
- lineHeight: 28,
- color: changeOpacity(theme.sidebarText, 0.72),
+ marginRight: 12,
},
text: {
- marginTop: -1,
color: changeOpacity(theme.sidebarText, 0.72),
- paddingLeft: 12,
- paddingRight: 20,
},
highlight: {
color: theme.sidebarUnreadText,
},
- textInfo: {
+ textOnCenterBg: {
color: theme.centerChannelColor,
- paddingRight: 20,
},
muted: {
color: changeOpacity(theme.sidebarText, 0.32),
},
- mutedInfo: {
+ mutedOnCenterBg: {
color: changeOpacity(theme.centerChannelColor, 0.32),
},
badge: {
borderColor: theme.sidebarBg,
- position: 'relative',
- left: 0,
- top: -2,
+ marginLeft: 4,
+
+ //Overwrite default badge styles
+ position: undefined,
+ top: undefined,
+ left: undefined,
alignSelf: undefined,
},
- infoBadge: {
+ badgeOnCenterBg: {
color: theme.buttonColor,
backgroundColor: theme.buttonBg,
borderColor: theme.centerChannelBg,
@@ -93,31 +87,15 @@ export const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
backgroundColor: changeOpacity(theme.sidebarTextActiveColor, 0.1),
borderLeftColor: theme.sidebarTextActiveBorder,
borderLeftWidth: 5,
- marginLeft: 0,
- paddingLeft: 14,
},
textActive: {
color: theme.sidebarText,
},
- teamName: {
- color: changeOpacity(theme.centerChannelColor, 0.64),
- paddingLeft: 12,
- marginTop: 4,
- ...typography('Body', 75),
- },
- teamNameMuted: {
- color: changeOpacity(theme.centerChannelColor, 0.32),
- },
- teamNameTablet: {
- marginLeft: -12,
- paddingLeft: 0,
- marginTop: 0,
- paddingBottom: 0,
- top: 5,
- },
hasCall: {
textAlign: 'right',
- paddingRight: 0,
+ },
+ filler: {
+ flex: 1,
},
}));
@@ -126,137 +104,118 @@ export const textStyle = StyleSheet.create({
regular: typography('Body', 200, 'Regular'),
});
-const ChannelListItem = ({
- channel, currentUserId, hasDraft,
- isActive, isInfo, isMuted, membersCount, hasMember,
- isUnread, mentionsCount, onPress, teamDisplayName, testID, hasCall}: Props) => {
+const ChannelItem = ({
+ channel,
+ currentUserId,
+ hasDraft,
+ isActive,
+ isMuted,
+ membersCount,
+ isUnread,
+ mentionsCount,
+ onPress,
+ teamDisplayName = '',
+ testID,
+ hasCall,
+ isOnCenterBg = false,
+ showChannelName = false,
+ isOnHome = false,
+}: Props) => {
const {formatMessage} = useIntl();
const theme = useTheme();
const isTablet = useIsTablet();
const styles = getStyleSheet(theme);
+ const channelName = (showChannelName && !isDMorGM(channel)) ? channel.name : '';
+
// Make it bolded if it has unreads or mentions
const isBolded = isUnread || mentionsCount > 0;
+ const showActive = isActive && isTablet;
+
+ const teammateId = (channel.type === General.DM_CHANNEL) ? getUserIdFromChannelName(currentUserId, channel.name) : undefined;
+ const isOwnDirectMessage = (channel.type === General.DM_CHANNEL) && currentUserId === teammateId;
+
+ let displayName = 'displayName' in channel ? channel.displayName : channel.display_name;
+ if (isOwnDirectMessage) {
+ displayName = formatMessage({id: 'channel_header.directchannel.you', defaultMessage: '{displayName} (you)'}, {displayName});
+ }
+
+ const deleteAt = 'deleteAt' in channel ? channel.deleteAt : channel.delete_at;
+ const channelItemTestId = `${testID}.${channel.name}`;
const height = useMemo(() => {
- let h = 40;
- if (isInfo) {
- h = (teamDisplayName && !isTablet) ? 58 : 44;
- }
- return h;
- }, [teamDisplayName, isInfo, isTablet]);
+ return (teamDisplayName && !isTablet) ? ROW_HEIGHT_WITH_TEAM : ROW_HEIGHT;
+ }, [teamDisplayName, isTablet]);
const handleOnPress = useCallback(() => {
- onPress(channel.id);
+ onPress(channel);
}, [channel.id]);
const textStyles = useMemo(() => [
isBolded && !isMuted ? textStyle.bold : textStyle.regular,
styles.text,
isBolded && styles.highlight,
- isActive && isTablet && !isInfo ? styles.textActive : null,
- isInfo ? styles.textInfo : null,
+ showActive ? styles.textActive : null,
+ isOnCenterBg ? styles.textOnCenterBg : null,
isMuted && styles.muted,
- isMuted && isInfo && styles.mutedInfo,
- ], [isBolded, styles, isMuted, isActive, isInfo, isTablet]);
+ isMuted && isOnCenterBg && styles.mutedOnCenterBg,
+ ], [isBolded, styles, isMuted, showActive, isOnCenterBg]);
const containerStyle = useMemo(() => [
styles.container,
- isActive && isTablet && !isInfo && styles.activeItem,
- isInfo && styles.infoItem,
+ isOnHome && HOME_PADDING,
+ showActive && styles.activeItem,
+ showActive && isOnHome && {
+ paddingLeft: HOME_PADDING.paddingLeft - styles.activeItem.borderLeftWidth,
+ },
{minHeight: height},
- ],
- [height, isActive, isTablet, isInfo, styles]);
-
- if (!hasMember) {
- return null;
- }
-
- const teammateId = (channel.type === General.DM_CHANNEL) ? getUserIdFromChannelName(currentUserId, channel.name) : undefined;
- const isOwnDirectMessage = (channel.type === General.DM_CHANNEL) && currentUserId === teammateId;
-
- let displayName = channel.displayName;
- if (isOwnDirectMessage) {
- displayName = formatMessage({id: 'channel_header.directchannel.you', defaultMessage: '{displayName} (you)'}, {displayName});
- }
-
- const channelItemTestId = `${testID}.${channel.name}`;
+ ], [height, showActive, styles, isOnHome]);
return (
- <>
-
-
- 0}
- membersCount={membersCount}
- name={channel.name}
- shared={channel.shared}
- size={24}
- type={channel.type}
- isMuted={isMuted}
- />
-
-
- {displayName}
-
- {isInfo && Boolean(teamDisplayName) && !isTablet &&
-
- {teamDisplayName}
-
- }
-
- {Boolean(teammateId) &&
-
- }
- {isInfo && Boolean(teamDisplayName) && isTablet &&
-
- {teamDisplayName}
-
- }
-
- 0}
- value={mentionsCount}
- style={[styles.badge, isMuted && styles.mutedBadge, isInfo && styles.infoBadge]}
- />
- {hasCall &&
-
- }
-
- >
+
+ 0}
+ membersCount={membersCount}
+ name={channel.name}
+ shared={channel.shared}
+ size={24}
+ type={channel.type}
+ isMuted={isMuted}
+ style={styles.icon}
+ />
+
+
+ 0}
+ value={mentionsCount}
+ style={[styles.badge, isMuted && styles.mutedBadge, isOnCenterBg && styles.badgeOnCenterBg]}
+ />
+ {hasCall &&
+
+ }
+
);
};
-export default ChannelListItem;
+export default ChannelItem;
diff --git a/app/components/channel_item/custom_status/custom_status.tsx b/app/components/channel_item/custom_status/custom_status.tsx
index b0503b7e95..4b24f41cbb 100644
--- a/app/components/channel_item/custom_status/custom_status.tsx
+++ b/app/components/channel_item/custom_status/custom_status.tsx
@@ -2,30 +2,20 @@
// See LICENSE.txt for license information.
import React from 'react';
-import {StyleSheet} from 'react-native';
import CustomStatusEmoji from '@components/custom_status/custom_status_emoji';
+import type {EmojiCommonStyle} from '@typings/components/emoji';
+import type {StyleProp} from 'react-native';
+
type Props = {
customStatus?: UserCustomStatus;
customStatusExpired: boolean;
isCustomStatusEnabled: boolean;
- isInfo?: boolean;
- testID?: string;
+ style: StyleProp;
}
-const style = StyleSheet.create({
- customStatusEmoji: {
- color: '#000',
- marginHorizontal: -5,
- top: 3,
- },
- info: {
- marginHorizontal: -15,
- },
-});
-
-const CustomStatus = ({customStatus, customStatusExpired, isCustomStatusEnabled, isInfo, testID}: Props) => {
+const CustomStatus = ({customStatus, customStatusExpired, isCustomStatusEnabled, style}: Props) => {
const showCustomStatusEmoji = Boolean(isCustomStatusEnabled && customStatus?.emoji && !customStatusExpired);
if (!showCustomStatusEmoji) {
@@ -35,8 +25,7 @@ const CustomStatus = ({customStatus, customStatusExpired, isCustomStatusEnabled,
return (
);
};
diff --git a/app/components/channel_item/index.ts b/app/components/channel_item/index.ts
index 9e07bf3404..9683b61aa5 100644
--- a/app/components/channel_item/index.ts
+++ b/app/components/channel_item/index.ts
@@ -21,66 +21,68 @@ import type {WithDatabaseArgs} from '@typings/database/database';
import type ChannelModel from '@typings/database/models/servers/channel';
type EnhanceProps = WithDatabaseArgs & {
- channel: ChannelModel;
+ channel: ChannelModel | Channel;
showTeamName?: boolean;
serverUrl?: string;
+ shouldHighlightActive?: boolean;
+ shouldHighlightState?: boolean;
}
-const enhance = withObservables(['channel', 'showTeamName'], ({
+const enhance = withObservables(['channel', 'showTeamName', 'shouldHighlightActive', 'shouldHighlightState'], ({
channel,
database,
- showTeamName,
serverUrl,
+ showTeamName = false,
+ shouldHighlightActive = false,
+ shouldHighlightState = false,
}: EnhanceProps) => {
const currentUserId = observeCurrentUserId(database);
const myChannel = observeMyChannel(database, channel.id);
- const hasDraft = queryDraft(database, channel.id).observeWithColumns(['message', 'files']).pipe(
- switchMap((draft) => of$(draft.length > 0)),
- distinctUntilChanged(),
- );
+ const hasDraft = shouldHighlightState ?
+ queryDraft(database, channel.id).observeWithColumns(['message', 'files']).pipe(
+ switchMap((draft) => of$(draft.length > 0)),
+ distinctUntilChanged(),
+ ) : of$(false);
- const isActive = observeCurrentChannelId(database).pipe(
- switchMap((id) => of$(id ? id === channel.id : false)),
- distinctUntilChanged(),
- );
+ const isActive = shouldHighlightActive ?
+ observeCurrentChannelId(database).pipe(
+ switchMap((id) => of$(id ? id === channel.id : false)),
+ distinctUntilChanged(),
+ ) : of$(false);
- const isMuted = myChannel.pipe(
- switchMap((mc) => {
- if (!mc) {
- return of$(false);
- }
- return observeIsMutedSetting(database, mc.id);
- }),
- );
+ const isMuted = shouldHighlightState ?
+ myChannel.pipe(
+ switchMap((mc) => {
+ if (!mc) {
+ return of$(false);
+ }
+ return observeIsMutedSetting(database, mc.id);
+ }),
+ ) : of$(false);
- let teamDisplayName = of$('');
- if (channel.teamId && showTeamName) {
- teamDisplayName = observeTeam(database, channel.teamId).pipe(
+ const teamId = 'teamId' in channel ? channel.teamId : channel.team_id;
+ const teamDisplayName = (teamId && showTeamName) ?
+ observeTeam(database, teamId).pipe(
switchMap((team) => of$(team?.displayName || '')),
distinctUntilChanged(),
- );
- }
+ ) : of$('');
- let membersCount = of$(0);
- if (channel.type === General.GM_CHANNEL) {
- membersCount = queryChannelMembers(database, channel.id).observeCount(false);
- }
+ const membersCount = channel.type === General.GM_CHANNEL ?
+ queryChannelMembers(database, channel.id).observeCount(false) :
+ of$(0);
- const isUnread = myChannel.pipe(
- switchMap((mc) => of$(mc?.isUnread)),
- distinctUntilChanged(),
- );
+ const isUnread = shouldHighlightState ?
+ myChannel.pipe(
+ switchMap((mc) => of$(mc?.isUnread)),
+ distinctUntilChanged(),
+ ) : of$(false);
- const mentionsCount = myChannel.pipe(
- switchMap((mc) => of$(mc?.mentionsCount)),
- distinctUntilChanged(),
- );
-
- const hasMember = myChannel.pipe(
- switchMap((mc) => of$(Boolean(mc))),
- distinctUntilChanged(),
- );
+ const mentionsCount = shouldHighlightState ?
+ myChannel.pipe(
+ switchMap((mc) => of$(mc?.mentionsCount)),
+ distinctUntilChanged(),
+ ) : of$(0);
const hasCall = observeChannelsWithCalls(serverUrl || '').pipe(
switchMap((calls) => of$(Boolean(calls[channel.id]))),
@@ -88,7 +90,7 @@ const enhance = withObservables(['channel', 'showTeamName'], ({
);
return {
- channel: channel.observe(),
+ channel: 'observe' in channel ? channel.observe() : of$(channel),
currentUserId,
hasDraft,
isActive,
@@ -97,7 +99,6 @@ const enhance = withObservables(['channel', 'showTeamName'], ({
isUnread,
mentionsCount,
teamDisplayName,
- hasMember,
hasCall,
};
});
diff --git a/app/components/custom_status/__snapshots__/custom_status_emoji.test.tsx.snap b/app/components/custom_status/__snapshots__/custom_status_emoji.test.tsx.snap
index db71fd5862..a3f1ff4b5f 100644
--- a/app/components/custom_status/__snapshots__/custom_status_emoji.test.tsx.snap
+++ b/app/components/custom_status/__snapshots__/custom_status_emoji.test.tsx.snap
@@ -1,41 +1,35 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/custom_status/custom_status_emoji should match snapshot 1`] = `
-
-
- 📆
-
-
+ 📆
+
`;
exports[`components/custom_status/custom_status_emoji should match snapshot with props 1`] = `
-
-
- 📆
-
-
+ 📆
+
`;
diff --git a/app/components/custom_status/custom_status_emoji.test.tsx b/app/components/custom_status/custom_status_emoji.test.tsx
index b235760845..77f1e89a60 100644
--- a/app/components/custom_status/custom_status_emoji.test.tsx
+++ b/app/components/custom_status/custom_status_emoji.test.tsx
@@ -26,7 +26,6 @@ describe('components/custom_status/custom_status_emoji', () => {
const wrapper = renderWithEverything(
,
{database},
);
@@ -38,7 +37,6 @@ describe('components/custom_status/custom_status_emoji', () => {
,
{database},
);
diff --git a/app/components/custom_status/custom_status_emoji.tsx b/app/components/custom_status/custom_status_emoji.tsx
index fea1f6985d..a0140ff45a 100644
--- a/app/components/custom_status/custom_status_emoji.tsx
+++ b/app/components/custom_status/custom_status_emoji.tsx
@@ -2,29 +2,26 @@
// See LICENSE.txt for license information.
import React from 'react';
-import {StyleProp, TextStyle, View} from 'react-native';
import Emoji from '@components/emoji';
+import type {EmojiCommonStyle} from '@typings/components/emoji';
+import type {StyleProp} from 'react-native';
+
interface ComponentProps {
customStatus: UserCustomStatus;
emojiSize?: number;
- style?: StyleProp;
- testID?: string;
+ style?: StyleProp;
}
-const CustomStatusEmoji = ({customStatus, emojiSize = 16, style, testID}: ComponentProps) => {
+const CustomStatusEmoji = ({customStatus, emojiSize = 16, style}: ComponentProps) => {
if (customStatus.emoji) {
return (
-
-
-
+
);
}
diff --git a/app/components/emoji/emoji.tsx b/app/components/emoji/emoji.tsx
index 2998213551..65d48a5eac 100644
--- a/app/components/emoji/emoji.tsx
+++ b/app/components/emoji/emoji.tsx
@@ -29,12 +29,13 @@ const assetImages = new Map([['mattermost.png', require('@assets/images/emojis/m
const Emoji = (props: EmojiProps) => {
const {
customEmojis,
- customEmojiStyle,
+ imageStyle,
displayTextOnly,
emojiName,
literal = '',
testID,
textStyle,
+ commonStyle,
} = props;
const serverUrl = useServerUrl();
let assetImage = '';
@@ -73,7 +74,7 @@ const Emoji = (props: EmojiProps) => {
if (displayTextOnly || (!imageUrl && !assetImage && !unicode)) {
return (
{literal}
@@ -91,7 +92,7 @@ const Emoji = (props: EmojiProps) => {
return (
{code}
@@ -110,7 +111,7 @@ const Emoji = (props: EmojiProps) => {
@@ -128,7 +129,7 @@ const Emoji = (props: EmojiProps) => {
return (
)}
diff --git a/app/components/post_with_channel_info/channel_info/channel_info.tsx b/app/components/post_with_channel_info/channel_info/channel_info.tsx
index 7673e38657..bf165362d2 100644
--- a/app/components/post_with_channel_info/channel_info/channel_info.tsx
+++ b/app/components/post_with_channel_info/channel_info/channel_info.tsx
@@ -5,8 +5,8 @@ import React, {useCallback, useMemo, useState} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {switchToChannelById} from '@actions/remote/channel';
-import TouchableWithFeedback from '@app/components/touchable_with_feedback';
-import {useServerUrl} from '@app/context/server';
+import TouchableWithFeedback from '@components/touchable_with_feedback';
+import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
diff --git a/app/components/profile_picture/index.tsx b/app/components/profile_picture/index.tsx
index 1a96a8822b..453ede9ecf 100644
--- a/app/components/profile_picture/index.tsx
+++ b/app/components/profile_picture/index.tsx
@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React, {useEffect, useMemo} from 'react';
-import {Platform, StyleProp, View, ViewStyle} from 'react-native';
+import {StyleProp, View, ViewStyle} from 'react-native';
import {fetchStatusInBatch} from '@actions/remote/user';
import {useServerUrl} from '@context/server';
@@ -15,11 +15,6 @@ import Status from './status';
import type UserModel from '@typings/database/models/servers/user';
import type {Source} from 'react-native-fast-image';
-const STATUS_BUFFER = Platform.select({
- ios: 3,
- android: 2,
-});
-
type ProfilePictureProps = {
author?: UserModel | UserProfile;
forwardRef?: React.RefObject;
@@ -27,6 +22,7 @@ type ProfilePictureProps = {
showStatus?: boolean;
size: number;
statusSize?: number;
+ containerStyle?: StyleProp;
statusStyle?: StyleProp;
testID?: string;
source?: Source | string;
@@ -38,7 +34,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
container: {
justifyContent: 'center',
alignItems: 'center',
- borderRadius: 80,
},
icon: {
color: changeOpacity(theme.centerChannelColor, 0.48),
@@ -67,6 +62,7 @@ const ProfilePicture = ({
showStatus = true,
size = 64,
statusSize = 14,
+ containerStyle,
statusStyle,
testID,
source,
@@ -77,7 +73,6 @@ const ProfilePicture = ({
serverUrl = url || serverUrl;
const style = getStyleSheet(theme);
- const buffer = showStatus ? STATUS_BUFFER || 0 : 0;
const isBot = author && (('isBot' in author) ? author.isBot : author.is_bot);
useEffect(() => {
@@ -86,26 +81,14 @@ const ProfilePicture = ({
}
}, []);
- const containerStyle = useMemo(() => {
- if (author) {
- return {
- width: size + (buffer - 1),
- height: size + (buffer - 1),
- borderRadius: (size + buffer) / 2,
- };
- }
-
- return {
- ...style.container,
- width: size + buffer,
- height: size + buffer,
- borderRadius: (size + buffer) / 2,
- };
- }, [author, size]);
+ const viewStyle = useMemo(
+ () => [style.container, {width: size, height: size}, containerStyle],
+ [style, size, containerStyle],
+ );
return (
{
return {
statusWrapper: {
position: 'absolute',
- bottom: 0,
- right: 0,
+ bottom: -STATUS_BUFFER,
+ right: -STATUS_BUFFER,
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'center',
diff --git a/app/components/selected_chip/index.tsx b/app/components/selected_chip/index.tsx
index 3ea8e221dd..0b78e7dc20 100644
--- a/app/components/selected_chip/index.tsx
+++ b/app/components/selected_chip/index.tsx
@@ -2,11 +2,12 @@
// See LICENSE.txt for license information.
import React, {useCallback} from 'react';
-import {Text, TouchableOpacity, View} from 'react-native';
+import {Text, TouchableOpacity, useWindowDimensions} from 'react-native';
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated';
import CompassIcon from '@components/compass_icon';
import {useTheme} from '@context/theme';
+import {nonBreakingString} from '@utils/strings';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
@@ -35,11 +36,6 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
marginRight: 8,
paddingHorizontal: 7,
},
- extraContent: {
- flexDirection: 'row',
- alignItems: 'center',
- color: theme.centerChannelColor,
- },
text: {
marginLeft: 8,
color: theme.centerChannelColor,
@@ -61,6 +57,7 @@ export default function SelectedChip({
}: SelectedChipProps) {
const theme = useTheme();
const style = getStyleFromTheme(theme);
+ const dimensions = useWindowDimensions();
const onPress = useCallback(() => {
onRemove(id);
@@ -73,16 +70,13 @@ export default function SelectedChip({
style={style.container}
testID={testID}
>
- {extra && (
-
- {extra}
-
- )}
+ {extra}
- {text}
+ {nonBreakingString(text)}
void;
term: string;
@@ -24,7 +23,6 @@ type Props = {
export default function ServerUserList({
currentUserId,
- teammateNameDisplay,
tutorialWatched,
handleSelectProfile,
term,
@@ -121,7 +119,6 @@ export default function ServerUserList({
profiles={data}
selectedIds={selectedIds}
showNoResults={!loading && page.current !== -1}
- teammateNameDisplay={teammateNameDisplay}
fetchMore={getProfiles}
term={term}
testID={testID}
diff --git a/app/components/tag/index.tsx b/app/components/tag/index.tsx
index 1b376d3302..2f87a10767 100644
--- a/app/components/tag/index.tsx
+++ b/app/components/tag/index.tsx
@@ -25,9 +25,6 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => {
alignSelf: 'center',
backgroundColor: changeOpacity(theme.centerChannelColor, 0.08),
borderRadius: 4,
- marginRight: 2,
- marginBottom: 1,
- marginLeft: 2,
paddingVertical: 2,
paddingHorizontal: 4,
},
diff --git a/app/components/threads_button/__snapshots__/threads_button.test.tsx.snap b/app/components/threads_button/__snapshots__/threads_button.test.tsx.snap
index c07f7a3829..8eb4071c91 100644
--- a/app/components/threads_button/__snapshots__/threads_button.test.tsx.snap
+++ b/app/components/threads_button/__snapshots__/threads_button.test.tsx.snap
@@ -38,70 +38,62 @@ exports[`Thread item in the channel list Threads Component should match snapshot
>
-
+
-
-
- Threads
-
-
+ Threads
+
`;
-exports[`Thread item in the channel list Threads Component should match snapshot with isInfo 1`] = `
+exports[`Thread item in the channel list Threads Component should match snapshot with onCenterBg 1`] = `
-
+
-
-
- Threads
-
-
+ Threads
+
`;
@@ -247,65 +230,57 @@ exports[`Thread item in the channel list Threads Component should match snapshot
>
-
+
-
-
- Threads
-
-
+ Threads
+
`;
diff --git a/app/components/threads_button/threads_button.test.tsx b/app/components/threads_button/threads_button.test.tsx
index cbc90ecd2e..ebea2b58f5 100644
--- a/app/components/threads_button/threads_button.test.tsx
+++ b/app/components/threads_button/threads_button.test.tsx
@@ -36,11 +36,11 @@ describe('Thread item in the channel list', () => {
expect(toJSON()).toMatchSnapshot();
});
- test('Threads Component should match snapshot with isInfo', () => {
+ test('Threads Component should match snapshot with onCenterBg', () => {
const {toJSON} = renderWithIntlAndTheme(
,
);
diff --git a/app/components/threads_button/threads_button.tsx b/app/components/threads_button/threads_button.tsx
index b543ee8ae4..6a150cbae3 100644
--- a/app/components/threads_button/threads_button.tsx
+++ b/app/components/threads_button/threads_button.tsx
@@ -8,10 +8,12 @@ import {switchToGlobalThreads} from '@actions/local/thread';
import Badge from '@components/badge';
import {
getStyleSheet as getChannelItemStyleSheet,
+ ROW_HEIGHT,
textStyle as channelItemTextStyle,
} from '@components/channel_item/channel_item';
import CompassIcon from '@components/compass_icon';
import FormattedText from '@components/formatted_text';
+import {HOME_PADDING} from '@constants/view';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
@@ -19,13 +21,10 @@ import {preventDoubleTap} from '@utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
- baseContainer: {
- marginLeft: -18,
- marginRight: -20,
- },
icon: {
color: changeOpacity(theme.sidebarText, 0.5),
fontSize: 24,
+ marginRight: 12,
},
iconActive: {
color: theme.sidebarText,
@@ -41,16 +40,27 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
type Props = {
currentChannelId: string;
groupUnreadsSeparately: boolean;
- isInfo?: boolean;
+ onCenterBg?: boolean;
onlyUnreads: boolean;
onPress?: () => void;
+ shouldHighlighActive?: boolean;
unreadsAndMentions: {
unreads: boolean;
mentions: number;
};
+ isOnHome?: boolean;
};
-const ThreadsButton = ({currentChannelId, groupUnreadsSeparately, isInfo, onlyUnreads, onPress, unreadsAndMentions}: Props) => {
+const ThreadsButton = ({
+ currentChannelId,
+ groupUnreadsSeparately,
+ onCenterBg,
+ onlyUnreads,
+ onPress,
+ unreadsAndMentions,
+ shouldHighlighActive = false,
+ isOnHome = false,
+}: Props) => {
const isTablet = useIsTablet();
const serverUrl = useServerUrl();
@@ -67,18 +77,23 @@ const ThreadsButton = ({currentChannelId, groupUnreadsSeparately, isInfo, onlyUn
}), [onPress, serverUrl]);
const {unreads, mentions} = unreadsAndMentions;
- const isActive = isTablet && !currentChannelId;
+ const isActive = isTablet && shouldHighlighActive && !currentChannelId;
const [containerStyle, iconStyle, textStyle, badgeStyle] = useMemo(() => {
const container = [
styles.container,
+ isOnHome && HOME_PADDING,
isActive && styles.activeItem,
+ isActive && isOnHome && {
+ paddingLeft: HOME_PADDING.paddingLeft - styles.activeItem.borderLeftWidth,
+ },
+ {minHeight: ROW_HEIGHT},
];
const icon = [
customStyles.icon,
(isActive || unreads) && customStyles.iconActive,
- isInfo && customStyles.iconInfo,
+ onCenterBg && customStyles.iconInfo,
];
const text = [
@@ -87,16 +102,16 @@ const ThreadsButton = ({currentChannelId, groupUnreadsSeparately, isInfo, onlyUn
styles.text,
unreads && styles.highlight,
isActive && styles.textActive,
- isInfo && styles.textInfo,
+ onCenterBg && styles.textOnCenterBg,
];
const badge = [
styles.badge,
- isInfo && styles.infoBadge,
+ onCenterBg && styles.badgeOnCenterBg,
];
return [container, icon, text, badge];
- }, [customStyles, isActive, isInfo, styles, unreads]);
+ }, [customStyles, isActive, onCenterBg, styles, unreads, isOnHome]);
if (groupUnreadsSeparately && (onlyUnreads && !isActive && !unreads && !mentions)) {
return null;
@@ -107,23 +122,21 @@ const ThreadsButton = ({currentChannelId, groupUnreadsSeparately, isInfo, onlyUn
onPress={handlePress}
testID='channel_list.threads.button'
>
-
-
-
-
- 0}
- />
-
+
+
+
+ 0}
+ />
);
diff --git a/app/components/user_avatars_stack/users_list/index.tsx b/app/components/user_avatars_stack/users_list/index.tsx
index 3a8490cb27..518ad0b21f 100644
--- a/app/components/user_avatars_stack/users_list/index.tsx
+++ b/app/components/user_avatars_stack/users_list/index.tsx
@@ -4,7 +4,7 @@
import {BottomSheetFlatList} from '@gorhom/bottom-sheet';
import React, {useCallback, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
-import {Keyboard, ListRenderItemInfo, NativeScrollEvent, NativeSyntheticEvent, PanResponder, StyleProp, StyleSheet, TouchableOpacity, ViewStyle} from 'react-native';
+import {Keyboard, ListRenderItemInfo, NativeScrollEvent, NativeSyntheticEvent, PanResponder} from 'react-native';
import {FlatList} from 'react-native-gesture-handler';
import UserItem from '@components/user_item';
@@ -23,40 +23,30 @@ type Props = {
type ItemProps = {
channelId: string;
- containerStyle: StyleProp;
location: string;
user: UserModel;
}
-const style = StyleSheet.create({
- container: {
- paddingLeft: 0,
- },
-});
-
-const Item = ({channelId, containerStyle, location, user}: ItemProps) => {
+const Item = ({channelId, location, user}: ItemProps) => {
const intl = useIntl();
const theme = useTheme();
- const openUserProfile = async () => {
- if (user) {
- await dismissBottomSheet(Screens.BOTTOM_SHEET);
- const screen = Screens.USER_PROFILE;
- const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
- const closeButtonId = 'close-user-profile';
- const props = {closeButtonId, location, userId: user.id, channelId};
- Keyboard.dismiss();
- openAsBottomSheet({screen, title, theme, closeButtonId, props});
- }
- };
+ const openUserProfile = useCallback(async (u: UserModel | UserProfile) => {
+ await dismissBottomSheet(Screens.BOTTOM_SHEET);
+ const screen = Screens.USER_PROFILE;
+ const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
+ const closeButtonId = 'close-user-profile';
+ const props = {closeButtonId, location, userId: u.id, channelId};
+
+ Keyboard.dismiss();
+ openAsBottomSheet({screen, title, theme, closeButtonId, props});
+ }, [location, channelId, theme, intl]);
return (
-
-
-
+
);
};
@@ -89,7 +79,6 @@ const UsersList = ({channelId, location, type = 'FlatList', users}: Props) => {
channelId={channelId}
location={location}
user={item}
- containerStyle={style.container}
/>
), [channelId, location]);
diff --git a/app/components/user_item/index.ts b/app/components/user_item/index.ts
index fa9f733861..df41e980b6 100644
--- a/app/components/user_item/index.ts
+++ b/app/components/user_item/index.ts
@@ -3,8 +3,11 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
+import {of as of$} from 'rxjs';
+import {distinctUntilChanged, switchMap} from 'rxjs/operators';
import {observeConfigBooleanValue, observeCurrentUserId} from '@queries/servers/system';
+import {observeCurrentUser, observeTeammateNameDisplay} from '@queries/servers/user';
import UserItem from './user_item';
@@ -12,12 +15,17 @@ import type {WithDatabaseArgs} from '@typings/database/database';
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
const isCustomStatusEnabled = observeConfigBooleanValue(database, 'EnableCustomUserStatuses');
- const showFullName = observeConfigBooleanValue(database, 'ShowFullName');
const currentUserId = observeCurrentUserId(database);
+ const locale = observeCurrentUser(database).pipe(
+ switchMap((u) => of$(u?.locale)),
+ distinctUntilChanged(),
+ );
+ const teammateNameDisplay = observeTeammateNameDisplay(database);
return {
isCustomStatusEnabled,
- showFullName,
currentUserId,
+ locale,
+ teammateNameDisplay,
};
});
diff --git a/app/components/user_item/user_item.tsx b/app/components/user_item/user_item.tsx
index 24c0812d78..656bd9b33e 100644
--- a/app/components/user_item/user_item.tsx
+++ b/app/components/user_item/user_item.tsx
@@ -1,104 +1,92 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import React, {useMemo} from 'react';
-import {IntlShape, useIntl} from 'react-intl';
-import {StyleProp, Text, View, ViewStyle} from 'react-native';
+import React, {useCallback, useMemo} from 'react';
+import {useIntl} from 'react-intl';
+import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
-import ChannelIcon from '@components/channel_icon';
+import CompassIcon from '@components/compass_icon';
import CustomStatusEmoji from '@components/custom_status/custom_status_emoji';
-import FormattedText from '@components/formatted_text';
import ProfilePicture from '@components/profile_picture';
import {BotTag, GuestTag} from '@components/tag';
-import {General} from '@constants';
import {useTheme} from '@context/theme';
+import {nonBreakingString} from '@utils/strings';
import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme';
import {typography} from '@utils/typography';
-import {getUserCustomStatus, isBot, isCustomStatusExpired, isGuest, isShared} from '@utils/user';
+import {displayUsername, getUserCustomStatus, isBot, isCustomStatusExpired, isGuest, isShared} from '@utils/user';
import type UserModel from '@typings/database/models/servers/user';
type AtMentionItemProps = {
- user?: UserProfile | UserModel;
- containerStyle?: StyleProp;
+ user: UserProfile | UserModel;
currentUserId: string;
- showFullName: boolean;
testID?: string;
isCustomStatusEnabled: boolean;
- pictureContainerStyle?: StyleProp;
+ showBadges?: boolean;
+ locale?: string;
+ teammateNameDisplay: string;
+ rightDecorator?: React.ReactNode;
+ onUserPress?: (user: UserProfile | UserModel) => void;
+ onUserLongPress?: (user: UserProfile | UserModel) => void;
+ disabled?: boolean;
+ viewRef?: React.LegacyRef;
+ padding?: number;
}
-const getName = (user: UserProfile | UserModel | undefined, showFullName: boolean, isCurrentUser: boolean, intl: IntlShape) => {
- let name = '';
- if (!user) {
- return intl.formatMessage({id: 'channel_loader.someone', defaultMessage: 'Someone'});
- }
-
- const hasNickname = user.nickname.length > 0;
-
- if (showFullName) {
- const first = 'first_name' in user ? user.first_name : user.firstName;
- const last = 'last_name' in user ? user.last_name : user.lastName;
- name += `${first} ${last} `;
- }
-
- if (hasNickname && !isCurrentUser) {
- name += name.length > 0 ? `(${user.nickname})` : user.nickname;
- }
-
- return name.trim();
-};
-
-const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => {
+const getThemedStyles = makeStyleSheetFromTheme((theme: Theme) => {
return {
- row: {
- height: 40,
- paddingVertical: 8,
- paddingTop: 4,
- paddingHorizontal: 16,
- flexDirection: 'row',
- alignItems: 'center',
- },
- rowPicture: {
- marginRight: 10,
- marginLeft: 2,
- width: 24,
- alignItems: 'center',
- justifyContent: 'center',
- },
- rowInfo: {
- flexDirection: 'row',
- overflow: 'hidden',
- },
rowFullname: {
...typography('Body', 200),
color: theme.centerChannelColor,
- paddingLeft: 4,
+ flex: 0,
flexShrink: 1,
},
rowUsername: {
- ...typography('Body', 200),
+ ...typography('Body', 100),
color: changeOpacity(theme.centerChannelColor, 0.64),
- fontSize: 15,
- fontFamily: 'OpenSans',
- },
- icon: {
- marginLeft: 4,
},
};
});
+const nonThemedStyles = StyleSheet.create({
+ row: {
+ height: 40,
+ paddingVertical: 8,
+ paddingTop: 4,
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ icon: {
+ marginLeft: 4,
+ },
+ profile: {
+ marginRight: 12,
+ },
+ tag: {
+ marginLeft: 6,
+ },
+ flex: {
+ flex: 1,
+ },
+});
+
const UserItem = ({
- containerStyle,
user,
currentUserId,
- showFullName,
testID,
isCustomStatusEnabled,
- pictureContainerStyle,
+ showBadges = false,
+ locale,
+ teammateNameDisplay,
+ rightDecorator,
+ onUserPress,
+ onUserLongPress,
+ disabled = false,
+ viewRef,
+ padding,
}: AtMentionItemProps) => {
const theme = useTheme();
- const style = getStyleFromTheme(theme);
+ const style = getThemedStyles(theme);
const intl = useIntl();
const bot = user ? isBot(user) : false;
@@ -106,91 +94,109 @@ const UserItem = ({
const shared = user ? isShared(user) : false;
const isCurrentUser = currentUserId === user?.id;
- const name = getName(user, showFullName, isCurrentUser, intl);
const customStatus = getUserCustomStatus(user);
const customStatusExpired = isCustomStatusExpired(user);
- const userItemTestId = `${testID}.${user?.id}`;
+ const deleteAt = 'deleteAt' in user ? user.deleteAt : user.delete_at;
- let rowUsernameFlexShrink = 1;
- if (user) {
- for (const rowInfoElem of [bot, guest, Boolean(name.length), isCurrentUser]) {
- if (rowInfoElem) {
- rowUsernameFlexShrink++;
- }
- }
+ let displayName = displayUsername(user, locale, teammateNameDisplay);
+ const showTeammateDisplay = displayName !== user?.username;
+ if (isCurrentUser) {
+ displayName = intl.formatMessage({id: 'channel_header.directchannel.you', defaultMessage: '{displayName} (you)'}, {displayName});
}
- const usernameTextStyle = useMemo(() => {
- return [style.rowUsername, {flexShrink: rowUsernameFlexShrink}];
- }, [user, rowUsernameFlexShrink]);
+ const userItemTestId = `${testID}.${user?.id}`;
+
+ const containerStyle = useMemo(() => {
+ return [
+ nonThemedStyles.row,
+ {
+ opacity: disabled ? 0.32 : 1,
+ paddingHorizontal: padding || undefined,
+ },
+ ];
+ }, [disabled, padding]);
+
+ const onPress = useCallback(() => {
+ onUserPress?.(user);
+ }, [user, onUserPress]);
+
+ const onLongPress = useCallback(() => {
+ onUserLongPress?.(user);
+ }, [user, onUserLongPress]);
return (
-
-
+
-
-
- {bot && }
- {guest && }
- {Boolean(name.length) &&
-
- {name}
-
- }
- {isCurrentUser &&
-
+ {nonBreakingString(displayName)}
+ {Boolean(showTeammateDisplay) && (
+
+ {nonBreakingString(` @${user!.username}`)}
+
+ )}
+ {Boolean(deleteAt) && (
+
+ {nonBreakingString(` ${intl.formatMessage({id: 'mobile.user_list.deactivated', defaultMessage: 'Deactivated'})}`)}
+
+ )}
+
+ {showBadges && bot && (
+
- }
- {Boolean(user) && (
-
- {` @${user!.username}`}
-
)}
+ {showBadges && guest && (
+
+ )}
+ {Boolean(isCustomStatusEnabled && !bot && customStatus?.emoji && !customStatusExpired) && (
+
+ )}
+ {shared && (
+
+ )}
+
+ {Boolean(rightDecorator) && rightDecorator}
- {Boolean(isCustomStatusEnabled && !bot && customStatus?.emoji && !customStatusExpired) && (
-
- )}
- {shared && (
-
- )}
-
+
);
};
diff --git a/app/components/user_list/__snapshots__/index.test.tsx.snap b/app/components/user_list/__snapshots__/index.test.tsx.snap
index 45fc81c90d..74c833e9d9 100644
--- a/app/components/user_list/__snapshots__/index.test.tsx.snap
+++ b/app/components/user_list/__snapshots__/index.test.tsx.snap
@@ -117,141 +117,123 @@ exports[`components/channel_list_row should show no results 1`] = `
style={null}
>
+
+
+
+
+ johndoe
+
+
-
-
-
-
-
-
-
-
- johndoe
-
-
-
-
-
-
+
@@ -400,141 +382,123 @@ exports[`components/channel_list_row should show results and tutorial 1`] = `
style={null}
>
+
+
+
+
+ johndoe
+
+
-
-
-
-
-
-
-
-
- johndoe
-
-
-
-
-
-
+
@@ -763,141 +727,123 @@ exports[`components/channel_list_row should show results no tutorial 1`] = `
style={null}
>
+
+
+
+
+ johndoe
+
+
-
-
-
-
-
-
-
-
- johndoe
-
-
-
-
-
-
+
@@ -1078,141 +1024,123 @@ exports[`components/channel_list_row should show results no tutorial 2 users 1`]
style={null}
>
+
+
+
+
+ johndoe
+
+
-
-
-
-
-
-
-
-
- johndoe
-
-
-
-
-
-
+
@@ -1266,141 +1194,123 @@ exports[`components/channel_list_row should show results no tutorial 2 users 1`]
style={null}
>
+
+
+
+
+ rocky
+
+
-
-
-
-
-
-
-
-
- rocky
-
-
-
-
-
-
+
diff --git a/app/components/user_list/index.test.tsx b/app/components/user_list/index.test.tsx
index fd0ae5d99c..8ae4fbaf65 100644
--- a/app/components/user_list/index.test.tsx
+++ b/app/components/user_list/index.test.tsx
@@ -77,7 +77,6 @@ describe('components/channel_list_row', () => {
profiles={[user]}
testID='UserListRow'
currentUserId={'1'}
- teammateNameDisplay={'johndoe'}
handleSelectProfile={() => {
// noop
}}
@@ -101,7 +100,6 @@ describe('components/channel_list_row', () => {
profiles={[user]}
testID='UserListRow'
currentUserId={'1'}
- teammateNameDisplay={'johndoe'}
handleSelectProfile={() => {
// noop
}}
@@ -125,7 +123,6 @@ describe('components/channel_list_row', () => {
profiles={[user, user2]}
testID='UserListRow'
currentUserId={'1'}
- teammateNameDisplay={'johndoe'}
handleSelectProfile={() => {
// noop
}}
@@ -149,7 +146,6 @@ describe('components/channel_list_row', () => {
profiles={[user]}
testID='UserListRow'
currentUserId={'1'}
- teammateNameDisplay={'johndoe'}
handleSelectProfile={() => {
// noop
}}
diff --git a/app/components/user_list/index.tsx b/app/components/user_list/index.tsx
index 9383e1cda3..a78436d1e1 100644
--- a/app/components/user_list/index.tsx
+++ b/app/components/user_list/index.tsx
@@ -21,6 +21,8 @@ import {
} from '@utils/theme';
import {typography} from '@utils/typography';
+import type UserModel from '@typings/database/models/servers/user';
+
type UserProfileWithChannelAdmin = UserProfile & {scheme_admin?: boolean}
type RenderItemType = ListRenderItemInfo & {section?: SectionListData}
@@ -147,8 +149,7 @@ type Props = {
profiles: UserProfile[];
channelMembers?: ChannelMembership[];
currentUserId: string;
- teammateNameDisplay: string;
- handleSelectProfile: (user: UserProfile) => void;
+ handleSelectProfile: (user: UserProfile | UserModel) => void;
fetchMore?: () => void;
loading: boolean;
manageMode?: boolean;
@@ -165,7 +166,6 @@ export default function UserList({
channelMembers,
selectedIds,
currentUserId,
- teammateNameDisplay,
handleSelectProfile,
fetchMore,
loading,
@@ -198,21 +198,29 @@ export default function UserList({
return createProfilesSections(intl, profiles, channelMembers);
}, [channelMembers, loading, profiles, term]);
- const openUserProfile = useCallback(async (profile: UserProfile) => {
- const {user} = await storeProfile(serverUrl, profile);
- if (user) {
- const screen = Screens.USER_PROFILE;
- const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
- const closeButtonId = 'close-user-profile';
- const props = {
- closeButtonId,
- userId: user.id,
- location: Screens.USER_PROFILE,
- };
-
- Keyboard.dismiss();
- openAsBottomSheet({screen, title, theme, closeButtonId, props});
+ const openUserProfile = useCallback(async (profile: UserProfile | UserModel) => {
+ let user: UserModel;
+ if ('create_at' in profile) {
+ const res = await storeProfile(serverUrl, profile);
+ if (!res.user) {
+ return;
+ }
+ user = res.user;
+ } else {
+ user = profile;
}
+
+ const screen = Screens.USER_PROFILE;
+ const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
+ const closeButtonId = 'close-user-profile';
+ const props = {
+ closeButtonId,
+ userId: user.id,
+ location: Screens.USER_PROFILE,
+ };
+
+ Keyboard.dismiss();
+ openAsBottomSheet({screen, title, theme, closeButtonId, props});
}, []);
const renderItem = useCallback(({item, index, section}: RenderItemType) => {
@@ -237,12 +245,11 @@ export default function UserList({
selected={selected}
showManageMode={showManageMode}
testID='create_direct_message.user_list.user_item'
- teammateNameDisplay={teammateNameDisplay}
tutorialWatched={tutorialWatched}
user={item}
/>
);
- }, [selectedIds, handleSelectProfile, showManageMode, manageMode, teammateNameDisplay, tutorialWatched]);
+ }, [selectedIds, handleSelectProfile, showManageMode, manageMode, tutorialWatched]);
const renderLoading = useCallback(() => {
if (!loading) {
diff --git a/app/components/user_list_row/index.tsx b/app/components/user_list_row/index.tsx
index 85bcc2b919..bf9faa9c73 100644
--- a/app/components/user_list_row/index.tsx
+++ b/app/components/user_list_row/index.tsx
@@ -6,24 +6,22 @@ import {useIntl} from 'react-intl';
import {
InteractionManager,
Platform,
- Text,
View,
} from 'react-native';
import {storeProfileLongPressTutorial} from '@actions/app/global';
import CompassIcon from '@components/compass_icon';
import FormattedText from '@components/formatted_text';
-import ProfilePicture from '@components/profile_picture';
-import {BotTag, GuestTag} from '@components/tag';
-import TouchableWithFeedback from '@components/touchable_with_feedback';
import TutorialHighlight from '@components/tutorial_highlight';
import TutorialLongPress from '@components/tutorial_highlight/long_press';
+import UserItem from '@components/user_item';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import {t} from '@i18n';
import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme';
import {typography} from '@utils/typography';
-import {displayUsername, isGuest} from '@utils/user';
+
+import type UserModel from '@typings/database/models/servers/user';
type Props = {
highlight?: boolean;
@@ -31,13 +29,12 @@ type Props = {
isMyUser: boolean;
isChannelAdmin: boolean;
manageMode: boolean;
- onLongPress: (user: UserProfile) => void;
- onPress?: (user: UserProfile) => void;
+ onLongPress: (user: UserProfile | UserModel) => void;
+ onPress?: (user: UserProfile | UserModel) => void;
selectable: boolean;
disabled?: boolean;
selected: boolean;
showManageMode: boolean;
- teammateNameDisplay: string;
testID: string;
tutorialWatched?: boolean;
user: UserProfile;
@@ -45,54 +42,16 @@ type Props = {
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
return {
- container: {
- flex: 1,
- flexDirection: 'row',
- paddingHorizontal: 20,
- height: 58,
- overflow: 'hidden',
- },
- profileContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- color: theme.centerChannelColor,
- },
- textContainer: {
- paddingHorizontal: 10,
- justifyContent: 'center',
- flexDirection: 'column',
- flex: 1,
- },
- username: {
- color: changeOpacity(theme.centerChannelColor, 0.64),
- ...typography('Body', 75, 'Regular'),
- },
- displayName: {
- height: 24,
- color: theme.centerChannelColor,
- maxWidth: '80%',
- ...typography('Body', 200, 'Regular'),
- },
- indicatorContainer: {
- flexDirection: 'row',
- },
- deactivated: {
- marginTop: 2,
- fontSize: 12,
- color: changeOpacity(theme.centerChannelColor, 0.64),
- },
- sharedUserIcon: {
- alignSelf: 'center',
- opacity: 0.75,
- },
selector: {
alignItems: 'center',
justifyContent: 'center',
+ marginLeft: 12,
},
selectorManage: {
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'center',
+ marginLeft: 12,
},
manageText: {
color: changeOpacity(theme.centerChannelColor, 0.64),
@@ -107,7 +66,6 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
};
});
-const DISABLED_OPACITY = 0.32;
const DEFAULT_ICON_OPACITY = 0.32;
function UserListRow({
@@ -122,7 +80,6 @@ function UserListRow({
disabled,
selected,
showManageMode = false,
- teammateNameDisplay,
testID,
tutorialWatched = false,
user,
@@ -133,8 +90,7 @@ function UserListRow({
const [itemBounds, setItemBounds] = useState({startX: 0, startY: 0, endX: 0, endY: 0});
const viewRef = useRef(null);
const style = getStyleFromTheme(theme);
- const {formatMessage, locale} = useIntl();
- const {username} = user;
+ const {formatMessage} = useIntl();
const startTutorial = () => {
viewRef.current?.measureInWindow((x, y, w, h) => {
@@ -167,16 +123,12 @@ function UserListRow({
}
}, [highlight, tutorialWatched, isTablet]);
- const handlePress = useCallback(() => {
+ const handlePress = useCallback((u: UserModel | UserProfile) => {
if (isMyUser && manageMode) {
return;
}
- onPress?.(user);
- }, [onPress, isMyUser, manageMode, user]);
-
- const handleLongPress = useCallback(() => {
- onLongPress?.(user);
- }, [onLongPress, user]);
+ onPress?.(u);
+ }, [onPress, isMyUser, manageMode]);
const manageModeIcon = useMemo(() => {
if (!showManageMode || isMyUser) {
@@ -208,12 +160,11 @@ function UserListRow({
}, []);
const icon = useMemo(() => {
- if (!selectable) {
+ if (!selectable && !selected) {
return null;
}
- const iconOpacity = DEFAULT_ICON_OPACITY * (disabled ? DISABLED_OPACITY : 1);
- const color = selected ? theme.buttonBg : changeOpacity(theme.centerChannelColor, iconOpacity);
+ const color = selected ? theme.buttonBg : changeOpacity(theme.centerChannelColor, DEFAULT_ICON_OPACITY);
return (
-
-
-
-
-
-
-
-
- {teammateDisplay}
-
-
-
-
- {showTeammateDisplay &&
-
-
- {usernameDisplay}
-
-
- }
- {user.delete_at > 0 &&
-
-
- {formatMessage({id: 'mobile.user_list.deactivated', defaultMessage: 'Deactivated'})}
-
-
- }
-
- {manageMode ? manageModeIcon : icon}
-
-
+
{showTutorial &&
}
diff --git a/app/screens/channel_add_members/channel_add_members.tsx b/app/screens/channel_add_members/channel_add_members.tsx
index 30e9ddc3fa..dbd6687200 100644
--- a/app/screens/channel_add_members/channel_add_members.tsx
+++ b/app/screens/channel_add_members/channel_add_members.tsx
@@ -273,7 +273,6 @@ export default function ChannelAddMembers({
currentUserId={currentUserId}
handleSelectProfile={handleSelectProfile}
selectedIds={selectedIds}
- teammateNameDisplay={teammateNameDisplay}
term={term}
testID={`${TEST_ID}.user_list`}
tutorialWatched={tutorialWatched}
diff --git a/app/screens/channel_info/options/add_members/add_members.tsx b/app/screens/channel_info/options/add_members/add_members.tsx
index 106c4af67a..b031841ca4 100644
--- a/app/screens/channel_info/options/add_members/add_members.tsx
+++ b/app/screens/channel_info/options/add_members/add_members.tsx
@@ -5,10 +5,10 @@ import React from 'react';
import {useIntl} from 'react-intl';
import {Platform} from 'react-native';
-import {getHeaderOptions} from '@app/screens/channel_add_members/channel_add_members';
import OptionItem from '@components/option_item';
import {Screens} from '@constants';
import {useTheme} from '@context/theme';
+import {getHeaderOptions} from '@screens/channel_add_members/channel_add_members';
import {goToScreen} from '@screens/navigation';
import {preventDoubleTap} from '@utils/tap';
diff --git a/app/screens/channel_notification_preferences/muted_banner.tsx b/app/screens/channel_notification_preferences/muted_banner.tsx
index 0b7451ef4b..f315c9ff52 100644
--- a/app/screens/channel_notification_preferences/muted_banner.tsx
+++ b/app/screens/channel_notification_preferences/muted_banner.tsx
@@ -7,13 +7,13 @@ import {View} from 'react-native';
import Animated, {FlipOutXUp} from 'react-native-reanimated';
import {toggleMuteChannel} from '@actions/remote/channel';
-import Button from '@app/components/button';
-import CompassIcon from '@app/components/compass_icon';
-import FormattedText from '@app/components/formatted_text';
-import {useServerUrl} from '@app/context/server';
-import {useTheme} from '@app/context/theme';
-import {preventDoubleTap} from '@app/utils/tap';
-import {changeOpacity, makeStyleSheetFromTheme} from '@app/utils/theme';
+import Button from '@components/button';
+import CompassIcon from '@components/compass_icon';
+import FormattedText from '@components/formatted_text';
+import {useServerUrl} from '@context/server';
+import {useTheme} from '@context/theme';
+import {preventDoubleTap} from '@utils/tap';
+import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
type Props = {
diff --git a/app/screens/create_direct_message/create_direct_message.tsx b/app/screens/create_direct_message/create_direct_message.tsx
index 4e4dd36138..5ea8f0ac35 100644
--- a/app/screens/create_direct_message/create_direct_message.tsx
+++ b/app/screens/create_direct_message/create_direct_message.tsx
@@ -320,7 +320,6 @@ export default function CreateDirectMessage({
currentUserId={currentUserId}
handleSelectProfile={handleSelectProfile}
selectedIds={selectedIds}
- teammateNameDisplay={teammateNameDisplay}
term={term}
testID='create_direct_message.user_list'
tutorialWatched={tutorialWatched}
diff --git a/app/screens/find_channels/filtered_list/filtered_list.tsx b/app/screens/find_channels/filtered_list/filtered_list.tsx
index 7cc19de2ca..96343d6e06 100644
--- a/app/screens/find_channels/filtered_list/filtered_list.tsx
+++ b/app/screens/find_channels/filtered_list/filtered_list.tsx
@@ -14,14 +14,12 @@ import ChannelItem from '@components/channel_item';
import Loading from '@components/loading';
import NoResultsWithTerm from '@components/no_results_with_term';
import ThreadsButton from '@components/threads_button';
+import UserItem from '@components/user_item';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {sortChannelsByDisplayName} from '@utils/channel';
import {displayUsername} from '@utils/user';
-import RemoteChannelItem from './remote_channel_item';
-import UserItem from './user_item';
-
import type ChannelModel from '@typings/database/models/servers/channel';
import type UserModel from '@typings/database/models/servers/user';
@@ -144,8 +142,9 @@ const FilteredList = ({
onLoading(false);
};
- const onJoinChannel = useCallback(async (channelId: string, displayName: string) => {
- const {error} = await joinChannelIfNeeded(serverUrl, channelId);
+ const onJoinChannel = useCallback(async (c: Channel | ChannelModel) => {
+ const {error} = await joinChannelIfNeeded(serverUrl, c.id);
+ const displayName = 'display_name' in c ? c.display_name : c.displayName;
if (error) {
Alert.alert(
'',
@@ -158,11 +157,12 @@ const FilteredList = ({
}
await close();
- switchToChannelById(serverUrl, channelId, undefined, true);
+ switchToChannelById(serverUrl, c.id, undefined, true);
}, [serverUrl, close, locale]);
- const onOpenDirectMessage = useCallback(async (teammateId: string, displayName: string) => {
- const {data, error} = await makeDirectChannel(serverUrl, teammateId, displayName, false);
+ const onOpenDirectMessage = useCallback(async (u: UserProfile | UserModel) => {
+ const displayName = displayUsername(u, locale, teammateDisplayNameSetting);
+ const {data, error} = await makeDirectChannel(serverUrl, u.id, displayName, false);
if (error || !data) {
Alert.alert(
'',
@@ -176,11 +176,11 @@ const FilteredList = ({
await close();
switchToChannelById(serverUrl, data.id);
- }, [serverUrl, close, locale]);
+ }, [serverUrl, close, locale, teammateDisplayNameSetting]);
- const onSwitchToChannel = useCallback(async (channelId: string) => {
+ const onSwitchToChannel = useCallback(async (c: Channel | ChannelModel) => {
await close();
- switchToChannelById(serverUrl, channelId);
+ switchToChannelById(serverUrl, c.id);
}, [serverUrl, close]);
const onSwitchToThreads = useCallback(async () => {
@@ -214,7 +214,7 @@ const FilteredList = ({
if (item === 'thread') {
return (
);
@@ -223,27 +223,30 @@ const FilteredList = ({
return (
);
} else if ('username' in item) {
return (
);
}
return (
-
);
diff --git a/app/screens/find_channels/filtered_list/remote_channel_item/index.ts b/app/screens/find_channels/filtered_list/remote_channel_item/index.ts
deleted file mode 100644
index 63676e0620..0000000000
--- a/app/screens/find_channels/filtered_list/remote_channel_item/index.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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 React from 'react';
-import {of as of$} from 'rxjs';
-import {switchMap} from 'rxjs/operators';
-
-import {observeTeam} from '@queries/servers/team';
-
-import RemoteChannelItem from './remote_channel_item';
-
-import type {WithDatabaseArgs} from '@typings/database/database';
-
-type EnhanceProps = WithDatabaseArgs & {
- channel: Channel;
- showTeamName?: boolean;
-}
-
-const enhance = withObservables(['channel', 'showTeamName'], ({channel, database, showTeamName}: EnhanceProps) => {
- let teamDisplayName = of$('');
- if (channel.team_id && showTeamName) {
- teamDisplayName = observeTeam(database, channel.team_id).pipe(
- switchMap((team) => of$(team?.displayName || '')),
- );
- }
-
- return {
- teamDisplayName,
- };
-});
-
-export default React.memo(withDatabase(enhance(RemoteChannelItem)));
diff --git a/app/screens/find_channels/filtered_list/remote_channel_item/remote_channel_item.tsx b/app/screens/find_channels/filtered_list/remote_channel_item/remote_channel_item.tsx
deleted file mode 100644
index 7a88214f00..0000000000
--- a/app/screens/find_channels/filtered_list/remote_channel_item/remote_channel_item.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-import React, {useCallback, useMemo} from 'react';
-import {Text, TouchableOpacity, View} from 'react-native';
-
-import ChannelIcon from '@components/channel_icon';
-import {useTheme} from '@context/theme';
-import {useIsTablet} from '@hooks/device';
-import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
-import {typography} from '@utils/typography';
-
-type Props = {
- onPress: (channelId: string, displayName: string) => void;
- channel: Channel;
- teamDisplayName?: string;
- testID?: string;
-}
-
-export const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
- container: {
- flexDirection: 'row',
- paddingHorizontal: 0,
- minHeight: 44,
- alignItems: 'center',
- marginVertical: 2,
- },
- wrapper: {
- flex: 1,
- flexDirection: 'row',
- },
- text: {
- marginTop: -1,
- color: theme.centerChannelColor,
- paddingLeft: 12,
- paddingRight: 20,
- ...typography('Body', 200, 'Regular'),
- },
- teamName: {
- color: changeOpacity(theme.centerChannelColor, 0.64),
- paddingLeft: 12,
- marginTop: 4,
- ...typography('Body', 75),
- },
- teamNameTablet: {
- marginLeft: -12,
- paddingLeft: 0,
- marginTop: 0,
- paddingBottom: 0,
- top: 5,
- },
-}));
-
-const RemoteChannelItem = ({onPress, channel, teamDisplayName, testID}: Props) => {
- const theme = useTheme();
- const isTablet = useIsTablet();
- const styles = getStyleSheet(theme);
- const height = (teamDisplayName && !isTablet) ? 58 : 44;
-
- const handleOnPress = useCallback(() => {
- onPress(channel.id, channel.display_name);
- }, [channel]);
-
- const containerStyle = useMemo(() => [
- styles.container,
- {minHeight: height},
- ],
- [height, styles]);
-
- return (
-
- <>
-
-
- 0}
- name={channel.name}
- shared={channel.shared}
- size={24}
- type={channel.type}
- />
-
-
- {channel.display_name}
-
- {Boolean(teamDisplayName) && !isTablet &&
-
- {teamDisplayName}
-
- }
-
- {Boolean(teamDisplayName) && isTablet &&
-
- {teamDisplayName}
-
- }
-
-
- >
-
- );
-};
-
-export default RemoteChannelItem;
diff --git a/app/screens/find_channels/filtered_list/user_item/index.ts b/app/screens/find_channels/filtered_list/user_item/index.ts
deleted file mode 100644
index 3eab004ecf..0000000000
--- a/app/screens/find_channels/filtered_list/user_item/index.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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 React from 'react';
-
-import {observeCurrentUserId} from '@queries/servers/system';
-import {observeTeammateNameDisplay} from '@queries/servers/user';
-
-import UserItem from './user_item';
-
-import type {WithDatabaseArgs} from '@typings/database/database';
-import type UserModel from '@typings/database/models/servers/user';
-
-type EnhanceProps = WithDatabaseArgs & {
- user: UserModel;
-}
-
-const enhance = withObservables(['user'], ({database, user}: EnhanceProps) => ({
- currentUserId: observeCurrentUserId(database),
- teammateDisplayNameSetting: observeTeammateNameDisplay(database),
- user: user.observe(),
-}));
-
-export default React.memo(withDatabase(enhance(UserItem)));
diff --git a/app/screens/find_channels/filtered_list/user_item/user_item.tsx b/app/screens/find_channels/filtered_list/user_item/user_item.tsx
deleted file mode 100644
index 4efcec50df..0000000000
--- a/app/screens/find_channels/filtered_list/user_item/user_item.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-import React, {useCallback} from 'react';
-import {useIntl} from 'react-intl';
-import {Text, TouchableOpacity, View} from 'react-native';
-
-import CustomStatus from '@components/channel_item/custom_status';
-import ProfilePicture from '@components/profile_picture';
-import {useTheme} from '@context/theme';
-import {makeStyleSheetFromTheme} from '@utils/theme';
-import {typography} from '@utils/typography';
-import {displayUsername} from '@utils/user';
-
-import type UserModel from '@typings/database/models/servers/user';
-
-type Props = {
- currentUserId: string;
- onPress: (channelId: string, displayName: string) => void;
- teammateDisplayNameSetting?: string;
- testID?: string;
- user: UserModel;
-}
-
-export const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
- container: {
- flexDirection: 'row',
- paddingHorizontal: 0,
- height: 44,
- alignItems: 'center',
- marginVertical: 2,
- },
- wrapper: {
- flex: 1,
- flexDirection: 'row',
- },
- text: {
- marginTop: -1,
- color: theme.centerChannelColor,
- paddingLeft: 12,
- paddingRight: 20,
- ...typography('Body', 200, 'Regular'),
- },
- avatar: {marginLeft: 4},
- status: {
- backgroundColor: theme.centerChannelBg,
- borderWidth: 0,
- },
-}));
-
-const UserItem = ({currentUserId, onPress, teammateDisplayNameSetting, testID, user}: Props) => {
- const {formatMessage, locale} = useIntl();
- const theme = useTheme();
- const styles = getStyleSheet(theme);
- const isOwnDirectMessage = currentUserId === user.id;
- const displayName = displayUsername(user, locale, teammateDisplayNameSetting);
- const userItemTestId = `${testID}.${user.id}`;
-
- const handleOnPress = useCallback(() => {
- onPress(user.id, displayName);
- }, [user.id, displayName, onPress]);
-
- return (
-
- <>
-
-
-
-
-
-
-
- {isOwnDirectMessage ? formatMessage({id: 'channel_header.directchannel.you', defaultMessage: '{displayName} (you)'}, {displayName}) : displayName}
-
-
-
-
-
- >
-
- );
-};
-
-export default UserItem;
diff --git a/app/screens/find_channels/unfiltered_list/unfiltered_list.tsx b/app/screens/find_channels/unfiltered_list/unfiltered_list.tsx
index fc0ea767ae..dce037b174 100644
--- a/app/screens/find_channels/unfiltered_list/unfiltered_list.tsx
+++ b/app/screens/find_channels/unfiltered_list/unfiltered_list.tsx
@@ -52,9 +52,9 @@ const UnfilteredList = ({close, keyboardHeight, recentChannels, showTeamName, te
const [sections, setSections] = useState(buildSections(recentChannels));
const sectionListStyle = useMemo(() => ({paddingBottom: keyboardHeight}), [keyboardHeight]);
- const onPress = useCallback(async (channelId: string) => {
+ const onPress = useCallback(async (c: Channel | ChannelModel) => {
await close();
- switchToChannelById(serverUrl, channelId);
+ switchToChannelById(serverUrl, c.id);
}, [serverUrl, close]);
const renderSectionHeader = useCallback(({section}: SectionListRenderItemInfo) => (
@@ -65,9 +65,10 @@ const UnfilteredList = ({close, keyboardHeight, recentChannels, showTeamName, te
return (
);
diff --git a/app/screens/global_threads/threads_list/thread/thread.tsx b/app/screens/global_threads/threads_list/thread/thread.tsx
index 5a4cbbf93d..a96197f588 100644
--- a/app/screens/global_threads/threads_list/thread/thread.tsx
+++ b/app/screens/global_threads/threads_list/thread/thread.tsx
@@ -7,10 +7,10 @@ import {Text, TouchableHighlight, View} from 'react-native';
import {switchToChannelById} from '@actions/remote/channel';
import {fetchAndSwitchToThread} from '@actions/remote/thread';
-import TouchableWithFeedback from '@app/components/touchable_with_feedback';
import FormattedText from '@components/formatted_text';
import FriendlyDate from '@components/friendly_date';
import RemoveMarkdown from '@components/remove_markdown';
+import TouchableWithFeedback from '@components/touchable_with_feedback';
import {Screens} from '@constants';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
diff --git a/app/screens/home/channel_list/categories_list/__snapshots__/index.test.tsx.snap b/app/screens/home/channel_list/categories_list/__snapshots__/index.test.tsx.snap
index 0c36ae6be6..9767f9a211 100644
--- a/app/screens/home/channel_list/categories_list/__snapshots__/index.test.tsx.snap
+++ b/app/screens/home/channel_list/categories_list/__snapshots__/index.test.tsx.snap
@@ -15,8 +15,6 @@ exports[`components/categories_list should render channels error 1`] = `
"backgroundColor": "#1e325c",
"flex": 1,
"maxWidth": 750,
- "paddingLeft": 18,
- "paddingRight": 20,
"paddingTop": 10,
}
}
@@ -33,6 +31,8 @@ exports[`components/categories_list should render channels error 1`] = `
style={
{
"marginLeft": 0,
+ "paddingLeft": 18,
+ "paddingRight": 20,
}
}
>
@@ -350,8 +350,6 @@ exports[`components/categories_list should render team error 1`] = `
"backgroundColor": "#1e325c",
"flex": 1,
"maxWidth": 750,
- "paddingLeft": 18,
- "paddingRight": 20,
"paddingTop": 10,
}
}
@@ -368,6 +366,8 @@ exports[`components/categories_list should render team error 1`] = `
style={
{
"marginLeft": 0,
+ "paddingLeft": 18,
+ "paddingRight": 20,
}
}
>
@@ -467,6 +467,8 @@ exports[`components/categories_list should render team error 1`] = `
style={
{
"flexDirection": "row",
+ "paddingLeft": 18,
+ "paddingRight": 20,
}
}
>
diff --git a/app/screens/home/channel_list/categories_list/categories/body/category_body.tsx b/app/screens/home/channel_list/categories_list/categories/body/category_body.tsx
index 89d11cdc8e..cd4bd15be2 100644
--- a/app/screens/home/channel_list/categories_list/categories/body/category_body.tsx
+++ b/app/screens/home/channel_list/categories_list/categories/body/category_body.tsx
@@ -7,6 +7,7 @@ import Animated, {Easing, useAnimatedStyle, useSharedValue, withTiming} from 're
import {fetchDirectChannelsInfo} from '@actions/remote/channel';
import ChannelItem from '@components/channel_item';
+import {ROW_HEIGHT as CHANNEL_ROW_HEIGHT} from '@components/channel_item/channel_item';
import {useServerUrl} from '@context/server';
import {isDMorGM} from '@utils/channel';
@@ -16,7 +17,7 @@ import type ChannelModel from '@typings/database/models/servers/channel';
type Props = {
sortedChannels: ChannelModel[];
category: CategoryModel;
- onChannelSwitch: (channelId: string) => void;
+ onChannelSwitch: (channel: Channel | ChannelModel) => void;
unreadIds: Set;
unreadsOnTop: boolean;
};
@@ -46,6 +47,9 @@ const CategoryBody = ({sortedChannels, unreadIds, unreadsOnTop, category, onChan
onPress={onChannelSwitch}
key={item.id}
testID={`channel_list.category.${category.displayName.replace(/ /g, '_').toLocaleLowerCase()}.channel_item`}
+ shouldHighlightActive={true}
+ shouldHighlightState={true}
+ isOnHome={true}
/>
);
}, [onChannelSwitch]);
@@ -62,8 +66,8 @@ const CategoryBody = ({sortedChannels, unreadIds, unreadsOnTop, category, onChan
}
}, [directChannels.length]);
- const height = ids.length ? ids.length * 40 : 0;
- const unreadHeight = unreadChannels.length ? unreadChannels.length * 40 : 0;
+ const height = ids.length ? ids.length * CHANNEL_ROW_HEIGHT : 0;
+ const unreadHeight = unreadChannels.length ? unreadChannels.length * CHANNEL_ROW_HEIGHT : 0;
const animatedStyle = useAnimatedStyle(() => {
const opacity = unreadHeight > 0 ? 1 : 0;
@@ -74,7 +78,7 @@ const CategoryBody = ({sortedChannels, unreadIds, unreadsOnTop, category, onChan
};
}, [height, unreadHeight]);
- const listHeight = useMemo(() => ({
+ const listStyle = useMemo(() => ({
height: category.collapsed ? unreadHeight : height,
}), [category.collapsed, height, unreadHeight]);
@@ -87,7 +91,7 @@ const CategoryBody = ({sortedChannels, unreadIds, unreadsOnTop, category, onChan
// @ts-expect-error strictMode not exposed on the types
strictMode={true}
- style={listHeight}
+ style={listStyle}
/>
);
diff --git a/app/screens/home/channel_list/categories_list/categories/categories.tsx b/app/screens/home/channel_list/categories_list/categories/categories.tsx
index a347b35aee..b6ecbc01ca 100644
--- a/app/screens/home/channel_list/categories_list/categories/categories.tsx
+++ b/app/screens/home/channel_list/categories_list/categories/categories.tsx
@@ -17,6 +17,7 @@ import CategoryHeader from './header';
import UnreadCategories from './unreads';
import type CategoryModel from '@typings/database/models/servers/category';
+import type ChannelModel from '@typings/database/models/servers/channel';
type Props = {
categories: CategoryModel[];
@@ -27,8 +28,6 @@ type Props = {
const styles = StyleSheet.create({
mainList: {
flex: 1,
- marginLeft: -18,
- marginRight: -20,
},
loadingView: {
alignItems: 'center',
@@ -39,7 +38,11 @@ const styles = StyleSheet.create({
const extractKey = (item: CategoryModel | 'UNREADS') => (item === 'UNREADS' ? 'UNREADS' : item.id);
-const Categories = ({categories, onlyUnreads, unreadsOnTop}: Props) => {
+const Categories = ({
+ categories,
+ onlyUnreads,
+ unreadsOnTop,
+}: Props) => {
const intl = useIntl();
const listRef = useRef(null);
const serverUrl = useServerUrl();
@@ -60,8 +63,8 @@ const Categories = ({categories, onlyUnreads, unreadsOnTop}: Props) => {
const [initiaLoad, setInitialLoad] = useState(!categoriesToShow.length);
- const onChannelSwitch = useCallback(async (channelId: string) => {
- switchToChannelById(serverUrl, channelId);
+ const onChannelSwitch = useCallback(async (c: Channel | ChannelModel) => {
+ switchToChannelById(serverUrl, c.id);
}, [serverUrl]);
const renderCategory = useCallback((data: {item: CategoryModel | 'UNREADS'}) => {
diff --git a/app/screens/home/channel_list/categories_list/categories/unreads/unreads.tsx b/app/screens/home/channel_list/categories_list/categories/unreads/unreads.tsx
index 106ef596c8..e55249cdf1 100644
--- a/app/screens/home/channel_list/categories_list/categories/unreads/unreads.tsx
+++ b/app/screens/home/channel_list/categories_list/categories/unreads/unreads.tsx
@@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import React, {useCallback} from 'react';
+import React, {useCallback, useMemo} from 'react';
import {useIntl} from 'react-intl';
import {FlatList, Text} from 'react-native';
import ChannelItem from '@components/channel_item';
+import {HOME_PADDING} from '@constants/view';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
@@ -25,14 +26,14 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
color: changeOpacity(theme.sidebarText, 0.64),
...typography('Heading', 75),
textTransform: 'uppercase',
- paddingLeft: 18,
paddingVertical: 8,
marginTop: 12,
+ ...HOME_PADDING,
},
}));
type UnreadCategoriesProps = {
- onChannelSwitch: (channelId: string) => void;
+ onChannelSwitch: (channel: Channel | ChannelModel) => void;
onlyUnreads: boolean;
unreadChannels: ChannelModel[];
unreadThreads: {unreads: boolean; mentions: number};
@@ -52,11 +53,20 @@ const UnreadCategories = ({onChannelSwitch, onlyUnreads, unreadChannels, unreadT
channel={item}
onPress={onChannelSwitch}
testID='channel_list.category.unreads.channel_item'
+ shouldHighlightActive={true}
+ shouldHighlightState={true}
+ isOnHome={true}
/>
);
}, [onChannelSwitch]);
const showEmptyState = onlyUnreads && !unreadChannels.length;
+ const containerStyle = useMemo(() => {
+ return [
+ showEmptyState && !isTablet && styles.empty,
+ ];
+ }, [styles, showEmptyState, isTablet]);
+
const showTitle = !onlyUnreads || (onlyUnreads && !showEmptyState);
const EmptyState = showEmptyState && !isTablet ? (
@@ -76,7 +86,7 @@ const UnreadCategories = ({onChannelSwitch, onlyUnreads, unreadChannels, unreadT
}
diff --git a/app/screens/home/channel_list/categories_list/header/header.tsx b/app/screens/home/channel_list/categories_list/header/header.tsx
index ebb4fc5aa0..20a1460faa 100644
--- a/app/screens/home/channel_list/categories_list/header/header.tsx
+++ b/app/screens/home/channel_list/categories_list/header/header.tsx
@@ -12,6 +12,7 @@ import CompassIcon from '@components/compass_icon';
import {ITEM_HEIGHT} from '@components/slide_up_panel_item';
import TouchableWithFeedback from '@components/touchable_with_feedback';
import {PUSH_PROXY_STATUS_NOT_AVAILABLE, PUSH_PROXY_STATUS_VERIFIED} from '@constants/push_proxy';
+import {HOME_PADDING} from '@constants/view';
import {useServerDisplayName, useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
@@ -266,7 +267,7 @@ const ChannelListHeader = ({
}
return (
-
+
{header}
);
diff --git a/app/screens/home/channel_list/categories_list/index.tsx b/app/screens/home/channel_list/categories_list/index.tsx
index 7287ce050e..0778c5b564 100644
--- a/app/screens/home/channel_list/categories_list/index.tsx
+++ b/app/screens/home/channel_list/categories_list/index.tsx
@@ -20,8 +20,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
container: {
flex: 1,
backgroundColor: theme.sidebarBg,
- paddingLeft: 18,
- paddingRight: 20,
paddingTop: 10,
},
}));
@@ -68,7 +66,12 @@ const CategoriesList = ({hasChannels, iconPad, isCRTEnabled, moreThanOneTeam}: C
return (
<>
- {isCRTEnabled && }
+ {isCRTEnabled &&
+
+ }
>
);
@@ -76,9 +79,7 @@ const CategoriesList = ({hasChannels, iconPad, isCRTEnabled, moreThanOneTeam}: C
return (
-
+
{content}
);
diff --git a/app/screens/home/channel_list/categories_list/subheader/subheader.tsx b/app/screens/home/channel_list/categories_list/subheader/subheader.tsx
index 46a6d9ffcb..62746a80fa 100644
--- a/app/screens/home/channel_list/categories_list/subheader/subheader.tsx
+++ b/app/screens/home/channel_list/categories_list/subheader/subheader.tsx
@@ -5,6 +5,8 @@ import React, {useEffect} from 'react';
import {StyleSheet, View} from 'react-native';
import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import {HOME_PADDING} from '@constants/view';
+
import SearchField from './search_field';
import UnreadFilter from './unread_filter';
@@ -15,6 +17,7 @@ type Props = {
const style = StyleSheet.create({
container: {
flexDirection: 'row',
+ ...HOME_PADDING,
},
});
diff --git a/app/screens/integration_selector/integration_selector.tsx b/app/screens/integration_selector/integration_selector.tsx
index 0b16b8c9c4..434bf0d892 100644
--- a/app/screens/integration_selector/integration_selector.tsx
+++ b/app/screens/integration_selector/integration_selector.tsx
@@ -118,7 +118,6 @@ export type Props = {
isMultiselect?: boolean;
selected: SelectedDialogValue;
theme: Theme;
- teammateNameDisplay: string;
componentId: AvailableScreens;
}
@@ -165,7 +164,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
function IntegrationSelector(
{dataSource, data, isMultiselect = false, selected, handleSelect,
- currentTeamId, currentUserId, componentId, getDynamicOptions, options, teammateNameDisplay}: Props) {
+ currentTeamId, currentUserId, componentId, getDynamicOptions, options}: Props) {
const serverUrl = useServerUrl();
const theme = useTheme();
const searchTimeoutId = useRef(null);
@@ -554,7 +553,6 @@ function IntegrationSelector(
return (
{
export type EmailInvite = string;
-export type SearchResult = UserProfile|EmailInvite;
+export type SearchResult = UserProfile|UserModel|EmailInvite;
export type InviteResult = {
userId: string;
diff --git a/app/screens/invite/selection.tsx b/app/screens/invite/selection.tsx
index b31bccbd67..7eaa262079 100644
--- a/app/screens/invite/selection.tsx
+++ b/app/screens/invite/selection.tsx
@@ -70,6 +70,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
},
searchListPadding: {
paddingVertical: 8,
+ flex: 1,
},
searchListShadow: {
shadowColor: '#000',
@@ -85,6 +86,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
searchListFlatList: {
backgroundColor: theme.centerChannelBg,
borderRadius: 4,
+ paddingHorizontal: 16,
},
selectedItems: {
display: 'flex',
@@ -222,12 +224,12 @@ export default function Selection({
style.push(styles.searchListFlatList);
- if (searchResults.length) {
+ if (searchResults.length || (term && !loading)) {
style.push(styles.searchListBorder, styles.searchListPadding);
}
return style;
- }, [searchResults, styles]);
+ }, [searchResults, styles, Boolean(term && !loading)]);
const renderNoResults = useCallback(() => {
if (!term || loading) {
@@ -235,20 +237,18 @@ export default function Selection({
}
return (
-
-
-
+
);
}, [term, loading]);
const renderItem = useCallback(({item}: ListRenderItemInfo) => {
const key = keyExtractor(item);
- return (
+ return typeof item === 'string' ? (
- {typeof item === 'string' ? (
-
- ) : (
-
- )}
+
+ ) : (
+
);
}, [searchResults, onSelectItem]);
diff --git a/app/screens/invite/summary_report.tsx b/app/screens/invite/summary_report.tsx
index 874e002678..8819ca6b2b 100644
--- a/app/screens/invite/summary_report.tsx
+++ b/app/screens/invite/summary_report.tsx
@@ -46,11 +46,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
flexDirection: 'column',
paddingVertical: 12,
},
- user: {
- paddingTop: 0,
- paddingBottom: 0,
- height: 'auto',
- },
reason: {
paddingLeft: 56,
paddingRight: 20,
@@ -135,7 +130,6 @@ export default function SummaryReport({
) : (
)}
diff --git a/app/screens/invite/text_item.tsx b/app/screens/invite/text_item.tsx
index 9a14e3ae1c..593d092f2c 100644
--- a/app/screens/invite/text_item.tsx
+++ b/app/screens/invite/text_item.tsx
@@ -13,7 +13,6 @@ import {typography} from '@utils/typography';
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
item: {
- paddingHorizontal: 20,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
@@ -21,7 +20,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
search: {
height: 40,
paddingVertical: 8,
- paddingHorizontal: 16,
},
itemText: {
display: 'flex',
diff --git a/app/screens/manage_channel_members/index.tsx b/app/screens/manage_channel_members/index.tsx
index ae1d739069..d1b7dca7a1 100644
--- a/app/screens/manage_channel_members/index.tsx
+++ b/app/screens/manage_channel_members/index.tsx
@@ -10,7 +10,7 @@ import {observeTutorialWatched} from '@queries/app/global';
import {observeCurrentChannel} from '@queries/servers/channel';
import {observeCanManageChannelMembers, observePermissionForChannel} from '@queries/servers/role';
import {observeCurrentChannelId, observeCurrentTeamId, observeCurrentUserId} from '@queries/servers/system';
-import {observeCurrentUser, observeTeammateNameDisplay} from '@queries/servers/user';
+import {observeCurrentUser} from '@queries/servers/user';
import ManageChannelMembers from './manage_channel_members';
@@ -31,7 +31,6 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
currentUserId: observeCurrentUserId(database),
currentTeamId: observeCurrentTeamId(database),
canManageAndRemoveMembers,
- teammateNameDisplay: observeTeammateNameDisplay(database),
tutorialWatched: observeTutorialWatched(Tutorial.PROFILE_LONG_PRESS),
canChangeMemberRoles,
};
diff --git a/app/screens/manage_channel_members/manage_channel_members.tsx b/app/screens/manage_channel_members/manage_channel_members.tsx
index 920bbf53ff..4cf7c8d22a 100644
--- a/app/screens/manage_channel_members/manage_channel_members.tsx
+++ b/app/screens/manage_channel_members/manage_channel_members.tsx
@@ -30,7 +30,6 @@ type Props = {
componentId: AvailableScreens;
currentTeamId: string;
currentUserId: string;
- teammateNameDisplay: string;
tutorialWatched: boolean;
}
@@ -69,7 +68,6 @@ export default function ManageChannelMembers({
componentId,
currentTeamId,
currentUserId,
- teammateNameDisplay,
tutorialWatched,
}: Props) {
const serverUrl = useServerUrl();
@@ -271,7 +269,6 @@ export default function ManageChannelMembers({
selectedIds={EMPTY_IDS}
showManageMode={canManageAndRemoveMembers && isManageMode}
showNoResults={!loading}
- teammateNameDisplay={teammateNameDisplay}
term={term}
testID='manage_members.user_list'
tutorialWatched={tutorialWatched}
diff --git a/app/screens/reactions/reactors_list/reactor/reactor.tsx b/app/screens/reactions/reactors_list/reactor/reactor.tsx
index 61b2582bc6..7b1f733ac3 100644
--- a/app/screens/reactions/reactors_list/reactor/reactor.tsx
+++ b/app/screens/reactions/reactors_list/reactor/reactor.tsx
@@ -3,7 +3,7 @@
import React from 'react';
import {useIntl} from 'react-intl';
-import {Keyboard, StyleSheet, TouchableOpacity} from 'react-native';
+import {Keyboard} from 'react-native';
import UserItem from '@components/user_item';
import {Screens} from '@constants';
@@ -15,20 +15,9 @@ import type UserModel from '@typings/database/models/servers/user';
type Props = {
channelId: string;
location: string;
- user?: UserModel;
+ user: UserModel;
}
-const style = StyleSheet.create({
- container: {
- marginBottom: 8,
- paddingLeft: 0,
- flexDirection: 'row',
- },
- picture: {
- marginLeft: 0,
- },
-});
-
const Reactor = ({channelId, location, user}: Props) => {
const intl = useIntl();
const theme = useTheme();
@@ -46,14 +35,11 @@ const Reactor = ({channelId, location, user}: Props) => {
};
return (
-
-
-
+
);
};
diff --git a/app/screens/user_profile/custom_status.tsx b/app/screens/user_profile/custom_status.tsx
index 5f6f82a33a..51e420e037 100644
--- a/app/screens/user_profile/custom_status.tsx
+++ b/app/screens/user_profile/custom_status.tsx
@@ -74,7 +74,6 @@ const UserProfileCustomStatus = ({customStatus}: Props) => {
}
diff --git a/app/utils/strings.ts b/app/utils/strings.ts
new file mode 100644
index 0000000000..610ba37718
--- /dev/null
+++ b/app/utils/strings.ts
@@ -0,0 +1,6 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+export function nonBreakingString(s: string) {
+ return s.replace(' ', '\xa0');
+}
diff --git a/types/components/emoji.ts b/types/components/emoji.ts
index 8daf4074f7..77cc747495 100644
--- a/types/components/emoji.ts
+++ b/types/components/emoji.ts
@@ -5,13 +5,18 @@ import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji
import type {StyleProp, TextStyle} from 'react-native';
import type {ImageStyle} from 'react-native-fast-image';
+// The intersection of the image styles and text styles
+type ImageStyleUniques = Omit
+export type EmojiCommonStyle = Omit
+
export type EmojiProps = {
emojiName: string;
displayTextOnly?: boolean;
literal?: string;
size?: number;
textStyle?: StyleProp;
- customEmojiStyle?: StyleProp;
+ imageStyle?: StyleProp;
+ commonStyle?: StyleProp;
customEmojis: CustomEmojiModel[];
testID?: string;
}