From 88f35d607a22d71d6b445710709e3c5406297c14 Mon Sep 17 00:00:00 2001 From: Julien Tant Date: Tue, 10 Dec 2024 16:30:00 -0700 Subject: [PATCH 01/15] Add new team picker for search --- app/components/team_list/index.tsx | 22 +++- .../team_list_item/team_list_item.tsx | 27 ++--- .../home/search/bottom_sheet_team_list.tsx | 12 ++- app/screens/home/search/index.tsx | 2 + .../home/search/initial/modifiers/index.tsx | 101 +++++++++--------- app/screens/home/search/results/header.tsx | 6 +- .../{team_picker_icon.tsx => team_picker.tsx} | 52 ++++----- assets/base/i18n/en.json | 1 + ios/Gekidou/Package.resolved | 85 ++++++++++++++- 9 files changed, 204 insertions(+), 104 deletions(-) rename app/screens/home/search/{team_picker_icon.tsx => team_picker.tsx} (64%) diff --git a/app/components/team_list/index.tsx b/app/components/team_list/index.tsx index b1e5e412468..165753319d7 100644 --- a/app/components/team_list/index.tsx +++ b/app/components/team_list/index.tsx @@ -23,6 +23,8 @@ type Props = { testID?: string; textColor?: string; type?: BottomSheetList; + hideIcon?: boolean; + separatorAfterFirstItem?: boolean; } const styles = StyleSheet.create({ @@ -32,6 +34,10 @@ const styles = StyleSheet.create({ contentContainer: { marginBottom: 4, }, + separator: { + height: 1, + backgroundColor: 'rgba(0, 0, 0, 0.1)', + }, }); const keyExtractor = (item: TeamModel) => item.id; @@ -47,11 +53,13 @@ export default function TeamList({ testID, textColor, type = 'FlatList', + hideIcon = false, + separatorAfterFirstItem = false, }: Props) { const List = useMemo(() => (type === 'FlatList' ? FlatList : BottomSheetFlatList), [type]); - const renderTeam = useCallback(({item: t}: ListRenderItemInfo) => { - return ( + const renderTeam = useCallback(({item: t, index: i}: ListRenderItemInfo) => { + let teamListItem = ( ); - }, [textColor, iconTextColor, iconBackgroundColor, onPress, selectedTeamId]); + if (separatorAfterFirstItem && i === 0) { + teamListItem = (<> + {teamListItem} + + ); + } + return teamListItem; + }, [textColor, iconTextColor, iconBackgroundColor, onPress, selectedTeamId, hideIcon, separatorAfterFirstItem]); let footer; if (loading) { diff --git a/app/components/team_list/team_list_item/team_list_item.tsx b/app/components/team_list/team_list_item/team_list_item.tsx index a903e25beb1..eb012c743b2 100644 --- a/app/components/team_list/team_list_item/team_list_item.tsx +++ b/app/components/team_list/team_list_item/team_list_item.tsx @@ -20,6 +20,7 @@ type Props = { iconBackgroundColor?: string; selectedTeamId?: string; onPress: (teamId: string) => void; + hideIcon?: boolean; } export const ITEM_HEIGHT = 56; @@ -50,7 +51,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { }; }); -export default function TeamListItem({team, textColor, iconTextColor, iconBackgroundColor, selectedTeamId, onPress}: Props) { +export default function TeamListItem({team, textColor, iconTextColor, iconBackgroundColor, selectedTeamId, onPress, hideIcon}: Props) { const theme = useTheme(); const styles = getStyleSheet(theme); @@ -68,17 +69,19 @@ export default function TeamListItem({team, textColor, iconTextColor, iconBackgr type='opacity' style={styles.touchable} > - - - + {!hideIcon && + + + + } ); diff --git a/app/screens/home/search/index.tsx b/app/screens/home/search/index.tsx index 7240fd2b7b0..c9ccd187121 100644 --- a/app/screens/home/search/index.tsx +++ b/app/screens/home/search/index.tsx @@ -19,6 +19,8 @@ const enhance = withObservables([], ({database}: WithDatabaseArgs) => { }; }); +export const ALL_TEAMS_ID = ''; + export default compose( withDatabase, enhance, diff --git a/app/screens/home/search/initial/modifiers/index.tsx b/app/screens/home/search/initial/modifiers/index.tsx index f2028d9a571..7ffbeb424c5 100644 --- a/app/screens/home/search/initial/modifiers/index.tsx +++ b/app/screens/home/search/initial/modifiers/index.tsx @@ -8,10 +8,12 @@ import Animated, {useSharedValue, useAnimatedStyle, withTiming} from 'react-nati import FormattedText from '@components/formatted_text'; import {useTheme} from '@context/theme'; -import TeamPickerIcon from '@screens/home/search/team_picker_icon'; +import TeamPicker from '@screens/home/search/team_picker'; import {makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; +import {ALL_TEAMS_ID} from '../..'; + import Modifier, {type ModifierItem} from './modifier'; import ShowMoreButton from './show_more'; @@ -19,65 +21,62 @@ import type {SearchRef} from '@components/search'; import type TeamModel from '@typings/database/models/servers/team'; const MODIFIER_LABEL_HEIGHT = 48; -const TEAM_PICKER_ICON_SIZE = 32; const NUM_ITEMS_BEFORE_EXPAND = 4; const getStyleFromTheme = makeStyleSheetFromTheme((theme) => { return { - titleContainer: { + header: { alignItems: 'center', flexDirection: 'row', marginTop: 20, marginRight: 18, }, - title: { + titleContainer: { flex: 1, - alignItems: 'center', paddingLeft: 18, + }, + title: { + alignItems: 'center', color: theme.centerChannelColor, ...typography('Heading', 300, 'SemiBold'), }, + teamPickerContainer: { + flex: 1, + alignItems: 'flex-end', + }, }; }); -const getModifiersSectionsData = (intl: IntlShape): ModifierItem[] => { +const getModifiersSectionsData = (intl: IntlShape, teamId: string): ModifierItem[] => { const formatMessage = intl.formatMessage; - const sectionsData = [ - { + + const sectionsData = []; + if (teamId !== ALL_TEAMS_ID) { + sectionsData.push({ term: 'From:', testID: 'search.modifier.from', description: formatMessage({id: 'mobile.search.modifier.from', defaultMessage: ' a specific user'}), - }, { + }); + + sectionsData.push({ term: 'In:', testID: 'search.modifier.in', description: formatMessage({id: 'mobile.search.modifier.in', defaultMessage: ' a specific channel'}), - }, + }); + } + + sectionsData.push({ + term: '-', + testID: 'search.modifier.exclude', + description: formatMessage({id: 'mobile.search.modifier.exclude', defaultMessage: ' exclude search terms'}), + }); + sectionsData.push({ + term: '""', + testID: 'search.modifier.phrases', + description: formatMessage({id: 'mobile.search.modifier.phrases', defaultMessage: ' messages with phrases'}), + cursorPosition: -1, + }); - // { - // term: 'On:', - // testID: 'search.modifier.on', - // description: formatMessage({id: 'mobile.search.modifier.on', defaultMessage: ' a specific date'}), - // }, - // { - // term: 'After:', - // testID: 'search.modifier.after', - // description: formatMessage({id: 'mobile.search.modifier.after', defaultMessage: ' after a date'}), - // }, { - // term: 'Before:', - // testID: 'search.modifier.before', - // description: formatMessage({id: 'mobile.search.modifier.before', defaultMessage: ' before a date'}), - // }, - { - term: '-', - testID: 'search.modifier.exclude', - description: formatMessage({id: 'mobile.search.modifier.exclude', defaultMessage: ' exclude search terms'}), - }, { - term: '""', - testID: 'search.modifier.phrases', - description: formatMessage({id: 'mobile.search.modifier.phrases', defaultMessage: ' messages with phrases'}), - cursorPosition: -1, - }, - ]; return sectionsData; }; @@ -96,7 +95,7 @@ const Modifiers = ({scrollEnabled, searchValue, setSearchValue, searchRef, setTe const [showMore, setShowMore] = useState(false); const height = useSharedValue(NUM_ITEMS_BEFORE_EXPAND * MODIFIER_LABEL_HEIGHT); - const data = useMemo(() => getModifiersSectionsData(intl), [intl]); + const data = useMemo(() => getModifiersSectionsData(intl, teamId), [intl, teamId]); const timeoutRef = useRef(); const styles = getStyleFromTheme(theme); @@ -143,20 +142,24 @@ const Modifiers = ({scrollEnabled, searchValue, setSearchValue, searchRef, setTe return ( <> - - + + + + + {teams.length > 1 && - + + + } diff --git a/app/screens/home/search/results/header.tsx b/app/screens/home/search/results/header.tsx index e989ecd38a5..83530cbd5cb 100644 --- a/app/screens/home/search/results/header.tsx +++ b/app/screens/home/search/results/header.tsx @@ -10,7 +10,7 @@ import Filter, {DIVIDERS_HEIGHT, FILTER_ITEM_HEIGHT, NUMBER_FILTER_ITEMS} from ' import {useTheme} from '@context/theme'; import {useIsTablet} from '@hooks/device'; import {TITLE_SEPARATOR_MARGIN, TITLE_SEPARATOR_MARGIN_TABLET, TITLE_HEIGHT} from '@screens/bottom_sheet/content'; -import TeamPickerIcon from '@screens/home/search/team_picker_icon'; +import TeamPicker from '@screens/home/search/team_picker'; import {bottomSheet} from '@screens/navigation'; import {type FileFilter, FileFilters} from '@utils/file'; import {bottomSheetSnapPoint} from '@utils/helpers'; @@ -151,9 +151,7 @@ const Header = ({ )} {teams.length > 1 && ( - { return { teamContainer: { - paddingLeft: 8, flexDirection: 'row', alignItems: 'center', + width: '100%', }, - border: { - marginLeft: 12, - borderLeftWidth: 1, - borderLeftColor: changeOpacity(theme.centerChannelColor, 0.16), - }, - teamIcon: { - flexDirection: 'row', - }, - compass: { - alignItems: 'center', - marginLeft: 0, + teamName: { + color: theme.centerChannelColor, + fontSize: 12, }, }; }); type Props = { - size?: number; - divider?: boolean; teams: TeamModel[]; setTeamId: (id: string) => void; teamId: string; } -const TeamPickerIcon = ({size = 24, divider = false, setTeamId, teams, teamId}: Props) => { +const TeamPicker = ({setTeamId, teams, teamId}: Props) => { const intl = useIntl(); const theme = useTheme(); const styles = getStyleFromTheme(theme); - const selectedTeam = teams.find((t) => t.id === teamId); + let selectedTeam = teams.find((t) => t.id === teamId); + if (teamId === ALL_TEAMS_ID) { + selectedTeam = {id: ALL_TEAMS_ID, displayName: intl.formatMessage({id: 'mobile.search.team.all_teams', defaultMessage: 'All teams'})} as TeamModel; + } const title = intl.formatMessage({id: 'mobile.search.team.select', defaultMessage: 'Select a team to search'}); @@ -99,22 +93,14 @@ const TeamPickerIcon = ({size = 24, divider = false, setTeamId, teams, teamId}: type='opacity' testID='team_picker.button' > - - - - + + {selectedTeam.displayName} @@ -124,4 +110,4 @@ const TeamPickerIcon = ({size = 24, divider = false, setTeamId, teams, teamId}: ); }; -export default TeamPickerIcon; +export default TeamPicker; diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 76f2e399ed9..0ec805a5af1 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -774,6 +774,7 @@ "mobile.search.results": "{count} search {count, plural, one {result} other {results}}", "mobile.search.show_less": "Show less", "mobile.search.show_more": "Show more", + "mobile.search.team.all_teams": "All teams", "mobile.search.team.select": "Select a team to search", "mobile.server_identifier.exists": "You are already connected to this server.", "mobile.server_link.error.text": "The link could not be found on this server.", diff --git a/ios/Gekidou/Package.resolved b/ios/Gekidou/Package.resolved index 2877a162dae..d5f7fdb972d 100644 --- a/ios/Gekidou/Package.resolved +++ b/ios/Gekidou/Package.resolved @@ -1,13 +1,94 @@ { "object": { "pins": [ + { + "package": "Cryptor", + "repositoryURL": "https://github.com/Kitura/BlueCryptor.git", + "state": { + "branch": null, + "revision": "cec97c24b111351e70e448972a7d3fe68a756d6d", + "version": "2.0.2" + } + }, + { + "package": "CryptorECC", + "repositoryURL": "https://github.com/Kitura/BlueECC.git", + "state": { + "branch": null, + "revision": "1485268a54f8135435a825a855e733f026fa6cc8", + "version": "1.2.201" + } + }, + { + "package": "CryptorRSA", + "repositoryURL": "https://github.com/Kitura/BlueRSA.git", + "state": { + "branch": null, + "revision": "4c9464b4a21dd558a9b1f4a4ed603bb67dcbc773", + "version": "1.0.202" + } + }, + { + "package": "KituraContracts", + "repositoryURL": "https://github.com/Kitura/KituraContracts.git", + "state": { + "branch": null, + "revision": "8a4778c3aa7833e9e1af884e8819d436c237cd70", + "version": "1.2.201" + } + }, + { + "package": "LoggerAPI", + "repositoryURL": "https://github.com/Kitura/LoggerAPI.git", + "state": { + "branch": null, + "revision": "e82d34eab3f0b05391082b11ea07d3b70d2f65bb", + "version": "1.9.200" + } + }, + { + "package": "OpenGraph", + "repositoryURL": "https://github.com/satoshi-takano/OpenGraph.git", + "state": { + "branch": null, + "revision": "382972f1963580eeabafd88ad012e66576b4d213", + "version": "1.4.1" + } + }, + { + "package": "Sentry", + "repositoryURL": "https://github.com/getsentry/sentry-cocoa.git", + "state": { + "branch": null, + "revision": "5575af93efb776414f243e93d6af9f6258dc539a", + "version": "8.36.0" + } + }, { "package": "SQLite.swift", "repositoryURL": "https://github.com/stephencelis/SQLite.swift.git", "state": { "branch": null, - "revision": "9af51e2edf491c0ea632e369a6566e09b65aa333", - "version": "0.13.0" + "revision": "a95fc6df17d108bd99210db5e8a9bac90fe984b8", + "version": "0.15.3" + } + }, + { + "package": "SwiftJWT", + "repositoryURL": "https://github.com/Kitura/Swift-JWT.git", + "state": { + "branch": null, + "revision": "47c6384b6923e9bb1f214d2ba4bd52af39440588", + "version": "3.6.201" + } + }, + { + "package": "swift-log", + "repositoryURL": "https://github.com/apple/swift-log.git", + "state": { + "branch": null, + "revision": "9cb486020ebf03bfa5b5df985387a14a98744537", + "version": "1.6.1" } } ] From 76a710cd60c9c168e6539195d9254800fd97b596 Mon Sep 17 00:00:00 2001 From: Julien Tant Date: Wed, 11 Dec 2024 10:17:20 -0700 Subject: [PATCH 02/15] try fix result header --- app/screens/home/search/results/header.tsx | 53 +++++++++++++--------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/app/screens/home/search/results/header.tsx b/app/screens/home/search/results/header.tsx index 83530cbd5cb..bb7eb2a5e5e 100644 --- a/app/screens/home/search/results/header.tsx +++ b/app/screens/home/search/results/header.tsx @@ -35,7 +35,6 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => { return { container: { marginTop: 10, - backgroundColor: theme.centerChannelBg, borderBottomWidth: 1, borderBottomColor: changeOpacity(theme.centerChannelColor, 0.1), }, @@ -44,15 +43,21 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => { borderColor: theme.centerChannelBg, marginTop: 2, }, - buttonsContainer: { + header: { marginBottom: 12, paddingHorizontal: 12, flexDirection: 'row', + justifyContent: 'space-between', }, - iconsContainer: { - alignItems: 'center', + buttonContainer: { flexDirection: 'row', - marginLeft: 'auto', + backgroundColor: 'red', + }, + filtersContainer: { + flexDirection: 'row', + justifyContent: 'flex-end', + backgroundColor: 'blue', + alignItems: 'center', }, }; }); @@ -119,18 +124,20 @@ const Header = ({ return ( - - - - + + + + + + {showFilterIcon && ( )} {teams.length > 1 && ( - + + + )} From 19612511acf858644850a95631c84ad61d1d612e Mon Sep 17 00:00:00 2001 From: Julien Tant Date: Thu, 12 Dec 2024 11:00:39 -0700 Subject: [PATCH 03/15] fix style --- .../home/search/initial/modifiers/index.tsx | 3 +- app/screens/home/search/results/header.tsx | 67 ++++++++++--------- app/screens/home/search/team_picker.tsx | 7 +- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/app/screens/home/search/initial/modifiers/index.tsx b/app/screens/home/search/initial/modifiers/index.tsx index 7ffbeb424c5..e3709210e06 100644 --- a/app/screens/home/search/initial/modifiers/index.tsx +++ b/app/screens/home/search/initial/modifiers/index.tsx @@ -29,11 +29,10 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => { alignItems: 'center', flexDirection: 'row', marginTop: 20, - marginRight: 18, + marginHorizontal: 18, }, titleContainer: { flex: 1, - paddingLeft: 18, }, title: { alignItems: 'center', diff --git a/app/screens/home/search/results/header.tsx b/app/screens/home/search/results/header.tsx index bb7eb2a5e5e..421cae77db4 100644 --- a/app/screens/home/search/results/header.tsx +++ b/app/screens/home/search/results/header.tsx @@ -51,13 +51,18 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => { }, buttonContainer: { flexDirection: 'row', - backgroundColor: 'red', }, - filtersContainer: { + teamPickerContainer: { + flex: 1, flexDirection: 'row', justifyContent: 'flex-end', - backgroundColor: 'blue', + }, + filterContainer: { + flex: 1, + flexDirection: 'row', alignItems: 'center', + justifyContent: 'center', + maxWidth: 32, }, }; }); @@ -118,9 +123,7 @@ const Header = ({ theme, title, }); - }, [onFilterChanged, selectedFilter]); - - const filterStyle = useMemo(() => ({marginRight: teams.length > 1 ? 0 : 8}), [teams.length > 1]); + }, [onFilterChanged, selectedFilter, snapPoints, title, theme]); return ( @@ -137,34 +140,32 @@ const Header = ({ text={filesText} /> - - {showFilterIcon && ( - - - - - )} + {showFilterIcon && ( + + + + + )} + {teams.length > 1 && ( - - - + )} diff --git a/app/screens/home/search/team_picker.tsx b/app/screens/home/search/team_picker.tsx index 72558b197db..0f3e0fb7189 100644 --- a/app/screens/home/search/team_picker.tsx +++ b/app/screens/home/search/team_picker.tsx @@ -29,11 +29,13 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => { teamContainer: { flexDirection: 'row', alignItems: 'center', + justifyContent: 'flex-end', width: '100%', }, teamName: { color: theme.centerChannelColor, fontSize: 12, + textAlign: 'right', }, }; }); @@ -92,13 +94,16 @@ const TeamPicker = ({setTeamId, teams, teamId}: Props) => { onPress={handleTeamChange} type='opacity' testID='team_picker.button' + style={styles.teamContainer} > - + {selectedTeam.displayName} + + Date: Thu, 12 Dec 2024 11:43:00 -0700 Subject: [PATCH 04/15] add test for team picker --- app/screens/home/search/team_picker.test.tsx | 61 ++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 app/screens/home/search/team_picker.test.tsx diff --git a/app/screens/home/search/team_picker.test.tsx b/app/screens/home/search/team_picker.test.tsx new file mode 100644 index 00000000000..22c5e95ac1f --- /dev/null +++ b/app/screens/home/search/team_picker.test.tsx @@ -0,0 +1,61 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import {bottomSheet} from '@screens/navigation'; +import {fireEvent, renderWithIntlAndTheme} from '@test/intl-test-helper'; + +import TeamPicker from './team_picker'; + +import {ALL_TEAMS_ID} from '.'; + +import type {TeamModel} from '@database/models/server'; + +jest.mock('@screens/navigation', () => ({ + bottomSheet: jest.fn(), +})); + +// Some subcomponents require react-native-camera-roll, which is not available in the test environment +jest.mock('@react-native-camera-roll/camera-roll', () => ({})); + +describe('TeamPicker', () => { + const teams = [ + {id: 'team1', displayName: 'Team 1'} as TeamModel, + {id: 'team2', displayName: 'Team 2'} as TeamModel, + ]; + + it('should render the selected team name', () => { + const {getByText} = renderWithIntlAndTheme( + , + ); + expect(getByText('Team 1')).toBeTruthy(); + }); + + it('should render "All teams" when teamId is ALL_TEAMS_ID', () => { + const {getByText} = renderWithIntlAndTheme( + , + ); + expect(getByText('All teams')).toBeTruthy(); + }); + + it('should call bottomSheet when the team picker is pressed', () => { + const {getByTestId} = renderWithIntlAndTheme( + , + ); + fireEvent.press(getByTestId('team_picker.button')); + expect(bottomSheet).toHaveBeenCalled(); + }); +}); From 8fad4b4b72de59f1b21499ae2081120c4090336c Mon Sep 17 00:00:00 2001 From: Julien Tant Date: Thu, 12 Dec 2024 12:28:43 -0700 Subject: [PATCH 05/15] add some tests --- .../search/initial/modifiers/index.test.tsx | 62 ++++++++++ .../home/search/initial/modifiers/index.tsx | 4 +- .../home/search/results/header.test.tsx | 109 ++++++++++++++++++ app/screens/home/search/results/header.tsx | 1 + 4 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 app/screens/home/search/initial/modifiers/index.test.tsx create mode 100644 app/screens/home/search/results/header.test.tsx diff --git a/app/screens/home/search/initial/modifiers/index.test.tsx b/app/screens/home/search/initial/modifiers/index.test.tsx new file mode 100644 index 00000000000..8089a2fb325 --- /dev/null +++ b/app/screens/home/search/initial/modifiers/index.test.tsx @@ -0,0 +1,62 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {useSharedValue} from 'react-native-reanimated'; + +import TeamPicker from '@screens/home/search/team_picker'; +import {renderWithIntlAndTheme} from '@test/intl-test-helper'; + +import {ALL_TEAMS_ID} from '../..'; + +import Modifiers from './index'; + +import type {SearchRef} from '@components/search'; +import type {TeamModel} from '@database/models/server'; + +jest.mock('@screens/home/search/team_picker', () => jest.fn(() => null)); +jest.mock('./show_more', () => jest.fn(() => null)); +jest.mock('@react-native-camera-roll/camera-roll', () => jest.fn()); + +describe('Modifiers', () => { + const scrollEnabled = useSharedValue(true); + const searchRef = React.createRef(); + const setSearchValue = jest.fn(); + const setTeamId = jest.fn(); + const teams = [{id: 'team1', displayName: 'Team 1'}, {id: 'team2', displayName: 'Team 2'}] as TeamModel[]; + + const renderComponent = (teamId = ALL_TEAMS_ID) => { + return renderWithIntlAndTheme( + , + ); + }; + + it('should render correctly', () => { + const {getByTestId} = renderComponent(); + expect(getByTestId('search.modifier.header')).toBeTruthy(); + }); + + it('should render TeamPicker when there are multiple teams', () => { + renderComponent(); + expect(TeamPicker).toHaveBeenCalled(); + }); + + it('should render the From: and In: modifiers when a team is selected', () => { + const {getByTestId} = renderComponent('team1'); + expect(getByTestId('search.modifier.from')).toBeTruthy(); + expect(getByTestId('search.modifier.in')).toBeTruthy(); + }); + + it('should not render the From: and In: modifiers when all teams are selected', () => { + const {queryByTestId} = renderComponent(ALL_TEAMS_ID); + expect(queryByTestId('search.modifier.from')).toBeNull(); + expect(queryByTestId('search.modifier.in')).toBeNull(); + }); +}); diff --git a/app/screens/home/search/initial/modifiers/index.tsx b/app/screens/home/search/initial/modifiers/index.tsx index e3709210e06..5c00fb253eb 100644 --- a/app/screens/home/search/initial/modifiers/index.tsx +++ b/app/screens/home/search/initial/modifiers/index.tsx @@ -4,7 +4,7 @@ import React, {type Dispatch, type RefObject, type SetStateAction, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {type IntlShape, useIntl} from 'react-intl'; import {View} from 'react-native'; -import Animated, {useSharedValue, useAnimatedStyle, withTiming} from 'react-native-reanimated'; +import Animated, {type SharedValue, useSharedValue, useAnimatedStyle, withTiming} from 'react-native-reanimated'; import FormattedText from '@components/formatted_text'; import {useTheme} from '@context/theme'; @@ -80,7 +80,7 @@ const getModifiersSectionsData = (intl: IntlShape, teamId: string): ModifierItem }; type Props = { - scrollEnabled: Animated.SharedValue; + scrollEnabled: SharedValue; searchRef: RefObject; setSearchValue: Dispatch>; searchValue?: string; diff --git a/app/screens/home/search/results/header.test.tsx b/app/screens/home/search/results/header.test.tsx new file mode 100644 index 00000000000..b37238a60e6 --- /dev/null +++ b/app/screens/home/search/results/header.test.tsx @@ -0,0 +1,109 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import {renderWithIntlAndTheme, fireEvent} from '@test/intl-test-helper'; +import {FileFilters} from '@utils/file'; +import {TabTypes} from '@utils/search'; + +import Header from './header'; + +import type TeamModel from '@typings/database/models/servers/team'; + +// Some subcomponents require react-native-camera-roll, which is not available in the test environment +jest.mock('@react-native-camera-roll/camera-roll', () => ({})); + +describe('Header', () => { + const onTabSelect = jest.fn(); + const onFilterChanged = jest.fn(); + const setTeamId = jest.fn(); + + const teams = [ + {id: 'team1', displayName: 'Team 1'}, + {id: 'team2', displayName: 'Team 2'}, + ] as TeamModel[]; + + it('should render correctly', () => { + const {getByText} = renderWithIntlAndTheme( +
, + ); + + expect(getByText('Messages')).toBeTruthy(); + expect(getByText('Files')).toBeTruthy(); + }); + + it('should call onTabSelect with MESSAGES when Messages button is pressed', () => { + const {getByText} = renderWithIntlAndTheme( +
, + ); + + fireEvent.press(getByText('Messages')); + expect(onTabSelect).toHaveBeenCalledWith(TabTypes.MESSAGES); + }); + + it('should call onTabSelect with FILES when Files button is pressed', () => { + const {getByText} = renderWithIntlAndTheme( +
, + ); + + fireEvent.press(getByText('Files')); + expect(onTabSelect).toHaveBeenCalledWith(TabTypes.FILES); + }); + + it('should render TeamPicker when there are multiple teams', () => { + const {getByText} = renderWithIntlAndTheme( +
, + ); + + expect(getByText('Team 1')).toBeTruthy(); + }); + + it('should render the file type filter when the Files tab is selected', () => { + const {getByTestId} = renderWithIntlAndTheme( +
, + ); + + expect(getByTestId('search.filters.file_type_icon')).toBeTruthy(); + }); +}); diff --git a/app/screens/home/search/results/header.tsx b/app/screens/home/search/results/header.tsx index 421cae77db4..b1c3f0353ae 100644 --- a/app/screens/home/search/results/header.tsx +++ b/app/screens/home/search/results/header.tsx @@ -144,6 +144,7 @@ const Header = ({ Date: Thu, 12 Dec 2024 13:02:42 -0700 Subject: [PATCH 06/15] add tests on team list and team list item --- app/components/team_list/index.test.tsx | 65 +++++++++++++++++++ app/components/team_list/index.tsx | 7 +- .../team_list_item/team_list_item.test.tsx | 57 ++++++++++++++++ 3 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 app/components/team_list/index.test.tsx create mode 100644 app/components/team_list/team_list_item/team_list_item.test.tsx diff --git a/app/components/team_list/index.test.tsx b/app/components/team_list/index.test.tsx new file mode 100644 index 00000000000..adafd9e7f0a --- /dev/null +++ b/app/components/team_list/index.test.tsx @@ -0,0 +1,65 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import {fireEvent, renderWithEverything} from '@test/intl-test-helper'; +import TestHelper from '@test/test_helper'; + +import TeamList from './index'; + +import type Database from '@nozbe/watermelondb/Database'; +import type TeamModel from '@typings/database/models/servers/team'; + +describe('TeamList', () => { + let database: Database; + beforeAll(async () => { + const server = await TestHelper.setupServerDatabase(); + database = server.database; + }); + const teams = [ + {id: 'team1', displayName: 'Team 1'} as TeamModel, + {id: 'team2', displayName: 'Team 2'} as TeamModel, + ]; + beforeAll(async () => { + const server = await TestHelper.setupServerDatabase(); + database = server.database; + }); + + it('should call onPress when a team is pressed', () => { + const onPress = jest.fn(); + const {getByText} = renderWithEverything( + , + {database}, + ); + fireEvent.press(getByText('Team 1')); + expect(onPress).toHaveBeenCalledWith('team1'); + }); + + it('should render loading component when loading is true', () => { + const {getByTestId} = renderWithEverything( + , + {database}, + ); + expect(getByTestId('team_list.loading')).toBeTruthy(); + }); + + it('should render separator after the first item when separatorAfterFirstItem is true', () => { + const {getByTestId} = renderWithEverything( + , + {database}, + ); + expect(getByTestId('team_list.separator')).toBeTruthy(); + }); +}); diff --git a/app/components/team_list/index.tsx b/app/components/team_list/index.tsx index 165753319d7..3e1540bf5d9 100644 --- a/app/components/team_list/index.tsx +++ b/app/components/team_list/index.tsx @@ -73,7 +73,10 @@ export default function TeamList({ if (separatorAfterFirstItem && i === 0) { teamListItem = (<> {teamListItem} - + ); } return teamListItem; @@ -81,7 +84,7 @@ export default function TeamList({ let footer; if (loading) { - footer = (); + footer = (); } return ( diff --git a/app/components/team_list/team_list_item/team_list_item.test.tsx b/app/components/team_list/team_list_item/team_list_item.test.tsx new file mode 100644 index 00000000000..21524344b31 --- /dev/null +++ b/app/components/team_list/team_list_item/team_list_item.test.tsx @@ -0,0 +1,57 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import {renderWithIntlAndTheme, fireEvent} from '@test/intl-test-helper'; + +import TeamListItem from './team_list_item'; + +import type TeamModel from '@typings/database/models/servers/team'; + +describe('TeamListItem', () => { + const team = { + id: 'team_id', + displayName: 'Team Display Name', + lastTeamIconUpdatedAt: 0, + } as TeamModel; + const iconTestId = `team_sidebar.team_list.team_list_item.${team.id}.team_icon`; + + it('should call onPress when pressed', () => { + const onPressMock = jest.fn(); + const {getByText} = renderWithIntlAndTheme( + , + ); + + fireEvent.press(getByText('Team Display Name')); + + expect(onPressMock).toHaveBeenCalledWith('team_id'); + }); + + it('should render TeamIcon when hideIcon is false', () => { + const {getByTestId} = renderWithIntlAndTheme( + , + ); + + expect(getByTestId(iconTestId)).toBeTruthy(); + }); + + it('should not render TeamIcon when hideIcon is true', () => { + const {queryByTestId} = renderWithIntlAndTheme( + , + ); + + expect(queryByTestId(iconTestId)).toBeNull(); + }); +}); From 7507ceb98ea03743559026156dcc345250595b4c Mon Sep 17 00:00:00 2001 From: Julien Tant Date: Mon, 16 Dec 2024 16:06:40 -0700 Subject: [PATCH 07/15] hide All Teams search behind FF --- app/screens/home/search/bottom_sheet_team_list.tsx | 9 ++++++--- app/screens/home/search/index.tsx | 3 ++- app/screens/home/search/initial/index.tsx | 2 ++ app/screens/home/search/initial/initial.tsx | 9 +++++++-- app/screens/home/search/initial/modifiers/index.test.tsx | 1 + app/screens/home/search/initial/modifiers/index.tsx | 4 +++- app/screens/home/search/results/header.test.tsx | 5 +++++ app/screens/home/search/results/header.tsx | 3 +++ app/screens/home/search/search.tsx | 4 +++- app/screens/home/search/team_picker.test.tsx | 3 +++ app/screens/home/search/team_picker.tsx | 4 +++- types/api/config.d.ts | 1 + 12 files changed, 39 insertions(+), 9 deletions(-) diff --git a/app/screens/home/search/bottom_sheet_team_list.tsx b/app/screens/home/search/bottom_sheet_team_list.tsx index 34de2ac4021..a3882060ad8 100644 --- a/app/screens/home/search/bottom_sheet_team_list.tsx +++ b/app/screens/home/search/bottom_sheet_team_list.tsx @@ -18,9 +18,10 @@ type Props = { teamId: string; setTeamId: (teamId: string) => void; title: string; + crossTeamSearchEnabled: boolean; } -export default function BottomSheetTeamList({teams, title, setTeamId, teamId}: Props) { +export default function BottomSheetTeamList({teams, title, setTeamId, teamId, crossTeamSearchEnabled}: Props) { const intl = useIntl(); const isTablet = useIsTablet(); const showTitle = !isTablet && Boolean(teams.length); @@ -32,7 +33,9 @@ export default function BottomSheetTeamList({teams, title, setTeamId, teamId}: P // teamList is a copy of teams to avoid modifying the original array const teamList = [...teams]; - teamList.unshift({id: ALL_TEAMS_ID, displayName: intl.formatMessage({id: 'mobile.search.team.all_teams', defaultMessage: 'All teams'})} as TeamModel); + if (crossTeamSearchEnabled) { + teamList.unshift({id: ALL_TEAMS_ID, displayName: intl.formatMessage({id: 'mobile.search.team.all_teams', defaultMessage: 'All teams'})} as TeamModel); + } return ( ); diff --git a/app/screens/home/search/index.tsx b/app/screens/home/search/index.tsx index c9ccd187121..a64d1e95f97 100644 --- a/app/screens/home/search/index.tsx +++ b/app/screens/home/search/index.tsx @@ -4,7 +4,7 @@ import {withDatabase, withObservables} from '@nozbe/watermelondb/react'; import compose from 'lodash/fp/compose'; -import {observeCurrentTeamId} from '@queries/servers/system'; +import {observeConfigBooleanValue, observeCurrentTeamId} from '@queries/servers/system'; import {queryJoinedTeams} from '@queries/servers/team'; import SearchScreen from './search'; @@ -16,6 +16,7 @@ const enhance = withObservables([], ({database}: WithDatabaseArgs) => { return { teamId: currentTeamId, teams: queryJoinedTeams(database).observe(), + crossTeamSearchEnabled: observeConfigBooleanValue(database, 'FeatureFlagExperimentalCrossTeamSearch'), }; }); diff --git a/app/screens/home/search/initial/index.tsx b/app/screens/home/search/initial/index.tsx index 2782170eb71..10fe9d14730 100644 --- a/app/screens/home/search/initial/index.tsx +++ b/app/screens/home/search/initial/index.tsx @@ -5,6 +5,7 @@ import compose from 'lodash/fp/compose'; import {of as of$} from 'rxjs'; import {switchMap, distinctUntilChanged} from 'rxjs/operators'; +import {observeConfigBooleanValue} from '@queries/servers/system'; import {observeTeam, queryTeamSearchHistoryByTeamId} from '@queries/servers/team'; import Initial from './initial'; @@ -22,6 +23,7 @@ const enhance = withObservables(['teamId'], ({database, teamId}: EnhanceProps) = switchMap((t) => of$(t?.displayName || '')), distinctUntilChanged(), ), + crossTeamSearchEnabled: observeConfigBooleanValue(database, 'FeatureFlagExperimentalCrossTeamSearch'), }; }); diff --git a/app/screens/home/search/initial/initial.tsx b/app/screens/home/search/initial/initial.tsx index 3a70ccf02db..9ff01ae495e 100644 --- a/app/screens/home/search/initial/initial.tsx +++ b/app/screens/home/search/initial/initial.tsx @@ -3,6 +3,8 @@ import React, {type Dispatch, type RefObject, type SetStateAction} from 'react'; +import {ALL_TEAMS_ID} from '..'; + import Modifiers from './modifiers'; import RecentSearches from './recent_searches'; @@ -22,9 +24,11 @@ type Props = { teamId: string; teamName: string; teams: TeamModel[]; + crossTeamSearchEnabled: boolean; } -const Initial = ({recentSearches, scrollEnabled, searchValue, setRecentValue, searchRef, teamId, teamName, teams, setTeamId, setSearchValue}: Props) => { +const Initial = ({recentSearches, scrollEnabled, searchValue, setRecentValue, searchRef, teamId, teamName, teams, setTeamId, setSearchValue, crossTeamSearchEnabled}: Props) => { + const showRecentSearches = Boolean(recentSearches.length) && teamId !== ALL_TEAMS_ID; return ( <> - {Boolean(recentSearches.length) && + {showRecentSearches && { teams={teams} scrollEnabled={scrollEnabled} searchRef={searchRef} + crossTeamSearchEnabled={true} />, ); }; diff --git a/app/screens/home/search/initial/modifiers/index.tsx b/app/screens/home/search/initial/modifiers/index.tsx index 5c00fb253eb..6a53e76119c 100644 --- a/app/screens/home/search/initial/modifiers/index.tsx +++ b/app/screens/home/search/initial/modifiers/index.tsx @@ -87,8 +87,9 @@ type Props = { setTeamId: (id: string) => void; teamId: string; teams: TeamModel[]; + crossTeamSearchEnabled: boolean; } -const Modifiers = ({scrollEnabled, searchValue, setSearchValue, searchRef, setTeamId, teamId, teams}: Props) => { +const Modifiers = ({scrollEnabled, searchValue, setSearchValue, searchRef, setTeamId, teamId, teams, crossTeamSearchEnabled}: Props) => { const theme = useTheme(); const intl = useIntl(); @@ -157,6 +158,7 @@ const Modifiers = ({scrollEnabled, searchValue, setSearchValue, searchRef, setTe setTeamId={setTeamId} teamId={teamId} teams={teams} + crossTeamSearchEnabled={crossTeamSearchEnabled} /> } diff --git a/app/screens/home/search/results/header.test.tsx b/app/screens/home/search/results/header.test.tsx index b37238a60e6..ec1a44da3a5 100644 --- a/app/screens/home/search/results/header.test.tsx +++ b/app/screens/home/search/results/header.test.tsx @@ -34,6 +34,7 @@ describe('Header', () => { selectedTab={TabTypes.MESSAGES} selectedFilter={FileFilters.ALL} teams={teams} + crossTeamSearchEnabled={false} />, ); @@ -51,6 +52,7 @@ describe('Header', () => { selectedTab={TabTypes.MESSAGES} selectedFilter={FileFilters.ALL} teams={teams} + crossTeamSearchEnabled={false} />, ); @@ -68,6 +70,7 @@ describe('Header', () => { selectedTab={TabTypes.MESSAGES} selectedFilter={FileFilters.ALL} teams={teams} + crossTeamSearchEnabled={false} />, ); @@ -85,6 +88,7 @@ describe('Header', () => { selectedTab={TabTypes.MESSAGES} selectedFilter={FileFilters.ALL} teams={teams} + crossTeamSearchEnabled={false} />, ); @@ -101,6 +105,7 @@ describe('Header', () => { selectedTab={TabTypes.FILES} selectedFilter={FileFilters.ALL} teams={teams} + crossTeamSearchEnabled={false} />, ); diff --git a/app/screens/home/search/results/header.tsx b/app/screens/home/search/results/header.tsx index b1c3f0353ae..76fe8ba6bca 100644 --- a/app/screens/home/search/results/header.tsx +++ b/app/screens/home/search/results/header.tsx @@ -29,6 +29,7 @@ type Props = { setTeamId: (id: string) => void; teamId: string; teams: TeamModel[]; + crossTeamSearchEnabled: boolean; } const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => { @@ -75,6 +76,7 @@ const Header = ({ selectedTab, selectedFilter, teams, + crossTeamSearchEnabled, }: Props) => { const theme = useTheme(); const styles = getStyleFromTheme(theme); @@ -166,6 +168,7 @@ const Header = ({ setTeamId={setTeamId} teamId={teamId} teams={teams} + crossTeamSearchEnabled={crossTeamSearchEnabled} /> )} diff --git a/app/screens/home/search/search.tsx b/app/screens/home/search/search.tsx index 45386ab6bfc..8cff5305e71 100644 --- a/app/screens/home/search/search.tsx +++ b/app/screens/home/search/search.tsx @@ -50,6 +50,7 @@ const AutocompletePaddingTop = 4; type Props = { teamId: string; teams: TeamModel[]; + crossTeamSearchEnabled: boolean; } const styles = StyleSheet.create({ @@ -77,7 +78,7 @@ const getSearchParams = (terms: string, filterValue?: FileFilter) => { const searchScreenIndex = 1; -const SearchScreen = ({teamId, teams}: Props) => { +const SearchScreen = ({teamId, teams, crossTeamSearchEnabled}: Props) => { const nav = useNavigation(); const isFocused = useIsFocused(); const intl = useIntl(); @@ -384,6 +385,7 @@ const SearchScreen = ({teamId, teams}: Props) => { selectedTab={selectedTab} selectedFilter={filter} teams={teams} + crossTeamSearchEnabled={crossTeamSearchEnabled} /> } diff --git a/app/screens/home/search/team_picker.test.tsx b/app/screens/home/search/team_picker.test.tsx index 22c5e95ac1f..f0f922146cf 100644 --- a/app/screens/home/search/team_picker.test.tsx +++ b/app/screens/home/search/team_picker.test.tsx @@ -31,6 +31,7 @@ describe('TeamPicker', () => { setTeamId={jest.fn()} teams={teams} teamId={'team1'} + crossTeamSearchEnabled={true} />, ); expect(getByText('Team 1')).toBeTruthy(); @@ -42,6 +43,7 @@ describe('TeamPicker', () => { setTeamId={jest.fn()} teams={teams} teamId={ALL_TEAMS_ID} + crossTeamSearchEnabled={true} />, ); expect(getByText('All teams')).toBeTruthy(); @@ -53,6 +55,7 @@ describe('TeamPicker', () => { setTeamId={jest.fn()} teams={teams} teamId={'team1'} + crossTeamSearchEnabled={true} />, ); fireEvent.press(getByTestId('team_picker.button')); diff --git a/app/screens/home/search/team_picker.tsx b/app/screens/home/search/team_picker.tsx index 0f3e0fb7189..5fb21faec3a 100644 --- a/app/screens/home/search/team_picker.tsx +++ b/app/screens/home/search/team_picker.tsx @@ -44,8 +44,9 @@ type Props = { teams: TeamModel[]; setTeamId: (id: string) => void; teamId: string; + crossTeamSearchEnabled: boolean; } -const TeamPicker = ({setTeamId, teams, teamId}: Props) => { +const TeamPicker = ({setTeamId, teams, teamId, crossTeamSearchEnabled}: Props) => { const intl = useIntl(); const theme = useTheme(); const styles = getStyleFromTheme(theme); @@ -65,6 +66,7 @@ const TeamPicker = ({setTeamId, teams, teamId}: Props) => { teams={teams} teamId={teamId} title={title} + crossTeamSearchEnabled={crossTeamSearchEnabled} /> ); }; diff --git a/types/api/config.d.ts b/types/api/config.d.ts index 6221eeac8a5..e7b05c3e0be 100644 --- a/types/api/config.d.ts +++ b/types/api/config.d.ts @@ -121,6 +121,7 @@ interface ClientConfig { FeatureFlagCollapsedThreads?: string; FeatureFlagPostPriority?: string; FeatureFlagChannelBookmarks?: string; + FeatureFlagExperimentalCrossTeamSearch?: string; ForgotPasswordLink?: string; GfycatApiKey: string; GfycatApiSecret: string; From b59c05a3589d11c48fe18e8a940cd55d8067d386 Mon Sep 17 00:00:00 2001 From: Julien Tant Date: Mon, 16 Dec 2024 16:16:50 -0700 Subject: [PATCH 08/15] use style variable for separator --- app/components/team_list/index.tsx | 32 ++++++++++++++++++------------ 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/app/components/team_list/index.tsx b/app/components/team_list/index.tsx index 3e1540bf5d9..f107812e13f 100644 --- a/app/components/team_list/index.tsx +++ b/app/components/team_list/index.tsx @@ -3,10 +3,12 @@ import {BottomSheetFlatList} from '@gorhom/bottom-sheet'; import React, {useCallback, useMemo} from 'react'; -import {type ListRenderItemInfo, StyleSheet, View} from 'react-native'; +import {type ListRenderItemInfo, View} from 'react-native'; import {FlatList} from 'react-native-gesture-handler'; // Keep the FlatList from gesture handler so it works well with bottom sheet import Loading from '@components/loading'; +import {useTheme} from '@context/theme'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import TeamListItem from './team_list_item'; @@ -27,17 +29,19 @@ type Props = { separatorAfterFirstItem?: boolean; } -const styles = StyleSheet.create({ - container: { - flexGrow: 1, - }, - contentContainer: { - marginBottom: 4, - }, - separator: { - height: 1, - backgroundColor: 'rgba(0, 0, 0, 0.1)', - }, +const getStyleSheet = makeStyleSheetFromTheme((theme) => { + return { + container: { + flexGrow: 1, + }, + contentContainer: { + marginBottom: 4, + }, + separator: { + height: 1, + backgroundColor: changeOpacity(theme.centerChannelColor, 0.08), + }, + }; }); const keyExtractor = (item: TeamModel) => item.id; @@ -57,6 +61,8 @@ export default function TeamList({ separatorAfterFirstItem = false, }: Props) { const List = useMemo(() => (type === 'FlatList' ? FlatList : BottomSheetFlatList), [type]); + const theme = useTheme(); + const styles = getStyleSheet(theme); const renderTeam = useCallback(({item: t, index: i}: ListRenderItemInfo) => { let teamListItem = ( @@ -80,7 +86,7 @@ export default function TeamList({ ); } return teamListItem; - }, [textColor, iconTextColor, iconBackgroundColor, onPress, selectedTeamId, hideIcon, separatorAfterFirstItem]); + }, [textColor, iconTextColor, iconBackgroundColor, onPress, selectedTeamId, hideIcon, separatorAfterFirstItem, styles.separator]); let footer; if (loading) { From 9a9e34145df1fe4d5d40526d4ca307f7c9cb009a Mon Sep 17 00:00:00 2001 From: Julien Tant Date: Tue, 17 Dec 2024 10:14:27 -0700 Subject: [PATCH 09/15] ALL TEAMS does not have a search history --- app/screens/home/search/search.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/screens/home/search/search.tsx b/app/screens/home/search/search.tsx index 8cff5305e71..5ee4b5ee6aa 100644 --- a/app/screens/home/search/search.tsx +++ b/app/screens/home/search/search.tsx @@ -32,6 +32,8 @@ import Initial from './initial'; import Results from './results'; import Header from './results/header'; +import {ALL_TEAMS_ID} from '.'; + import type {SearchRef} from '@components/search'; import type PostModel from '@typings/database/models/servers/post'; import type TeamModel from '@typings/database/models/servers/team'; @@ -189,7 +191,10 @@ const SearchScreen = ({teamId, teams, crossTeamSearchEnabled}: Props) => { hideHeader(true); handleLoading(true); setLastSearchedValue(term); - addSearchToTeamSearchHistory(serverUrl, newSearchTeamId, term); + + if (newSearchTeamId !== ALL_TEAMS_ID) { + addSearchToTeamSearchHistory(serverUrl, newSearchTeamId, term); + } const [postResults, {files, channels}] = await Promise.all([ searchPosts(serverUrl, newSearchTeamId, searchParams), searchFiles(serverUrl, newSearchTeamId, searchParams), From fa204a40573f464f566450f09cc55bf9872f4b9e Mon Sep 17 00:00:00 2001 From: Julien Tant <785518+JulienTant@users.noreply.github.com> Date: Thu, 19 Dec 2024 09:59:24 -0700 Subject: [PATCH 10/15] Update app/components/team_list/index.test.tsx Co-authored-by: Elias Nahum --- app/components/team_list/index.test.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/components/team_list/index.test.tsx b/app/components/team_list/index.test.tsx index adafd9e7f0a..7a510c1fc48 100644 --- a/app/components/team_list/index.test.tsx +++ b/app/components/team_list/index.test.tsx @@ -21,10 +21,6 @@ describe('TeamList', () => { {id: 'team1', displayName: 'Team 1'} as TeamModel, {id: 'team2', displayName: 'Team 2'} as TeamModel, ]; - beforeAll(async () => { - const server = await TestHelper.setupServerDatabase(); - database = server.database; - }); it('should call onPress when a team is pressed', () => { const onPress = jest.fn(); From e650f51b45a4e7ea3c44a3464569dee3d1f8002d Mon Sep 17 00:00:00 2001 From: Julien Tant Date: Thu, 19 Dec 2024 10:22:39 -0700 Subject: [PATCH 11/15] move ALL_TEAMS_ID to a constant file --- app/constants/index.ts | 2 ++ app/constants/team.ts | 8 ++++++++ app/screens/home/search/bottom_sheet_team_list.tsx | 3 +-- app/screens/home/search/index.tsx | 2 -- app/screens/home/search/initial/initial.tsx | 2 +- app/screens/home/search/initial/modifiers/index.test.tsx | 3 +-- app/screens/home/search/initial/modifiers/index.tsx | 3 +-- app/screens/home/search/search.tsx | 3 +-- app/screens/home/search/team_picker.test.tsx | 3 +-- app/screens/home/search/team_picker.tsx | 3 +-- 10 files changed, 17 insertions(+), 15 deletions(-) create mode 100644 app/constants/team.ts diff --git a/app/constants/index.ts b/app/constants/index.ts index d7a407f40d8..b6761997406 100644 --- a/app/constants/index.ts +++ b/app/constants/index.ts @@ -35,6 +35,7 @@ import ServerErrors from './server_errors'; import SnackBar from './snack_bar'; import Sso from './sso'; import SupportedServer from './supported_server'; +import Team from './team'; import Tutorial from './tutorial'; import View from './view'; import WebsocketEvents from './websocket'; @@ -74,6 +75,7 @@ export { SnackBar, Sso, SupportedServer, + Team, Tutorial, View, WebsocketEvents, diff --git a/app/constants/team.ts b/app/constants/team.ts new file mode 100644 index 00000000000..1506b79b61e --- /dev/null +++ b/app/constants/team.ts @@ -0,0 +1,8 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +export const ALL_TEAMS_ID = ''; + +export default { + ALL_TEAMS_ID, +}; diff --git a/app/screens/home/search/bottom_sheet_team_list.tsx b/app/screens/home/search/bottom_sheet_team_list.tsx index a3882060ad8..8b38526e9c8 100644 --- a/app/screens/home/search/bottom_sheet_team_list.tsx +++ b/app/screens/home/search/bottom_sheet_team_list.tsx @@ -5,12 +5,11 @@ import React, {useCallback} from 'react'; import {useIntl} from 'react-intl'; import TeamList from '@components/team_list'; +import {ALL_TEAMS_ID} from '@constants/team'; import {useIsTablet} from '@hooks/device'; import BottomSheetContent from '@screens/bottom_sheet/content'; import {dismissBottomSheet} from '@screens/navigation'; -import {ALL_TEAMS_ID} from '.'; - import type TeamModel from '@typings/database/models/servers/team'; type Props = { diff --git a/app/screens/home/search/index.tsx b/app/screens/home/search/index.tsx index a64d1e95f97..5ed7666785c 100644 --- a/app/screens/home/search/index.tsx +++ b/app/screens/home/search/index.tsx @@ -20,8 +20,6 @@ const enhance = withObservables([], ({database}: WithDatabaseArgs) => { }; }); -export const ALL_TEAMS_ID = ''; - export default compose( withDatabase, enhance, diff --git a/app/screens/home/search/initial/initial.tsx b/app/screens/home/search/initial/initial.tsx index 9ff01ae495e..6053bd18eee 100644 --- a/app/screens/home/search/initial/initial.tsx +++ b/app/screens/home/search/initial/initial.tsx @@ -3,7 +3,7 @@ import React, {type Dispatch, type RefObject, type SetStateAction} from 'react'; -import {ALL_TEAMS_ID} from '..'; +import {ALL_TEAMS_ID} from '@constants/team'; import Modifiers from './modifiers'; import RecentSearches from './recent_searches'; diff --git a/app/screens/home/search/initial/modifiers/index.test.tsx b/app/screens/home/search/initial/modifiers/index.test.tsx index 1c6aada36f4..5964b9abd28 100644 --- a/app/screens/home/search/initial/modifiers/index.test.tsx +++ b/app/screens/home/search/initial/modifiers/index.test.tsx @@ -4,11 +4,10 @@ import React from 'react'; import {useSharedValue} from 'react-native-reanimated'; +import {ALL_TEAMS_ID} from '@constants/team'; import TeamPicker from '@screens/home/search/team_picker'; import {renderWithIntlAndTheme} from '@test/intl-test-helper'; -import {ALL_TEAMS_ID} from '../..'; - import Modifiers from './index'; import type {SearchRef} from '@components/search'; diff --git a/app/screens/home/search/initial/modifiers/index.tsx b/app/screens/home/search/initial/modifiers/index.tsx index 6a53e76119c..fe01990955f 100644 --- a/app/screens/home/search/initial/modifiers/index.tsx +++ b/app/screens/home/search/initial/modifiers/index.tsx @@ -7,13 +7,12 @@ import {View} from 'react-native'; import Animated, {type SharedValue, useSharedValue, useAnimatedStyle, withTiming} from 'react-native-reanimated'; import FormattedText from '@components/formatted_text'; +import {ALL_TEAMS_ID} from '@constants/team'; import {useTheme} from '@context/theme'; import TeamPicker from '@screens/home/search/team_picker'; import {makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; -import {ALL_TEAMS_ID} from '../..'; - import Modifier, {type ModifierItem} from './modifier'; import ShowMoreButton from './show_more'; diff --git a/app/screens/home/search/search.tsx b/app/screens/home/search/search.tsx index 5ee4b5ee6aa..b5288d532ff 100644 --- a/app/screens/home/search/search.tsx +++ b/app/screens/home/search/search.tsx @@ -18,6 +18,7 @@ import Loading from '@components/loading'; import NavigationHeader from '@components/navigation_header'; import RoundedHeaderContext from '@components/rounded_header_context'; import {Screens} from '@constants'; +import {ALL_TEAMS_ID} from '@constants/team'; import {BOTTOM_TAB_HEIGHT} from '@constants/view'; import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; @@ -32,8 +33,6 @@ import Initial from './initial'; import Results from './results'; import Header from './results/header'; -import {ALL_TEAMS_ID} from '.'; - import type {SearchRef} from '@components/search'; import type PostModel from '@typings/database/models/servers/post'; import type TeamModel from '@typings/database/models/servers/team'; diff --git a/app/screens/home/search/team_picker.test.tsx b/app/screens/home/search/team_picker.test.tsx index f0f922146cf..e6726f11d03 100644 --- a/app/screens/home/search/team_picker.test.tsx +++ b/app/screens/home/search/team_picker.test.tsx @@ -3,13 +3,12 @@ import React from 'react'; +import {ALL_TEAMS_ID} from '@constants/team'; import {bottomSheet} from '@screens/navigation'; import {fireEvent, renderWithIntlAndTheme} from '@test/intl-test-helper'; import TeamPicker from './team_picker'; -import {ALL_TEAMS_ID} from '.'; - import type {TeamModel} from '@database/models/server'; jest.mock('@screens/navigation', () => ({ diff --git a/app/screens/home/search/team_picker.tsx b/app/screens/home/search/team_picker.tsx index 5fb21faec3a..4fd3f2eca12 100644 --- a/app/screens/home/search/team_picker.tsx +++ b/app/screens/home/search/team_picker.tsx @@ -8,6 +8,7 @@ import {Text, View} from 'react-native'; import CompassIcon from '@components/compass_icon'; import {ITEM_HEIGHT} from '@components/slide_up_panel_item'; import TouchableWithFeedback from '@components/touchable_with_feedback'; +import {ALL_TEAMS_ID} from '@constants/team'; import {useTheme} from '@context/theme'; import {TITLE_HEIGHT} from '@screens/bottom_sheet'; import {bottomSheet} from '@screens/navigation'; @@ -17,8 +18,6 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import BottomSheetTeamList from './bottom_sheet_team_list'; -import {ALL_TEAMS_ID} from '.'; - import type TeamModel from '@typings/database/models/servers/team'; const MENU_DOWN_ICON_SIZE = 24; From 77f5ab01b982b0628d20ca5b3b21edf2dbbcb9a0 Mon Sep 17 00:00:00 2001 From: Julien Tant Date: Thu, 19 Dec 2024 10:27:59 -0700 Subject: [PATCH 12/15] memoize the construction of team list to prevent useless allocation --- app/screens/home/search/bottom_sheet_team_list.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/screens/home/search/bottom_sheet_team_list.tsx b/app/screens/home/search/bottom_sheet_team_list.tsx index 8b38526e9c8..4f95e3ff431 100644 --- a/app/screens/home/search/bottom_sheet_team_list.tsx +++ b/app/screens/home/search/bottom_sheet_team_list.tsx @@ -1,7 +1,7 @@ // 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 TeamList from '@components/team_list'; @@ -30,11 +30,13 @@ export default function BottomSheetTeamList({teams, title, setTeamId, teamId, cr dismissBottomSheet(); }, [setTeamId]); - // teamList is a copy of teams to avoid modifying the original array - const teamList = [...teams]; - if (crossTeamSearchEnabled) { - teamList.unshift({id: ALL_TEAMS_ID, displayName: intl.formatMessage({id: 'mobile.search.team.all_teams', defaultMessage: 'All teams'})} as TeamModel); - } + const teamList = useMemo(() => { + const list = [...teams]; + if (crossTeamSearchEnabled) { + list.unshift({id: ALL_TEAMS_ID, displayName: intl.formatMessage({id: 'mobile.search.team.all_teams', defaultMessage: 'All teams'})} as TeamModel); + } + return list; + }, [teams, crossTeamSearchEnabled, intl]); return ( Date: Thu, 19 Dec 2024 10:45:40 -0700 Subject: [PATCH 13/15] combine pushes --- app/screens/home/search/initial/modifiers/index.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/screens/home/search/initial/modifiers/index.tsx b/app/screens/home/search/initial/modifiers/index.tsx index fe01990955f..a08cfe4955d 100644 --- a/app/screens/home/search/initial/modifiers/index.tsx +++ b/app/screens/home/search/initial/modifiers/index.tsx @@ -54,9 +54,7 @@ const getModifiersSectionsData = (intl: IntlShape, teamId: string): ModifierItem term: 'From:', testID: 'search.modifier.from', description: formatMessage({id: 'mobile.search.modifier.from', defaultMessage: ' a specific user'}), - }); - - sectionsData.push({ + }, { term: 'In:', testID: 'search.modifier.in', description: formatMessage({id: 'mobile.search.modifier.in', defaultMessage: ' a specific channel'}), @@ -67,8 +65,7 @@ const getModifiersSectionsData = (intl: IntlShape, teamId: string): ModifierItem term: '-', testID: 'search.modifier.exclude', description: formatMessage({id: 'mobile.search.modifier.exclude', defaultMessage: ' exclude search terms'}), - }); - sectionsData.push({ + }, { term: '""', testID: 'search.modifier.phrases', description: formatMessage({id: 'mobile.search.modifier.phrases', defaultMessage: ' messages with phrases'}), From 7ed6dd890cf6e36f2ab9f7f4278dfc5c49fa2c00 Mon Sep 17 00:00:00 2001 From: Julien Tant Date: Thu, 19 Dec 2024 10:57:35 -0700 Subject: [PATCH 14/15] move style to stylesheet --- app/screens/home/search/team_picker.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/screens/home/search/team_picker.tsx b/app/screens/home/search/team_picker.tsx index 4fd3f2eca12..4ee98af481d 100644 --- a/app/screens/home/search/team_picker.tsx +++ b/app/screens/home/search/team_picker.tsx @@ -25,12 +25,15 @@ const NO_TEAMS_HEIGHT = 392; const getStyleFromTheme = makeStyleSheetFromTheme((theme) => { return { - teamContainer: { + teamPicker: { flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end', width: '100%', }, + container: { + flex: 1, + }, teamName: { color: theme.centerChannelColor, fontSize: 12, @@ -95,9 +98,9 @@ const TeamPicker = ({setTeamId, teams, teamId, crossTeamSearchEnabled}: Props) = onPress={handleTeamChange} type='opacity' testID='team_picker.button' - style={styles.teamContainer} + style={styles.teamPicker} > - + Date: Thu, 19 Dec 2024 11:06:06 -0700 Subject: [PATCH 15/15] revert changes to Package.resolved --- ios/Gekidou/Package.resolved | 85 +----------------------------------- 1 file changed, 2 insertions(+), 83 deletions(-) diff --git a/ios/Gekidou/Package.resolved b/ios/Gekidou/Package.resolved index d5f7fdb972d..2877a162dae 100644 --- a/ios/Gekidou/Package.resolved +++ b/ios/Gekidou/Package.resolved @@ -1,94 +1,13 @@ { "object": { "pins": [ - { - "package": "Cryptor", - "repositoryURL": "https://github.com/Kitura/BlueCryptor.git", - "state": { - "branch": null, - "revision": "cec97c24b111351e70e448972a7d3fe68a756d6d", - "version": "2.0.2" - } - }, - { - "package": "CryptorECC", - "repositoryURL": "https://github.com/Kitura/BlueECC.git", - "state": { - "branch": null, - "revision": "1485268a54f8135435a825a855e733f026fa6cc8", - "version": "1.2.201" - } - }, - { - "package": "CryptorRSA", - "repositoryURL": "https://github.com/Kitura/BlueRSA.git", - "state": { - "branch": null, - "revision": "4c9464b4a21dd558a9b1f4a4ed603bb67dcbc773", - "version": "1.0.202" - } - }, - { - "package": "KituraContracts", - "repositoryURL": "https://github.com/Kitura/KituraContracts.git", - "state": { - "branch": null, - "revision": "8a4778c3aa7833e9e1af884e8819d436c237cd70", - "version": "1.2.201" - } - }, - { - "package": "LoggerAPI", - "repositoryURL": "https://github.com/Kitura/LoggerAPI.git", - "state": { - "branch": null, - "revision": "e82d34eab3f0b05391082b11ea07d3b70d2f65bb", - "version": "1.9.200" - } - }, - { - "package": "OpenGraph", - "repositoryURL": "https://github.com/satoshi-takano/OpenGraph.git", - "state": { - "branch": null, - "revision": "382972f1963580eeabafd88ad012e66576b4d213", - "version": "1.4.1" - } - }, - { - "package": "Sentry", - "repositoryURL": "https://github.com/getsentry/sentry-cocoa.git", - "state": { - "branch": null, - "revision": "5575af93efb776414f243e93d6af9f6258dc539a", - "version": "8.36.0" - } - }, { "package": "SQLite.swift", "repositoryURL": "https://github.com/stephencelis/SQLite.swift.git", "state": { "branch": null, - "revision": "a95fc6df17d108bd99210db5e8a9bac90fe984b8", - "version": "0.15.3" - } - }, - { - "package": "SwiftJWT", - "repositoryURL": "https://github.com/Kitura/Swift-JWT.git", - "state": { - "branch": null, - "revision": "47c6384b6923e9bb1f214d2ba4bd52af39440588", - "version": "3.6.201" - } - }, - { - "package": "swift-log", - "repositoryURL": "https://github.com/apple/swift-log.git", - "state": { - "branch": null, - "revision": "9cb486020ebf03bfa5b5df985387a14a98744537", - "version": "1.6.1" + "revision": "9af51e2edf491c0ea632e369a6566e09b65aa333", + "version": "0.13.0" } } ]