From 684d9421a7b91b72a7581cda9535072ee384dec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Espino=20Garc=C3=ADa?= Date: Wed, 2 Mar 2022 13:36:10 +0100 Subject: [PATCH] Add sample test for local actions (#5945) * Add sample test for local actions * Fix the tests and add all missing tests * Address feedback * Sort imports Co-authored-by: Elias Nahum --- app/actions/local/channel.test.ts | 437 +++++++++++++++++++++++ app/actions/local/channel.ts | 27 +- app/queries/servers/team.ts | 27 +- patches/@nozbe+watermelondb+0.24.0.patch | 10 +- 4 files changed, 472 insertions(+), 29 deletions(-) create mode 100644 app/actions/local/channel.test.ts diff --git a/app/actions/local/channel.test.ts b/app/actions/local/channel.test.ts new file mode 100644 index 000000000..9449a6465 --- /dev/null +++ b/app/actions/local/channel.test.ts @@ -0,0 +1,437 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Database} from '@nozbe/watermelondb'; +import {DeviceEventEmitter} from 'react-native'; + +import {Navigation} from '@constants'; +import {SYSTEM_IDENTIFIERS} from '@constants/database'; +import DatabaseManager from '@database/manager'; +import ServerDataOperator from '@database/operator/server_data_operator'; +import {queryMyChannel} from '@queries/servers/channel'; +import {queryCommonSystemValues, queryTeamHistory} from '@queries/servers/system'; +import {queryChannelHistory} from '@queries/servers/team'; +import {dismissAllModalsAndPopToRoot, dismissAllModalsAndPopToScreen} from '@screens/navigation'; + +import {switchToChannel} from './channel'; + +let mockIsTablet: jest.Mock; +const now = new Date('2020-01-01').getTime(); + +jest.mock('@screens/navigation', () => { + const original = jest.requireActual('@screens/navigation'); + return { + ...original, + dismissAllModalsAndPopToScreen: jest.fn(), + dismissAllModalsAndPopToRoot: jest.fn(), + }; +}); + +jest.mock('@utils/helpers', () => { + const original = jest.requireActual('@utils/helpers'); + mockIsTablet = jest.fn(() => false); + return { + ...original, + isTablet: mockIsTablet, + }; +}); + +const queryDatabaseValues = async (database: Database, teamId: string, channelId: string) => ({ + systemValues: await queryCommonSystemValues(database), + teamHistory: await queryTeamHistory(database), + channelHistory: await queryChannelHistory(database, teamId), + member: await queryMyChannel(database, channelId), +}); + +describe('switchToChannel', () => { + let operator: ServerDataOperator; + let spyNow: jest.SpyInstance; + const serverUrl = 'baseHandler.test.com'; + const channelId = 'id1'; + const teamId = 'tId1'; + const team: Team = { + id: teamId, + } as Team; + const channel: Channel = { + id: channelId, + team_id: teamId, + total_msg_count: 0, + } as Channel; + const channelMember: ChannelMembership = { + id: 'id', + channel_id: channelId, + msg_count: 0, + } as ChannelMembership; + beforeEach(async () => { + await DatabaseManager.init([serverUrl]); + operator = DatabaseManager.serverDatabases[serverUrl].operator; + spyNow = jest.spyOn(Date, 'now').mockImplementation(() => now); + }); + + afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); + spyNow.mockRestore(); + }); + + it('handle not found database', async () => { + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {error} = await switchToChannel('foo', 'channelId'); + listener.remove(); + + expect(error).toBeTruthy(); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(0); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(0); + expect(listenerCallback).toHaveBeenCalledTimes(0); + }); + + it('handle no member', async () => { + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {models, error} = await switchToChannel(serverUrl, 'channelId'); + listener.remove(); + + expect(error).toBeUndefined(); + expect(models?.length).toBe(0); + + const {systemValues, teamHistory, channelHistory, member} = await queryDatabaseValues(operator.database, teamId, channelId); + + expect(error).toBeUndefined(); + expect(systemValues.currentTeamId).toBe(''); + expect(systemValues.currentChannelId).toBe(''); + expect(teamHistory.length).toBe(0); + expect(channelHistory.length).toBe(0); + expect(member?.lastViewedAt).toBe(undefined); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(0); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(0); + expect(listenerCallback).toHaveBeenCalledTimes(0); + }); + + it('switch to current channel', async () => { + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}, {id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID, value: channelId}], prepareRecordsOnly: false}); + + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {models, error} = await switchToChannel(serverUrl, channelId); + listener.remove(); + + const {systemValues, teamHistory, channelHistory, member} = await queryDatabaseValues(operator.database, teamId, channelId); + + expect(error).toBeUndefined(); + expect(models?.length).toBe(1); // Viewed at + expect(systemValues.currentTeamId).toBe(teamId); + expect(systemValues.currentChannelId).toBe(channelId); + expect(teamHistory.length).toBe(0); + expect(channelHistory.length).toBe(0); + expect(member?.lastViewedAt).toBe(now); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(1); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(0); + expect(listenerCallback).toHaveBeenCalledTimes(0); + }); + + it('switch to different channel in same team', async () => { + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}, {id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID, value: 'currentChannelId'}], prepareRecordsOnly: false}); + + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {models, error} = await switchToChannel(serverUrl, channelId); + listener.remove(); + + const {systemValues, teamHistory, channelHistory, member} = await queryDatabaseValues(operator.database, teamId, channelId); + + expect(error).toBeUndefined(); + expect(models?.length).toBe(3); // Viewed at, channel history and currentChannelId + expect(systemValues.currentTeamId).toBe(teamId); + expect(systemValues.currentChannelId).toBe(channelId); + expect(teamHistory.length).toBe(0); + expect(channelHistory.length).toBe(1); + expect(channelHistory[0]).toBe(channelId); + expect(member?.lastViewedAt).toBe(now); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(1); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(0); + expect(listenerCallback).toHaveBeenCalledTimes(0); + }); + + it('switch to different channel in different team', async () => { + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: 'currentTeamId'}, {id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID, value: 'currentChannelId'}], prepareRecordsOnly: false}); + + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {models, error} = await switchToChannel(serverUrl, channelId, teamId); + listener.remove(); + + const {systemValues, teamHistory, channelHistory, member} = await queryDatabaseValues(operator.database, teamId, channelId); + + expect(error).toBeUndefined(); + expect(models?.length).toBe(5); // Viewed at, channel history, team history, currentTeamId and currentChannelId + expect(systemValues.currentTeamId).toBe(teamId); + expect(systemValues.currentChannelId).toBe(channelId); + expect(teamHistory.length).toBe(1); + expect(teamHistory[0]).toBe(teamId); + expect(channelHistory.length).toBe(1); + expect(channelHistory[0]).toBe(channelId); + expect(member?.lastViewedAt).toBe(now); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(1); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(0); + expect(listenerCallback).toHaveBeenCalledTimes(0); + }); + + it('switch to same channel in different team', async () => { + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + const tChannel = {...channel, team_id: ''}; + await operator.handleChannel({channels: [tChannel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [tChannel], myChannels: [channelMember], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: 'currentTeamId'}, {id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID, value: channelId}], prepareRecordsOnly: false}); + + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {models, error} = await switchToChannel(serverUrl, channelId, teamId); + listener.remove(); + + const {systemValues, teamHistory, channelHistory, member} = await queryDatabaseValues(operator.database, teamId, channelId); + + expect(error).toBeUndefined(); + expect(models?.length).toBe(4); // Viewed at, channel history, team history and currentTeamId + expect(systemValues.currentTeamId).toBe(teamId); + expect(systemValues.currentChannelId).toBe(channelId); + expect(teamHistory.length).toBe(1); + expect(teamHistory[0]).toBe(teamId); + expect(channelHistory.length).toBe(1); + expect(channelHistory[0]).toBe(channelId); + expect(member?.lastViewedAt).toBe(now); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(1); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(0); + expect(listenerCallback).toHaveBeenCalledTimes(0); + }); + + it('switch to dm channel in different team', async () => { + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + const tChannel = {...channel, team_id: ''}; + await operator.handleChannel({channels: [tChannel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [tChannel], myChannels: [channelMember], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: 'currentTeamId'}, {id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID, value: 'currentChannelId'}], prepareRecordsOnly: false}); + + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {models, error} = await switchToChannel(serverUrl, channelId, teamId); + listener.remove(); + + const {systemValues, teamHistory, channelHistory, member} = await queryDatabaseValues(operator.database, teamId, channelId); + + expect(error).toBeUndefined(); + expect(models?.length).toBe(5); // Viewed at, channel history, team history, currentTeamId and currentChannelId + expect(systemValues.currentTeamId).toBe(teamId); + expect(systemValues.currentChannelId).toBe(channelId); + expect(teamHistory.length).toBe(1); + expect(teamHistory[0]).toBe(teamId); + expect(channelHistory.length).toBe(1); + expect(channelHistory[0]).toBe(channelId); + expect(member?.lastViewedAt).toBe(now); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(1); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(0); + expect(listenerCallback).toHaveBeenCalledTimes(0); + }); + + it('switch to a channel from different team without passing the teamId', async () => { + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: 'currentTeamId'}, {id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID, value: 'currentChannelId'}], prepareRecordsOnly: false}); + + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {models, error} = await switchToChannel(serverUrl, channelId); + listener.remove(); + + const {systemValues, teamHistory, channelHistory, member} = await queryDatabaseValues(operator.database, teamId, channelId); + + expect(error).toBeUndefined(); + expect(models?.length).toBe(5); // Viewed at, channel history, team history, currentTeamId and currentChannelId + expect(systemValues.currentTeamId).toBe(teamId); + expect(systemValues.currentChannelId).toBe(channelId); + expect(teamHistory.length).toBe(1); + expect(teamHistory[0]).toBe(teamId); + expect(channelHistory.length).toBe(1); + expect(channelHistory[0]).toBe(channelId); + expect(member?.lastViewedAt).toBe(now); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(1); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(0); + expect(listenerCallback).toHaveBeenCalledTimes(0); + }); + + it('switch to a channel from the same team without passing a teamId', async () => { + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}, {id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID, value: 'currentChannelId'}], prepareRecordsOnly: false}); + + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {models, error} = await switchToChannel(serverUrl, channelId); + listener.remove(); + + const {systemValues, teamHistory, channelHistory, member} = await queryDatabaseValues(operator.database, teamId, channelId); + + expect(error).toBeUndefined(); + expect(models?.length).toBe(3); // Viewed at, channel history and currentChannelId + expect(systemValues.currentTeamId).toBe(teamId); + expect(systemValues.currentChannelId).toBe(channelId); + expect(teamHistory.length).toBe(0); + expect(channelHistory.length).toBe(1); + expect(channelHistory[0]).toBe(channelId); + expect(member?.lastViewedAt).toBe(now); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(1); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(0); + expect(listenerCallback).toHaveBeenCalledTimes(0); + }); + + it('switch to a DM without passing the teamId', async () => { + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + const tChannel = {...channel, team_id: ''}; + await operator.handleChannel({channels: [tChannel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [tChannel], myChannels: [channelMember], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}, {id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID, value: 'currentChannelId'}], prepareRecordsOnly: false}); + + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {models, error} = await switchToChannel(serverUrl, channelId); + listener.remove(); + + const {systemValues, teamHistory, channelHistory, member} = await queryDatabaseValues(operator.database, teamId, channelId); + + expect(error).toBeUndefined(); + expect(models?.length).toBe(3); // Viewed at, channel history and currentChannelId + expect(systemValues.currentTeamId).toBe(teamId); + expect(systemValues.currentChannelId).toBe(channelId); + expect(teamHistory.length).toBe(0); + expect(channelHistory.length).toBe(1); + expect(channelHistory[0]).toBe(channelId); + expect(member?.lastViewedAt).toBe(now); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(1); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(0); + expect(listenerCallback).toHaveBeenCalledTimes(0); + }); + + it('switch to a channel passing a wrong teamId', async () => { + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: 'currentTeamId'}, {id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID, value: 'currentChannelId'}], prepareRecordsOnly: false}); + + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {models, error} = await switchToChannel(serverUrl, channelId, 'someTeamId'); + listener.remove(); + + const {systemValues, teamHistory, channelHistory, member} = await queryDatabaseValues(operator.database, teamId, channelId); + + expect(error).toBeUndefined(); + expect(models?.length).toBe(5); // Viewed at, channel history, team history, currentTeamId and currentChannelId + expect(systemValues.currentTeamId).toBe(teamId); + expect(systemValues.currentChannelId).toBe(channelId); + expect(teamHistory.length).toBe(1); + expect(teamHistory[0]).toBe(teamId); + expect(channelHistory.length).toBe(1); + expect(channelHistory[0]).toBe(channelId); + expect(member?.lastViewedAt).toBe(now); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(1); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(0); + expect(listenerCallback).toHaveBeenCalledTimes(0); + }); + + it('switch to a DM passing a non-existing teamId', async () => { + const currentTeamId = 'ctid'; + const currentChannelId = 'ccid'; + + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + const tChannel = {...channel, team_id: ''}; + await operator.handleChannel({channels: [tChannel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [tChannel], myChannels: [channelMember], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: currentTeamId}, {id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID, value: currentChannelId}], prepareRecordsOnly: false}); + + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {error} = await switchToChannel(serverUrl, channelId, 'someTeamId'); + listener.remove(); + + const {systemValues, teamHistory, channelHistory, member} = await queryDatabaseValues(operator.database, teamId, channelId); + + expect(error).toBeTruthy(); + expect(systemValues.currentTeamId).toBe(currentTeamId); + expect(systemValues.currentChannelId).toBe(currentChannelId); + expect(teamHistory.length).toBe(0); + expect(channelHistory.length).toBe(0); + expect(member?.lastViewedAt).toBe(0); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(0); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(0); + expect(listenerCallback).toHaveBeenCalledTimes(0); + }); + + it('prepare records only does not change the database', async () => { + const currentTeamId = 'ctid'; + const currentChannelId = 'ccid'; + + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: currentTeamId}, {id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID, value: currentChannelId}], prepareRecordsOnly: false}); + + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {models, error} = await switchToChannel(serverUrl, channelId, teamId, true); + for (const model of models!) { + model.cancelPrepareUpdate(); + } + listener.remove(); + + const {systemValues, teamHistory, channelHistory, member} = await queryDatabaseValues(operator.database, teamId, channelId); + + expect(error).toBeUndefined(); + expect(models?.length).toBe(5); // Viewed at, channel history, team history, currentTeamId and currentChannelId + expect(systemValues.currentTeamId).toBe(currentTeamId); + expect(systemValues.currentChannelId).toBe(currentChannelId); + expect(teamHistory.length).toBe(0); + expect(channelHistory.length).toBe(0); + expect(member?.lastViewedAt).toBe(0); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(1); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(0); + expect(listenerCallback).toHaveBeenCalledTimes(0); + }); + + it('test behaviour when it is a tablet', async () => { + mockIsTablet.mockImplementationOnce(() => true); + + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: 'currentTeamId'}, {id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID, value: 'currentChannelId'}], prepareRecordsOnly: false}); + + const listenerCallback = jest.fn(); + const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, listenerCallback); + const {models, error} = await switchToChannel(serverUrl, channelId, teamId); + listener.remove(); + + const {systemValues, teamHistory, channelHistory, member} = await queryDatabaseValues(operator.database, teamId, channelId); + + expect(error).toBeUndefined(); + expect(models?.length).toBe(5); // Viewed at, channel history, team history, currentTeamId and currentChannelId + expect(systemValues.currentTeamId).toBe(teamId); + expect(systemValues.currentChannelId).toBe(channelId); + expect(teamHistory.length).toBe(1); + expect(teamHistory[0]).toBe(teamId); + expect(channelHistory.length).toBe(1); + expect(channelHistory[0]).toBe(channelId); + expect(member?.lastViewedAt).toBe(now); + expect(dismissAllModalsAndPopToScreen).toHaveBeenCalledTimes(0); + expect(dismissAllModalsAndPopToRoot).toHaveBeenCalledTimes(1); + expect(listenerCallback).toHaveBeenCalledTimes(1); + }); +}); diff --git a/app/actions/local/channel.ts b/app/actions/local/channel.ts index 729fddb29..ec500bc80 100644 --- a/app/actions/local/channel.ts +++ b/app/actions/local/channel.ts @@ -11,7 +11,7 @@ import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; import {prepareDeleteChannel, prepareMyChannelsForTeam, queryAllMyChannelIds, queryChannelsById, queryMyChannel} from '@queries/servers/channel'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import {prepareCommonSystemValues, PrepareCommonSystemValuesArgs, queryCommonSystemValues, queryCurrentTeamId, setCurrentChannelId} from '@queries/servers/system'; -import {addChannelToTeamHistory, addTeamToTeamHistory, removeChannelFromTeamHistory} from '@queries/servers/team'; +import {addChannelToTeamHistory, addTeamToTeamHistory, queryTeamById, removeChannelFromTeamHistory} from '@queries/servers/team'; import {queryCurrentUser} from '@queries/servers/user'; import {dismissAllModalsAndPopToRoot, dismissAllModalsAndPopToScreen} from '@screens/navigation'; import {isTablet} from '@utils/helpers'; @@ -38,31 +38,38 @@ export const switchToChannel = async (serverUrl: string, channelId: string, team if (member) { const channel: ChannelModel = await member.channel.fetch(); - const commonValues: PrepareCommonSystemValuesArgs = {}; - if (system.currentChannelId !== channelId) { - commonValues.currentChannelId = channelId; + if (!channel.teamId && teamId) { + const team = await queryTeamById(database, teamId); + if (!team) { + return {error: `team with id ${teamId} not found`}; + } } + const toTeamId = channel.teamId || teamId || system.currentTeamId; + if (isTabletDevice && system.currentChannelId !== channelId) { // On tablet, the channel is being rendered, by setting the channel to empty first we speed up // the switch by ~3x await setCurrentChannelId(operator, ''); } - if (teamId && system.currentTeamId !== teamId) { - commonValues.currentTeamId = teamId; - const history = await addTeamToTeamHistory(operator, teamId, true); + if (system.currentTeamId !== toTeamId) { + const history = await addTeamToTeamHistory(operator, toTeamId, true); models.push(...history); } - if (Object.keys(commonValues).length) { + if ((system.currentTeamId !== toTeamId) || (system.currentChannelId !== channelId)) { + const commonValues: PrepareCommonSystemValuesArgs = { + currentChannelId: system.currentChannelId === channelId ? undefined : channelId, + currentTeamId: system.currentTeamId === toTeamId ? undefined : toTeamId, + }; const common = await prepareCommonSystemValues(operator, commonValues); if (common) { models.push(...common); } } - if (system.currentChannelId !== channelId) { - const history = await addChannelToTeamHistory(operator, channel.teamId || system.currentTeamId, channelId, true); + if (system.currentChannelId !== channelId || system.currentTeamId !== toTeamId) { + const history = await addChannelToTeamHistory(operator, toTeamId, channelId, true); models.push(...history); } diff --git a/app/queries/servers/team.ts b/app/queries/servers/team.ts index 143dbc16a..8ac78db82 100644 --- a/app/queries/servers/team.ts +++ b/app/queries/servers/team.ts @@ -52,26 +52,23 @@ export const addChannelToTeamHistory = async (operator: ServerDataOperator, team }; export const queryNthLastChannelFromTeam = async (database: Database, teamId: string, n = 0) => { - let channelId = ''; + const teamChannelHistory = await queryChannelHistory(database, teamId); + if (teamChannelHistory && teamChannelHistory.length > n + 1) { + return teamChannelHistory[n]; + } + // No channel history for the team + const channel = await queryDefaultChannelForTeam(database, teamId); + return channel?.id || ''; +}; + +export const queryChannelHistory = async (database: Database, teamId: string) => { try { const teamChannelHistory = await database.get(TEAM_CHANNEL_HISTORY).find(teamId); - if (teamChannelHistory.channelIds.length > n + 1) { - channelId = teamChannelHistory.channelIds[n]; - } + return teamChannelHistory.channelIds; } catch { - //Do nothing + return []; } - - if (!channelId) { - // No channel history for the team - const channel = await queryDefaultChannelForTeam(database, teamId); - if (channel) { - channelId = channel.id; - } - } - - return channelId; }; export const removeChannelFromTeamHistory = async (operator: ServerDataOperator, teamId: string, channelId: string, prepareRecordsOnly = false) => { diff --git a/patches/@nozbe+watermelondb+0.24.0.patch b/patches/@nozbe+watermelondb+0.24.0.patch index d22967b15..9f0f9a312 100644 --- a/patches/@nozbe+watermelondb+0.24.0.patch +++ b/patches/@nozbe+watermelondb+0.24.0.patch @@ -27,10 +27,10 @@ index 1a7c99e..0cb7b87 100644 public markAsDeleted(): Promise diff --git a/node_modules/@nozbe/watermelondb/Model/index.js b/node_modules/@nozbe/watermelondb/Model/index.js -index 075a047..e5e52fa 100644 +index 075a047..5af3995 100644 --- a/node_modules/@nozbe/watermelondb/Model/index.js +++ b/node_modules/@nozbe/watermelondb/Model/index.js -@@ -78,6 +78,16 @@ var Model = /*#__PURE__*/function () { +@@ -78,6 +78,18 @@ var Model = /*#__PURE__*/function () { // database.batch() ; @@ -40,14 +40,16 @@ index 075a047..e5e52fa 100644 + } + this.__changes = null; + this._preparedState = null; -+ this._raw = this.original; ++ if (this.__original) { ++ this._raw = this.__original; ++ } + this.__original = undefined; + } + _proto.prepareUpdate = function prepareUpdate(recordUpdater = _noop.default) { var _this = this; -@@ -92,6 +102,7 @@ var Model = /*#__PURE__*/function () { +@@ -92,6 +104,7 @@ var Model = /*#__PURE__*/function () { } // Perform updates