diff --git a/app/lib/database/services/Server.ts b/app/lib/database/services/Server.ts index 9c275ed83a..ee9722a2ef 100644 --- a/app/lib/database/services/Server.ts +++ b/app/lib/database/services/Server.ts @@ -6,12 +6,11 @@ import { SERVERS_TABLE } from '../model'; const getCollection = (db: TServerDatabase) => db.get(SERVERS_TABLE); export const getServerById = async (server: string): Promise => { - const db = database.servers; - const serverCollection = getCollection(db); try { - const result = await serverCollection.find(server); - return result; + return await getCollection(database.servers).find(server); } catch { return null; } }; + +export const getAllServers = (): Promise => getCollection(database.servers).query().fetch(); diff --git a/app/lib/methods/loggedInServer.ts b/app/lib/methods/loggedInServer.ts index d6e392bbbd..6b4ef2ddb8 100644 --- a/app/lib/methods/loggedInServer.ts +++ b/app/lib/methods/loggedInServer.ts @@ -1,13 +1,10 @@ import { type TServerModel } from '../../definitions'; import { TOKEN_KEY } from '../constants/keys'; -import database from '../database'; -import { SERVERS_TABLE } from '../database/model'; +import { getAllServers } from '../database/services/Server'; import UserPreferences from './userPreferences'; -export const hasStoredLoginToken = (serverId: string): boolean => !!UserPreferences.getString(`${TOKEN_KEY}-${serverId}`); +export const isLoggedInServer = (serverId?: string | null): boolean => + !!serverId && !!UserPreferences.getString(`${TOKEN_KEY}-${serverId}`); -export const findLoggedInServer = function* findLoggedInServer(): Generator { - const serversCollection = database.servers.get(SERVERS_TABLE); - const servers = (yield serversCollection.query().fetch()) as TServerModel[]; - return servers.find(({ id }) => hasStoredLoginToken(id)); -}; +export const findLoggedInServer = async (): Promise => + (await getAllServers()).find(({ id }) => isLoggedInServer(id)); diff --git a/app/sagas/__tests__/init.fallbackServer.test.ts b/app/sagas/__tests__/init.fallbackServer.test.ts index 07f689eee0..7489942343 100644 --- a/app/sagas/__tests__/init.fallbackServer.test.ts +++ b/app/sagas/__tests__/init.fallbackServer.test.ts @@ -2,15 +2,9 @@ const FALLBACK_SERVER = 'https://fallback.rocket.chat'; const FALLBACK_VERSION = '7.0.0'; const LOGGED_OUT_SERVER = 'https://loggedout.rocket.chat'; -jest.mock('../../lib/database', () => ({ - __esModule: true, - default: { - servers: { - get: () => ({ - query: () => ({ fetch: () => Promise.resolve([{ id: FALLBACK_SERVER, version: FALLBACK_VERSION }]) }) - }) - } - } +jest.mock('../../lib/database/services/Server', () => ({ + getServerById: jest.fn(), + getAllServers: jest.fn(() => Promise.resolve([{ id: FALLBACK_SERVER, version: FALLBACK_VERSION }])) })); jest.mock('../../lib/methods/helpers/localAuthentication', () => ({ @@ -22,10 +16,12 @@ import { SERVER } from '../../actions/actionsTypes'; import { CURRENT_SERVER, TOKEN_KEY } from '../../lib/constants/keys'; import UserPreferences from '../../lib/methods/userPreferences'; import { cancelSagaTasks, createRecordingStore, flushSagaMicrotasks } from '../../lib/testUtils/sagaStore'; +import { localAuthenticate } from '../../lib/methods/helpers/localAuthentication'; import initRoot from '../init'; describe('init saga — fallback workspace', () => { beforeEach(() => { + jest.mocked(localAuthenticate).mockClear(); UserPreferences.setString(CURRENT_SERVER, LOGGED_OUT_SERVER); UserPreferences.removeItem(`${TOKEN_KEY}-${LOGGED_OUT_SERVER}`); UserPreferences.setString(`${TOKEN_KEY}-${FALLBACK_SERVER}`, 'userId'); @@ -43,6 +39,19 @@ describe('init saga — fallback workspace', () => { store.dispatch(appInit()); await flushSagaMicrotasks(); + expect(dispatchedActions.find(action => action.type === SERVER.SELECT_REQUEST)).toEqual( + expect.objectContaining({ server: FALLBACK_SERVER, version: FALLBACK_VERSION }) + ); + expect(jest.mocked(localAuthenticate)).toHaveBeenCalledWith(FALLBACK_SERVER); + }); + + it('requests the fallback workspace when the current server was cleared by a logout', async () => { + UserPreferences.removeItem(CURRENT_SERVER); + const { store, dispatchedActions } = createRecordingStore(initRoot); + + store.dispatch(appInit()); + await flushSagaMicrotasks(); + expect(dispatchedActions.find(action => action.type === SERVER.SELECT_REQUEST)).toEqual( expect.objectContaining({ server: FALLBACK_SERVER, version: FALLBACK_VERSION }) ); diff --git a/app/sagas/__tests__/init.test.ts b/app/sagas/__tests__/init.test.ts index 47de04128d..80ce61ab80 100644 --- a/app/sagas/__tests__/init.test.ts +++ b/app/sagas/__tests__/init.test.ts @@ -6,7 +6,8 @@ jest.mock('../../lib/methods/userPreferences', () => ({ })); jest.mock('../../lib/database/services/Server', () => ({ - getServerById: jest.fn() + getServerById: jest.fn(), + getAllServers: jest.fn() })); jest.mock('../../lib/methods/helpers/localAuthentication', () => ({ @@ -30,26 +31,16 @@ jest.mock('@react-native-async-storage/async-storage', () => ({ } })); -jest.mock('../../lib/database', () => ({ - __esModule: true, - default: { - servers: { - get: jest.fn() - } - } -})); - import RNBootSplash from 'react-native-bootsplash'; import { appInit, appStart } from '../../actions/app'; import { RootEnum } from '../../definitions'; import initRoot from '../init'; import UserPreferences from '../../lib/methods/userPreferences'; -import { getServerById } from '../../lib/database/services/Server'; +import { getAllServers, getServerById } from '../../lib/database/services/Server'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { DEEP_LINKING } from '../../actions/actionsTypes'; import { TOKEN_KEY } from '../../lib/constants/keys'; -import database from '../../lib/database'; import { cancelSagaTasks, createRecordingStore, flushSagaMicrotasks } from '../../lib/testUtils/sagaStore'; import type { RecordingStore } from '../../lib/testUtils/sagaStore'; @@ -65,7 +56,7 @@ describe('init saga — restore user-facing roots', () => { jest.mocked(RNBootSplash.hide).mockClear(); jest.mocked(AsyncStorage.getItem).mockResolvedValue(null as any); jest.mocked(AsyncStorage.removeItem).mockClear(); - jest.mocked(database.servers.get).mockReset(); + jest.mocked(getAllServers).mockResolvedValue([]); jest.mocked(UserPreferences.getString).mockImplementation(() => HOST); }); @@ -107,9 +98,7 @@ describe('init saga — restore user-facing roots', () => { it('lands on ROOT_OUTSIDE when neither the stored server nor any other has a token', async () => { jest.mocked(UserPreferences.getString).mockImplementation(key => (key.startsWith(`${TOKEN_KEY}-`) ? null : HOST)); - jest.mocked(database.servers.get).mockReturnValue({ - query: () => ({ fetch: () => Promise.resolve([{ id: OTHER_HOST, version: '7.0.0' }]) }) - } as any); + jest.mocked(getAllServers).mockResolvedValue([{ id: OTHER_HOST, version: '7.0.0' }] as any); const { store } = setupStore(); store.dispatch(appInit()); @@ -125,9 +114,7 @@ describe('init saga — restore user-facing roots', () => { if (key.startsWith(`${TOKEN_KEY}-`)) return null; return HOST; }); - jest.mocked(database.servers.get).mockReturnValue({ - query: () => ({ fetch: () => Promise.resolve([{ id: OTHER_HOST, version: '7.0.0' }]) }) - } as any); + jest.mocked(getAllServers).mockResolvedValue([{ id: OTHER_HOST, version: '7.0.0' }] as any); const { store } = setupStore(); store.dispatch(appInit()); diff --git a/app/sagas/init.js b/app/sagas/init.js index d354ab802e..54e0d51e9a 100644 --- a/app/sagas/init.js +++ b/app/sagas/init.js @@ -4,7 +4,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import { CURRENT_SERVER } from '../lib/constants/keys'; import UserPreferences from '../lib/methods/userPreferences'; -import { findLoggedInServer, hasStoredLoginToken } from '../lib/methods/loggedInServer'; +import { findLoggedInServer, isLoggedInServer } from '../lib/methods/loggedInServer'; import { selectServerRequest } from '../actions/server'; import { setAllPreferences } from '../actions/sortPreferences'; import { APP } from '../actions/actionsTypes'; @@ -23,19 +23,20 @@ export const initLocalSettings = function* initLocalSettings() { yield put(setAllPreferences(sortPreferences)); }; -const serverToRestore = function* serverToRestore() { - try { - const server = UserPreferences.getString(CURRENT_SERVER); - if (!server) { - return null; - } +const findServerToRestore = async () => { + const server = UserPreferences.getString(CURRENT_SERVER); + const restoredServer = isLoggedInServer(server) ? await getServerById(server) : await findLoggedInServer(); - if (!hasStoredLoginToken(server)) { - return (yield* findLoggedInServer()) || null; - } + if (restoredServer) { + await localAuthenticate(restoredServer.id); + } + + return restoredServer; +}; - yield localAuthenticate(server); - return (yield getServerById(server)) || null; +const serverToRestore = function* serverToRestore() { + try { + return (yield call(findServerToRestore)) || null; } catch (e) { log(e); return null;