From 5dd3121bbc173f4f934b7ac20174119d251f0449 Mon Sep 17 00:00:00 2001 From: Anurag Shivarathri Date: Fri, 25 Nov 2022 18:51:04 +0530 Subject: [PATCH] [Gekidou MM-43527] Add threads item to the channel switcher (#6657) * Threads item in channel switcher * Misc * Updated test & renamed type * Reverted unwanted changes * Changed thread text to i18n * feedback fix * Merge conflict steps * Merge fix * test fix * Added onPress to the dependencies * Moved the term matching logic to the useMemo block * Misc Co-authored-by: Mattermod --- .../threads_button.test.tsx.snap | 101 +++++++++++++++++- .../threads_button/index.ts | 0 .../threads_button/threads_button.test.tsx | 11 ++ .../threads_button/threads_button.tsx | 42 +++++--- .../filtered_list/filtered_list.tsx | 47 +++++++- .../find_channels/filtered_list/index.ts | 2 + .../channel_list/categories_list/index.tsx | 2 +- 7 files changed, 182 insertions(+), 23 deletions(-) rename app/{screens/home/channel_list/categories_list => components}/threads_button/__snapshots__/threads_button.test.tsx.snap (63%) rename app/{screens/home/channel_list/categories_list => components}/threads_button/index.ts (100%) rename app/{screens/home/channel_list/categories_list => components}/threads_button/threads_button.test.tsx (82%) rename app/{screens/home/channel_list/categories_list => components}/threads_button/threads_button.tsx (75%) diff --git a/app/screens/home/channel_list/categories_list/threads_button/__snapshots__/threads_button.test.tsx.snap b/app/components/threads_button/__snapshots__/threads_button.test.tsx.snap similarity index 63% rename from app/screens/home/channel_list/categories_list/threads_button/__snapshots__/threads_button.test.tsx.snap rename to app/components/threads_button/__snapshots__/threads_button.test.tsx.snap index c178525daa..c7b564c6f8 100644 --- a/app/screens/home/channel_list/categories_list/threads_button/__snapshots__/threads_button.test.tsx.snap +++ b/app/components/threads_button/__snapshots__/threads_button.test.tsx.snap @@ -36,7 +36,7 @@ exports[`Thread item in the channel list Threads Component should match snapshot "minHeight": 40, "paddingHorizontal": 20, }, - undefined, + false, ] } > @@ -48,6 +48,7 @@ exports[`Thread item in the channel list Threads Component should match snapshot "color": "rgba(255,255,255,0.5)", "fontSize": 24, }, + false, undefined, ] } @@ -70,8 +71,98 @@ exports[`Thread item in the channel list Threads Component should match snapshot "paddingLeft": 12, "paddingRight": 20, }, + false, + false, undefined, - undefined, + ] + } + > + Threads + + + + +`; + +exports[`Thread item in the channel list Threads Component should match snapshot with isInfo 1`] = ` + + + + + @@ -120,7 +211,7 @@ exports[`Thread item in the channel list Threads Component should match snapshot "minHeight": 40, "paddingHorizontal": 20, }, - undefined, + false, ] } > @@ -132,6 +223,7 @@ exports[`Thread item in the channel list Threads Component should match snapshot "color": "rgba(255,255,255,0.5)", "fontSize": 24, }, + false, undefined, ] } @@ -154,7 +246,8 @@ exports[`Thread item in the channel list Threads Component should match snapshot "paddingLeft": 12, "paddingRight": 20, }, - undefined, + false, + false, undefined, ] } diff --git a/app/screens/home/channel_list/categories_list/threads_button/index.ts b/app/components/threads_button/index.ts similarity index 100% rename from app/screens/home/channel_list/categories_list/threads_button/index.ts rename to app/components/threads_button/index.ts diff --git a/app/screens/home/channel_list/categories_list/threads_button/threads_button.test.tsx b/app/components/threads_button/threads_button.test.tsx similarity index 82% rename from app/screens/home/channel_list/categories_list/threads_button/threads_button.test.tsx rename to app/components/threads_button/threads_button.test.tsx index c89b80180c..cbc90ecd2e 100644 --- a/app/screens/home/channel_list/categories_list/threads_button/threads_button.test.tsx +++ b/app/components/threads_button/threads_button.test.tsx @@ -36,6 +36,17 @@ describe('Thread item in the channel list', () => { expect(toJSON()).toMatchSnapshot(); }); + test('Threads Component should match snapshot with isInfo', () => { + const {toJSON} = renderWithIntlAndTheme( + , + ); + + expect(toJSON()).toMatchSnapshot(); + }); + test('Threads Component should match snapshot, groupUnreadsSeparately false, always show', () => { const {toJSON} = renderWithIntlAndTheme( ({ iconActive: { color: theme.sidebarText, }, + iconInfo: { + color: changeOpacity(theme.centerChannelColor, 0.72), + }, text: { flex: 1, }, @@ -37,15 +40,17 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ type Props = { currentChannelId: string; - onlyUnreads: boolean; groupUnreadsSeparately: boolean; + isInfo?: boolean; + onlyUnreads: boolean; + onPress?: () => void; unreadsAndMentions: { unreads: boolean; mentions: number; }; }; -const ThreadsButton = ({currentChannelId, groupUnreadsSeparately, onlyUnreads, unreadsAndMentions}: Props) => { +const ThreadsButton = ({currentChannelId, groupUnreadsSeparately, isInfo, onlyUnreads, onPress, unreadsAndMentions}: Props) => { const isTablet = useIsTablet(); const serverUrl = useServerUrl(); @@ -54,33 +59,44 @@ const ThreadsButton = ({currentChannelId, groupUnreadsSeparately, onlyUnreads, u const customStyles = getStyleSheet(theme); const handlePress = useCallback(preventDoubleTap(() => { - switchToGlobalThreads(serverUrl); - }), [serverUrl]); + if (onPress) { + onPress(); + } else { + switchToGlobalThreads(serverUrl); + } + }), [onPress, serverUrl]); const {unreads, mentions} = unreadsAndMentions; const isActive = isTablet && !currentChannelId; - const [containerStyle, iconStyle, textStyle] = useMemo(() => { + const [containerStyle, iconStyle, textStyle, badgeStyle] = useMemo(() => { const container = [ styles.container, - isActive ? styles.activeItem : undefined, + isActive && styles.activeItem, ]; const icon = [ customStyles.icon, - isActive || unreads ? customStyles.iconActive : undefined, + (isActive || unreads) && customStyles.iconActive, + isInfo && customStyles.iconInfo, ]; const text = [ customStyles.text, unreads ? channelItemTextStyle.bold : channelItemTextStyle.regular, styles.text, - unreads ? styles.highlight : undefined, - isActive ? styles.textActive : undefined, + unreads && styles.highlight, + isActive && styles.textActive, + isInfo && styles.textInfo, ]; - return [container, icon, text]; - }, [customStyles, isActive, styles, unreads]); + const badge = [ + styles.badge, + isInfo && styles.infoBadge, + ]; + + return [container, icon, text, badge]; + }, [customStyles, isActive, isInfo, styles, unreads]); if (groupUnreadsSeparately && (onlyUnreads && !isActive && !unreads && !mentions)) { return null; @@ -104,7 +120,7 @@ const ThreadsButton = ({currentChannelId, groupUnreadsSeparately, onlyUnreads, u /> 0} /> @@ -113,4 +129,4 @@ const ThreadsButton = ({currentChannelId, groupUnreadsSeparately, onlyUnreads, u ); }; -export default ThreadsButton; +export default React.memo(ThreadsButton); diff --git a/app/screens/find_channels/filtered_list/filtered_list.tsx b/app/screens/find_channels/filtered_list/filtered_list.tsx index 5d405eb81b..fe89218939 100644 --- a/app/screens/find_channels/filtered_list/filtered_list.tsx +++ b/app/screens/find_channels/filtered_list/filtered_list.tsx @@ -7,11 +7,13 @@ import {useIntl} from 'react-intl'; import {Alert, FlatList, ListRenderItemInfo, StyleSheet, View} from 'react-native'; import Animated, {FadeInDown, FadeOutUp} from 'react-native-reanimated'; +import {switchToGlobalThreads} from '@actions/local/thread'; import {joinChannelIfNeeded, makeDirectChannel, searchAllChannels, switchToChannelById} from '@actions/remote/channel'; import {searchProfiles} from '@actions/remote/user'; 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 {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; import {sortChannelsByDisplayName} from '@utils/channel'; @@ -23,6 +25,8 @@ import UserItem from './user_item'; import type ChannelModel from '@typings/database/models/servers/channel'; import type UserModel from '@typings/database/models/servers/user'; +type ResultItem = ChannelModel|Channel|UserModel|'thread'; + type RemoteChannels = { archived: Channel[]; startWith: Channel[]; @@ -35,6 +39,7 @@ type Props = { channelsMatch: ChannelModel[]; channelsMatchStart: ChannelModel[]; currentTeamId: string; + isCRTEnabled: boolean; keyboardHeight: number; loading: boolean; onLoading: (loading: boolean) => void; @@ -77,7 +82,7 @@ const sortByUserOrChannel = (locale: string, teamm const FilteredList = ({ archivedChannels, close, channelsMatch, channelsMatchStart, currentTeamId, - keyboardHeight, loading, onLoading, restrictDirectMessage, showTeamName, + isCRTEnabled, keyboardHeight, loading, onLoading, restrictDirectMessage, showTeamName, teamIds, teammateDisplayNameSetting, term, usersMatch, usersMatchStart, testID, }: Props) => { const bounce = useRef void>>(); @@ -87,6 +92,7 @@ const FilteredList = ({ const {locale, formatMessage} = useIntl(); const flatListStyle = useMemo(() => ({flexGrow: 1, paddingBottom: keyboardHeight}), [keyboardHeight]); const [remoteChannels, setRemoteChannels] = useState({archived: [], startWith: [], matches: []}); + const totalLocalResults = channelsMatchStart.length + channelsMatch.length + usersMatchStart.length; const search = async () => { @@ -182,6 +188,11 @@ const FilteredList = ({ switchToChannelById(serverUrl, channelId); }, [serverUrl, close]); + const onSwitchToThreads = useCallback(async () => { + await close(); + switchToGlobalThreads(serverUrl); + }, [serverUrl, close]); + const renderEmpty = useCallback(() => { if (loading) { return ( @@ -204,7 +215,15 @@ const FilteredList = ({ return null; }, [term, loading, theme]); - const renderItem = useCallback(({item}: ListRenderItemInfo) => { + const renderItem = useCallback(({item}: ListRenderItemInfo) => { + if (item === 'thread') { + return ( + + ); + } if ('teamId' in item) { return ( formatMessage({ + id: 'threads', + defaultMessage: 'Threads', + }).toLowerCase(), + [locale], + ); + const data = useMemo(() => { - const items: Array = [...channelsMatchStart]; + const items: ResultItem[] = []; + + // Add threads item to show it on the top of the list + if (isCRTEnabled) { + const isThreadTerm = threadLabel.indexOf(term.toLowerCase()) === 0; + if (isThreadTerm) { + items.push('thread'); + } + } + + items.push(...channelsMatchStart); // Channels that matches if (items.length < MAX_RESULTS) { @@ -275,14 +312,14 @@ const FilteredList = ({ } return [...new Set(items)].slice(0, MAX_RESULTS + 1); - }, [archivedChannels, channelsMatchStart, channelsMatch, remoteChannels, usersMatch, usersMatchStart, locale, teammateDisplayNameSetting]); + }, [archivedChannels, channelsMatchStart, channelsMatch, isCRTEnabled, remoteChannels, usersMatch, usersMatchStart, locale, teammateDisplayNameSetting, term, threadLabel]); useEffect(() => { mounted.current = true; return () => { mounted.current = false; }; - }); + }, []); useEffect(() => { bounce.current = debounce(search, 500); diff --git a/app/screens/find_channels/filtered_list/index.ts b/app/screens/find_channels/filtered_list/index.ts index c12c9d15c4..ce6d5c060f 100644 --- a/app/screens/find_channels/filtered_list/index.ts +++ b/app/screens/find_channels/filtered_list/index.ts @@ -10,6 +10,7 @@ import {General} from '@constants'; import {observeArchiveChannelsByTerm, observeDirectChannelsByTerm, observeJoinedChannelsByTerm, observeNotDirectChannelsByTerm} from '@queries/servers/channel'; import {observeConfigValue, observeCurrentTeamId} from '@queries/servers/system'; import {queryJoinedTeams} from '@queries/servers/team'; +import {observeIsCRTEnabled} from '@queries/servers/thread'; import {observeTeammateNameDisplay} from '@queries/servers/user'; import {retrieveChannels} from '@screens/find_channels/utils'; @@ -61,6 +62,7 @@ const enhanced = withObservables(['term'], ({database, term}: EnhanceProps) => { channelsMatch, channelsMatchStart, currentTeamId: observeCurrentTeamId(database), + isCRTEnabled: observeIsCRTEnabled(database), restrictDirectMessage, showTeamName: teamIds.pipe(switchMap((ids) => of$(ids.size > 1))), teamIds, diff --git a/app/screens/home/channel_list/categories_list/index.tsx b/app/screens/home/channel_list/categories_list/index.tsx index 5245ac3d94..409c31f99e 100644 --- a/app/screens/home/channel_list/categories_list/index.tsx +++ b/app/screens/home/channel_list/categories_list/index.tsx @@ -4,6 +4,7 @@ import React, {useEffect} from 'react'; import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; +import ThreadsButton from '@components/threads_button'; import {TABLET_SIDEBAR_WIDTH, TEAM_SIDEBAR_WIDTH} from '@constants/view'; import {useTheme} from '@context/theme'; import {makeStyleSheetFromTheme} from '@utils/theme'; @@ -12,7 +13,6 @@ import Categories from './categories'; import ChannelListHeader from './header'; import LoadChannelsError from './load_channels_error'; import SubHeader from './subheader'; -import ThreadsButton from './threads_button'; const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ container: {