diff --git a/app/actions/remote/apps.ts b/app/actions/remote/apps.ts index 725112e14..9acbfbf86 100644 --- a/app/actions/remote/apps.ts +++ b/app/actions/remote/apps.ts @@ -5,9 +5,10 @@ import {sendEphemeralPost} from '@actions/local/post'; import {AppCallResponseTypes} from '@constants/apps'; import NetworkManager from '@managers/network_manager'; import {cleanForm, createCallRequest, makeCallErrorResponse} from '@utils/apps'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import type {Client} from '@client/rest'; -import type ClientError from '@client/rest/error'; import type PostModel from '@typings/database/models/servers/post'; import type {IntlShape} from 'react-intl'; @@ -58,15 +59,9 @@ export async function handleBindingClick(serverUrl: string, binding return doAppSubmit(serverUrl, callRequest, intl); } -export async function doAppSubmit(serverUrl: string, inCall: AppCallRequest, intl: IntlShape) { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error: makeCallErrorResponse((error as ClientError).message)}; - } - +export async function doAppSubmit(serverUrl: string, inCall: AppCallRequest, intl: IntlShape): Promise<{data: AppCallResponse} | {error: AppCallResponse}> { try { + const client = NetworkManager.getClient(serverUrl); const call: AppCallRequest = { ...inCall, context: { @@ -116,23 +111,18 @@ export async function doAppSubmit(serverUrl: string, inCall: AppCal } } } catch (error) { - const errMsg = (error as ClientError).message || intl.formatMessage({ + const errMsg = getFullErrorMessage(error) || intl.formatMessage({ id: 'apps.error.responses.unexpected_error', defaultMessage: 'Received an unexpected error.', }); + logDebug('error on doAppSubmit', getFullErrorMessage(error)); return {error: makeCallErrorResponse(errMsg)}; } } export async function doAppFetchForm(serverUrl: string, call: AppCallRequest, intl: IntlShape) { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error: makeCallErrorResponse((error as ClientError).message)}; - } - try { + const client = NetworkManager.getClient(serverUrl); const res = await client.executeAppCall(call, false); const responseType = res.type || AppCallResponseTypes.OK; @@ -157,11 +147,12 @@ export async function doAppFetchForm(serverUrl: string, call: AppCa return {error: makeCallErrorResponse(errMsg)}; } } - } catch (error: any) { - const errMsg = error.message || intl.formatMessage({ + } catch (error) { + const errMsg = getFullErrorMessage(error) || intl.formatMessage({ id: 'apps.error.responses.unexpected_error', defaultMessage: 'Received an unexpected error.', }); + logDebug('error on doAppFetchForm', getFullErrorMessage(error)); return {error: makeCallErrorResponse(errMsg)}; } } @@ -170,11 +161,6 @@ export async function doAppLookup(serverUrl: string, call: AppCallR let client: Client; try { client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error: makeCallErrorResponse((error as ClientError).message)}; - } - - try { const res = await client.executeAppCall(call, false); const responseType = res.type || AppCallResponseTypes.OK; @@ -193,10 +179,11 @@ export async function doAppLookup(serverUrl: string, call: AppCallR } } } catch (error: any) { - const errMsg = error.message || intl.formatMessage({ + const errMsg = getFullErrorMessage(error) || intl.formatMessage({ id: 'apps.error.responses.unexpected_error', defaultMessage: 'Received an unexpected error.', }); + logDebug('error on doAppLookup', getFullErrorMessage(error)); return {error: makeCallErrorResponse(errMsg)}; } } diff --git a/app/actions/remote/category.ts b/app/actions/remote/category.ts index c4e92a53e..4a7342757 100644 --- a/app/actions/remote/category.ts +++ b/app/actions/remote/category.ts @@ -9,26 +9,20 @@ import NetworkManager from '@managers/network_manager'; import {getChannelCategory, queryCategoriesByTeamIds} from '@queries/servers/categories'; import {getChannelById} from '@queries/servers/channel'; import {getCurrentTeamId} from '@queries/servers/system'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import {showFavoriteChannelSnackbar} from '@utils/snack_bar'; import {forceLogoutIfNecessary} from './session'; -import type {Client} from '@client/rest'; - export type CategoriesRequest = { categories?: CategoryWithChannels[]; error?: unknown; } export const fetchCategories = async (serverUrl: string, teamId: string, prune = false, fetchOnly = false): Promise => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const {categories} = await client.getCategories('me', teamId); if (!fetchOnly) { @@ -37,26 +31,17 @@ export const fetchCategories = async (serverUrl: string, teamId: string, prune = return {categories}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchCategories', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const toggleFavoriteChannel = async (serverUrl: string, channelId: string, showSnackBar = false) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { - const {database} = operator; const channel = await getChannelById(database, channelId); if (!channel) { return {error: 'channel not found'}; @@ -109,7 +94,8 @@ export const toggleFavoriteChannel = async (serverUrl: string, channelId: string return {data: true}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on toggleFavoriteChannel', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; diff --git a/app/actions/remote/channel.ts b/app/actions/remote/channel.ts index 8f3ce7977..df4d6d34d 100644 --- a/app/actions/remote/channel.ts +++ b/app/actions/remote/channel.ts @@ -25,6 +25,7 @@ import {dismissAllModalsAndPopToRoot} from '@screens/navigation'; import EphemeralStore from '@store/ephemeral_store'; import {setTeamLoading} from '@store/team_load_store'; import {generateChannelNameFromDisplayName, getDirectChannelName, isDMorGM} from '@utils/channel'; +import {getFullErrorMessage} from '@utils/errors'; import {isTablet} from '@utils/helpers'; import {logDebug, logError, logInfo} from '@utils/log'; import {showMuteChannelSnackbar} from '@utils/snack_bar'; @@ -38,8 +39,6 @@ import {forceLogoutIfNecessary} from './session'; import {addCurrentUserToTeam, fetchTeamByName, removeCurrentUserFromTeam} from './team'; import {fetchProfilesInChannel, fetchProfilesInGroupChannels, fetchProfilesPerChannels, fetchUsersByIds, updateUsersNoLongerVisible} from './user'; -import type {Client} from '@client/rest'; -import type ClientError from '@client/rest/error'; import type {Model} from '@nozbe/watermelondb'; import type ChannelModel from '@typings/database/models/servers/channel'; import type {IntlShape} from 'react-intl'; @@ -64,10 +63,10 @@ export async function removeMemberFromChannel(serverUrl: string, channelId: stri await client.removeFromChannel(userId, channelId); await deleteChannelMembership(operator, userId, channelId); - return {error: undefined}; + return {}; } catch (error) { - logError('removeMemberFromChannel', error); - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on removeMemberFromChannel', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } @@ -94,8 +93,8 @@ export async function fetchChannelMembersByIds(serverUrl: string, channelId: str return {members}; } catch (error) { - logError('fetchChannelMembersByIds', error); - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchChannelMembersByIds', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } @@ -108,10 +107,10 @@ export async function updateChannelMemberSchemeRoles(serverUrl: string, channelI return getMemberInChannel(serverUrl, channelId, userId); } - return {error: undefined}; + return {}; } catch (error) { - logError('updateChannelMemberSchemeRoles', error); - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on updateChannelMemberSchemeRoles', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } @@ -124,10 +123,10 @@ export async function getMemberInChannel(serverUrl: string, channelId: string, u if (!fetchOnly) { updateLocalUser(serverUrl, member, userId); } - return {member, error: undefined}; + return {member}; } catch (error) { - logError('getMemberInChannel', error); - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on getMemberInChannel', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } @@ -145,19 +144,9 @@ export async function fetchChannelMemberships(serverUrl: string, channelId: stri } export async function addMembersToChannel(serverUrl: string, channelId: string, userIds: string[], postRootId = '', fetchOnly = false) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const client = NetworkManager.getClient(serverUrl); const promises = userIds.map((id) => client.addToChannel(id, channelId, postRootId)); const channelMemberships: ChannelMembership[] = await Promise.all(promises); const {users} = await fetchUsersByIds(serverUrl, userIds, true); @@ -180,51 +169,37 @@ export async function addMembersToChannel(serverUrl: string, channelId: string, } return {channelMemberships}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on addMembersToChannel', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function fetchChannelByName(serverUrl: string, teamId: string, channelName: string, fetchOnly = false) { - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const channel = await client.getChannelByName(teamId, channelName, true); if (!fetchOnly) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (operator) { - await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); - } + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); } return {channel}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchChannelByName', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function createChannel(serverUrl: string, displayName: string, purpose: string, header: string, type: ChannelType) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + EphemeralStore.creatingChannel = true; + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { - const {database} = operator; const {currentUserId, currentTeamId} = await getCommonSystemValues(database); const name = generateChannelNameFromDisplayName(displayName); const channel = { @@ -237,7 +212,6 @@ export async function createChannel(serverUrl: string, displayName: string, purp type, } as Channel; - EphemeralStore.creatingChannel = true; const channelData = await client.createChannel(channel); const member = await client.getChannelMember(channelData.id, currentUserId); @@ -259,29 +233,21 @@ export async function createChannel(serverUrl: string, displayName: string, purp EphemeralStore.creatingChannel = false; return {channel: channelData}; } catch (error) { + logDebug('error on createChannel', getFullErrorMessage(error)); EphemeralStore.creatingChannel = false; - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function patchChannel(serverUrl: string, channelPatch: Partial & {id: string}) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const channelData = await client.patchChannel(channelPatch.id, channelPatch); const models = []; - const channelInfo = (await getChannelInfo(operator.database, channelData.id)); + const channelInfo = (await getChannelInfo(database, channelData.id)); if (channelInfo && (channelInfo.purpose !== channelData.purpose || channelInfo.header !== channelData.header)) { channelInfo.prepareUpdate((v) => { v.purpose = channelData.purpose; @@ -289,7 +255,7 @@ export async function patchChannel(serverUrl: string, channelPatch: Partial { v.displayName = channelData.display_name; @@ -302,26 +268,18 @@ export async function patchChannel(serverUrl: string, channelPatch: Partial> = [{ guest_count: stats.guest_count, @@ -439,28 +380,19 @@ export async function fetchChannelStats(serverUrl: string, channelId: string, fe return {stats}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchChannelStats', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function fetchMyChannelsForTeam(serverUrl: string, teamId: string, includeDeleted = true, since = 0, fetchOnly = false, excludeDirect = false, isCRTEnabled?: boolean): Promise { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { if (!fetchOnly) { setTeamLoading(serverUrl, true); } + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const [allChannels, channelMemberships, categoriesWithOrder] = await Promise.all([ client.getMyChannels(teamId, includeDeleted, since), client.getMyChannelMembers(teamId), @@ -494,28 +426,19 @@ export async function fetchMyChannelsForTeam(serverUrl: string, teamId: string, return {channels, memberships, categories}; } catch (error) { + logDebug('error on fetchMyChannelsForTeam', getFullErrorMessage(error)); if (!fetchOnly) { setTeamLoading(serverUrl, false); } - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function fetchMyChannel(serverUrl: string, teamId: string, channelId: string, fetchOnly = false): Promise { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); - try { const [channel, member] = await Promise.all([ client.getChannel(channelId), client.getChannelMember(channelId, 'me'), @@ -530,7 +453,8 @@ export async function fetchMyChannel(serverUrl: string, teamId: string, channelI memberships: [member], }; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchMyChannel', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } @@ -617,6 +541,7 @@ export async function fetchMissingDirectChannelsInfo(serverUrl: string, directCh return {directChannels: updatedChannelsArray, users}; } catch (error) { + logDebug('error on fetchMissingDirectChannelsInfo', getFullErrorMessage(error)); return {error}; } } @@ -637,24 +562,15 @@ export async function fetchDirectChannelsInfo(serverUrl: string, directChannels: } export async function joinChannel(serverUrl: string, teamId: string, channelId?: string, channelName?: string, fetchOnly = false) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - const database = operator.database; - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - - const userId = await getCurrentUserId(database); - let member: ChannelMembership | undefined; let channel: Channel | undefined; + try { + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + + const userId = await getCurrentUserId(database); + if (channelId) { EphemeralStore.addJoiningChannel(channelId); member = await client.addToChannel(userId, channelId); @@ -668,15 +584,7 @@ export async function joinChannel(serverUrl: string, teamId: string, channelId?: member = await client.addToChannel(userId, channel.id); } } - } catch (error) { - if (channelId || channel?.id) { - EphemeralStore.removeJoiningChannel(channelId || channel!.id); - } - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); - return {error}; - } - try { if (channel && member && !fetchOnly) { fetchRolesIfNeeded(serverUrl, member.roles.split(' ')); @@ -698,9 +606,11 @@ export async function joinChannel(serverUrl: string, teamId: string, channelId?: } } } catch (error) { + logDebug('error on joinChannel', getFullErrorMessage(error)); if (channelId || channel?.id) { EphemeralStore.removeJoiningChannel(channelId || channel!.id); } + forceLogoutIfNecessary(serverUrl, error); return {error}; } @@ -712,15 +622,12 @@ export async function joinChannel(serverUrl: string, teamId: string, channelId?: } export async function joinChannelIfNeeded(serverUrl: string, channelId: string) { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - try { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const myChannel = await getMyChannel(database, channelId); if (myChannel) { - return {error: undefined}; + return {}; } return joinChannel(serverUrl, '', channelId); @@ -740,19 +647,12 @@ export async function markChannelAsRead(serverUrl: string, channelId: string, up return {}; } catch (error) { + logDebug('error on markChannelAsRead', getFullErrorMessage(error)); return {error}; } } export async function switchToChannelByName(serverUrl: string, channelName: string, teamName: string, errorHandler: (intl: IntlShape) => void, intl: IntlShape) { - let database; - try { - const result = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - database = result.database; - } catch (e) { - return {error: `${serverUrl} database not found`}; - } - const onError = (joinedTeam: boolean, teamId?: string) => { errorHandler(intl); if (joinedTeam && teamId) { @@ -762,7 +662,10 @@ export async function switchToChannelByName(serverUrl: string, channelName: stri let joinedTeam = false; let teamId = ''; + try { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + if (teamName === DeepLink.Redirect) { teamId = await getCurrentTeamId(database); } else { @@ -814,7 +717,7 @@ export async function switchToChannelByName(serverUrl: string, channelName: stri } switchToChannelById(serverUrl, channelId, teamId); - return {error: undefined}; + return {}; } catch (error) { onError(joinedTeam, teamId); return {error}; @@ -822,14 +725,8 @@ export async function switchToChannelByName(serverUrl: string, channelName: stri } export async function goToNPSChannel(serverUrl: string) { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const user = await client.getUserByUsername(General.NPS_PLUGIN_BOT_USERNAME); const {data, error} = await createDirectChannel(serverUrl, user.id); if (error || !data) { @@ -837,7 +734,8 @@ export async function goToNPSChannel(serverUrl: string) { } await switchToChannelById(serverUrl, data.id, data.team_id); } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on goToNPSChannel', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } @@ -845,20 +743,10 @@ export async function goToNPSChannel(serverUrl: string) { } export async function createDirectChannel(serverUrl: string, userId: string, displayName = '') { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - const {database} = operator; - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + EphemeralStore.creatingDMorGMTeammates = [userId]; + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const client = NetworkManager.getClient(serverUrl); const currentUser = await getCurrentUser(database); if (!currentUser) { return {error: 'Cannot get the current user'}; @@ -870,7 +758,6 @@ export async function createDirectChannel(serverUrl: string, userId: string, dis return {data: channel.toApi()}; } - EphemeralStore.creatingDMorGMTeammates = [userId]; const created = await client.createDirectChannel([userId, currentUser.id]); const profiles: UserProfile[] = []; @@ -925,40 +812,32 @@ export async function createDirectChannel(serverUrl: string, userId: string, dis fetchRolesIfNeeded(serverUrl, member.roles.split(' ')); return {data: created}; } catch (error) { + logDebug('error on createDirectChannel', getFullErrorMessage(error)); EphemeralStore.creatingDMorGMTeammates = []; - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function fetchChannels(serverUrl: string, teamId: string, page = 0, perPage: number = General.CHANNELS_CHUNK_SIZE) { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const channels = await client.getChannels(teamId, page, perPage); return {channels}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchChannels', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function makeDirectChannel(serverUrl: string, userId: string, displayName = '', shouldSwitchToChannel = true) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - try { - const currentUserId = await getCurrentUserId(operator.database); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const currentUserId = await getCurrentUserId(database); const channelName = getDirectChannelName(userId, currentUserId); - let channel: Channel|ChannelModel|undefined = await getChannelByName(operator.database, '', channelName); + let channel: Channel|ChannelModel|undefined = await getChannelByName(database, '', channelName); let result: {data?: Channel|ChannelModel; error?: any}; if (channel) { result = {data: channel}; @@ -978,38 +857,28 @@ export async function makeDirectChannel(serverUrl: string, userId: string, displ } export async function fetchArchivedChannels(serverUrl: string, teamId: string, page = 0, perPage: number = General.CHANNELS_CHUNK_SIZE) { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const channels = await client.getArchivedChannels(teamId, page, perPage); return {channels}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchArchivedChannels', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function createGroupChannel(serverUrl: string, userIds: string[]) { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + EphemeralStore.creatingDMorGMTeammates = userIds; + const client = NetworkManager.getClient(serverUrl); const {operator, database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const currentUser = await getCurrentUser(database); if (!currentUser) { return {error: 'Cannot get the current user'}; } - EphemeralStore.creatingDMorGMTeammates = userIds; + const created = await client.createGroupChannel(userIds); // Check the channel previous existency: if the channel already have @@ -1061,37 +930,30 @@ export async function createGroupChannel(serverUrl: string, userIds: string[]) { fetchRolesIfNeeded(serverUrl, member.roles.split(' ')); return {data: created}; } catch (error) { + logDebug('error on createGroupChannel', getFullErrorMessage(error)); EphemeralStore.creatingDMorGMTeammates = []; - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function fetchSharedChannels(serverUrl: string, teamId: string, page = 0, perPage: number = General.CHANNELS_CHUNK_SIZE) { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } try { + const client = NetworkManager.getClient(serverUrl); const channels = await client.getSharedChannels(teamId, page, perPage); return {channels}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchSharedChannels', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function makeGroupChannel(serverUrl: string, userIds: string[], shouldSwitchToChannel = true) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - try { - const currentUserId = await getCurrentUserId(operator.database); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const currentUserId = await getCurrentUserId(database); const result = await createGroupChannel(serverUrl, [currentUserId, ...userIds]); const channel = result.data; @@ -1106,33 +968,23 @@ export async function makeGroupChannel(serverUrl: string, userIds: string[], sho } export async function getChannelMemberCountsByGroup(serverUrl: string, channelId: string, includeTimezones: boolean) { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const channelMemberCountsByGroup = await client.getChannelMemberCountsByGroup(channelId, includeTimezones); return {channelMemberCountsByGroup}; } catch (error) { + logDebug('error on getChannelMemberCountsByGroup', getFullErrorMessage(error)); return {error}; } } export async function getChannelTimezones(serverUrl: string, channelId: string) { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const channelTimezones = await client.getChannelTimezones(channelId); return {channelTimezones}; } catch (error) { + logDebug('error on getChannelTimezones', getFullErrorMessage(error)); return {error}; } } @@ -1166,12 +1018,8 @@ export async function switchToChannelById(serverUrl: string, channelId: string, } export async function switchToPenultimateChannel(serverUrl: string, teamId?: string) { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - try { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const teamIdToUse = teamId || await getCurrentTeamId(database); const channelId = await getNthLastChannelFromTeam(database, teamIdToUse, 1); return switchToChannelById(serverUrl, channelId); @@ -1181,12 +1029,8 @@ export async function switchToPenultimateChannel(serverUrl: string, teamId?: str } export async function switchToLastChannel(serverUrl: string, teamId?: string) { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - try { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const teamIdToUse = teamId || await getCurrentTeamId(database); const channelId = await getNthLastChannelFromTeam(database, teamIdToUse); return switchToChannelById(serverUrl, channelId); @@ -1196,79 +1040,45 @@ export async function switchToLastChannel(serverUrl: string, teamId?: string) { } export async function searchChannels(serverUrl: string, term: string, teamId: string, isSearch = false) { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const autoCompleteFunc = isSearch ? client.autocompleteChannelsForSearch : client.autocompleteChannels; const channels = await autoCompleteFunc(teamId, term); return {channels}; } catch (error) { + logDebug('error on searchChannels', getFullErrorMessage(error)); return {error}; } } export async function fetchChannelById(serverUrl: string, id: string) { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const channel = await client.getChannel(id); return {channel}; } catch (error) { + logDebug('error on fetchChannelById', getFullErrorMessage(error)); return {error}; } } export async function searchAllChannels(serverUrl: string, term: string, archivedOnly = false) { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const myTeamIds = await queryMyTeams(database).fetchIds(); const channels = await client.searchAllChannels(term, myTeamIds, archivedOnly); return {channels}; } catch (error) { + logDebug('error on searchAllChannels', getFullErrorMessage(error)); return {error}; } } export const updateChannelNotifyProps = async (serverUrl: string, channelId: string, props: Partial) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - try { + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const userId = await getCurrentUserId(database); const notifyProps = {...props, channel_id: channelId, user_id: userId} as ChannelNotifyProps & {channel_id: string; user_id: string}; @@ -1278,18 +1088,15 @@ export const updateChannelNotifyProps = async (serverUrl: string, channelId: str notifyProps, }; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on updateChannelNotifyProps', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const toggleMuteChannel = async (serverUrl: string, channelId: string, showSnackBar = false) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - try { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const channelSettings = await queryMyChannelSettingsByIds(database, [channelId]).fetch(); const myChannelSetting = channelSettings?.[0]; const mark_unread = myChannelSetting.notifyProps?.mark_unread === 'mention' ? 'all' : 'mention'; @@ -1317,22 +1124,11 @@ export const toggleMuteChannel = async (serverUrl: string, channelId: string, sh }; export const archiveChannel = async (serverUrl: string, channelId: string) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - - try { - const {database} = operator; - const config = await getConfig(database); EphemeralStore.addArchivingChannel(channelId); + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const config = await getConfig(database); await client.deleteChannel(channelId); if (config?.ExperimentalViewArchivedChannels === 'true') { await setChannelDeleteAt(serverUrl, channelId, Date.now()); @@ -1340,9 +1136,10 @@ export const archiveChannel = async (serverUrl: string, channelId: string) => { removeCurrentUserFromChannel(serverUrl, channelId); } - return {error: undefined}; + return {}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on archiveChannel', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } finally { EphemeralStore.removeArchivingChannel(channelId); @@ -1350,25 +1147,15 @@ export const archiveChannel = async (serverUrl: string, channelId: string) => { }; export const unarchiveChannel = async (serverUrl: string, channelId: string) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { EphemeralStore.addArchivingChannel(channelId); + const client = NetworkManager.getClient(serverUrl); await client.unarchiveChannel(channelId); await setChannelDeleteAt(serverUrl, channelId, 0); - return {error: undefined}; + return {}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on unarchiveChannel', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } finally { EphemeralStore.removeArchivingChannel(channelId); @@ -1376,20 +1163,9 @@ export const unarchiveChannel = async (serverUrl: string, channelId: string) => }; export const convertChannelToPrivate = async (serverUrl: string, channelId: string) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - - try { - const {database} = operator; + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const channel = await getChannelById(database, channelId); if (channel) { EphemeralStore.addConvertingChannel(channelId); @@ -1401,9 +1177,10 @@ export const convertChannelToPrivate = async (serverUrl: string, channelId: stri }); await operator.batchRecords([channel], 'convertChannelToPrivate'); } - return {error: undefined}; + return {}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on convertChannelToPrivate', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } finally { EphemeralStore.removeConvertingChannel(channelId); diff --git a/app/actions/remote/command.ts b/app/actions/remote/command.ts index e95576f69..9360f62b5 100644 --- a/app/actions/remote/command.ts +++ b/app/actions/remote/command.ts @@ -14,26 +14,29 @@ import {getChannelById} from '@queries/servers/channel'; import {getConfig, getCurrentTeamId} from '@queries/servers/system'; import {showAppForm} from '@screens/navigation'; import {handleDeepLink, matchDeepLink} from '@utils/deep_link'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import {tryOpenURL} from '@utils/url'; import type {Client} from '@client/rest'; import type {IntlShape} from 'react-intl'; -export const executeCommand = async (serverUrl: string, intl: IntlShape, message: string, channelId: string, rootId?: string): Promise<{data?: CommandResponse; error?: string | {message: string}}> => { +export const executeCommand = async (serverUrl: string, intl: IntlShape, message: string, channelId: string, rootId?: string): Promise<{data?: CommandResponse; error?: unknown}> => { const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; if (!operator) { return {error: `${serverUrl} database not found`}; } + const {database} = operator; let client: Client; try { client = NetworkManager.getClient(serverUrl); } catch (error) { - return {error: error as ClientErrorProps}; + return {error}; } - const channel = await getChannelById(operator.database, channelId); - const teamId = channel?.teamId || (await getCurrentTeamId(operator.database)); + const channel = await getChannelById(database, channelId); + const teamId = channel?.teamId || (await getCurrentTeamId(database)); const args: CommandArgs = { channel_id: channelId, @@ -68,7 +71,8 @@ export const executeCommand = async (serverUrl: string, intl: IntlShape, message try { data = await client.executeCommand(msg, args); } catch (error) { - return {error: error as ClientErrorProps}; + logDebug('error on executeCommand', getFullErrorMessage(error)); + return {error}; } if (data?.trigger_id) { //eslint-disable-line camelcase @@ -89,14 +93,14 @@ const executeAppCommand = async (serverUrl: string, intl: IntlShape, parser: App } const res = await doAppSubmit(serverUrl, creq, intl); - if (res.error) { - const errorResponse = res.error as AppCallResponse; + if ('error' in res) { + const errorResponse = res.error; return createErrorMessage(errorResponse.text || intl.formatMessage({ id: 'apps.error.unknown', defaultMessage: 'Unknown error.', })); } - const callResp = res.data as AppCallResponse; + const callResp = res.data; switch (callResp.type) { case AppCallResponseTypes.OK: @@ -158,31 +162,22 @@ export const handleGotoLocation = async (serverUrl: string, intl: IntlShape, loc return {data: true}; }; -export const fetchCommands = async (serverUrl: string, teamId: string) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error: error as ClientErrorProps}; - } +export const fetchCommands = async (serverUrl: string, teamId: string): Promise<{commands: Command[]} | {error: unknown}> => { try { + const client = NetworkManager.getClient(serverUrl); return {commands: await client.getCommandsList(teamId)}; } catch (error) { - return {error: error as ClientErrorProps}; + logDebug('error on fetchCommands', getFullErrorMessage(error)); + return {error}; } }; -export const fetchSuggestions = async (serverUrl: string, term: string, teamId: string, channelId: string, rootId?: string) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error: error as ClientErrorProps}; - } - +export const fetchSuggestions = async (serverUrl: string, term: string, teamId: string, channelId: string, rootId?: string): Promise<{suggestions: AutocompleteSuggestion[]} | {error: unknown}> => { try { + const client = NetworkManager.getClient(serverUrl); return {suggestions: await client.getCommandAutocompleteSuggestionsList(term, teamId, channelId, rootId)}; } catch (error) { - return {error: error as ClientErrorProps}; + logDebug('error on fetchSuggestions', getFullErrorMessage(error)); + return {error}; } }; diff --git a/app/actions/remote/custom_emoji.ts b/app/actions/remote/custom_emoji.ts index a2bec37a6..3a3094200 100644 --- a/app/actions/remote/custom_emoji.ts +++ b/app/actions/remote/custom_emoji.ts @@ -7,23 +7,14 @@ import DatabaseManager from '@database/manager'; import {debounce} from '@helpers/api/general'; import NetworkManager from '@managers/network_manager'; import {queryCustomEmojisByName} from '@queries/servers/custom_emoji'; - -import type {Client} from '@client/rest'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; export const fetchCustomEmojis = async (serverUrl: string, page = 0, perPage = General.PAGE_SIZE_DEFAULT, sort = Emoji.SORT_BY_NAME) => { - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - try { const data = await client.getCustomEmojis(page, perPage, sort); await operator.handleCustomEmojis({ emojis: data, @@ -32,29 +23,20 @@ export const fetchCustomEmojis = async (serverUrl: string, page = 0, perPage = G return {data}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchCustomEmojis', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const searchCustomEmojis = async (serverUrl: string, term: string) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - try { + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const data = await client.searchCustomEmoji(term); if (data.length) { const names = data.map((c) => c.name); - const exist = await queryCustomEmojisByName(operator.database, names).fetch(); + const exist = await queryCustomEmojisByName(database, names).fetch(); const existingNames = new Set(exist.map((e) => e.name)); const emojis = data.filter((d) => !existingNames.has(d.name)); await operator.handleCustomEmojis({ @@ -64,31 +46,22 @@ export const searchCustomEmojis = async (serverUrl: string, term: string) => { } return {data}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on searchCustomEmojis', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; const names = new Set(); const debouncedFetchEmojiByNames = debounce(async (serverUrl: string) => { - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - const promises: Array> = []; - for (const name of names) { - promises.push(client.getCustomEmojiByName(name)); - } - - try { + const promises: Array> = []; + for (const name of names) { + promises.push(client.getCustomEmojiByName(name)); + } const emojisResult = await Promise.allSettled(promises); const emojis = emojisResult.reduce((result, e) => { if (e.status === 'fulfilled') { @@ -99,8 +72,9 @@ const debouncedFetchEmojiByNames = debounce(async (serverUrl: string) => { if (emojis.length) { await operator.handleCustomEmojis({emojis, prepareRecordsOnly: false}); } - return {error: undefined}; + return {}; } catch (error) { + logDebug('error on debouncedFetchEmojiByNames', getFullErrorMessage(error)); return {error}; } }, 200, false, () => { diff --git a/app/actions/remote/entry/common.ts b/app/actions/remote/entry/common.ts index 760054eb2..dff04e578 100644 --- a/app/actions/remote/entry/common.ts +++ b/app/actions/remote/entry/common.ts @@ -26,11 +26,11 @@ import {getConfig, getCurrentChannelId, getCurrentTeamId, getPushVerificationSta import {deleteMyTeams, getAvailableTeamIds, getTeamChannelHistory, queryMyTeams, queryMyTeamsByIds, queryTeamsById} from '@queries/servers/team'; import NavigationStore from '@store/navigation_store'; import {isDMorGM, sortChannelsByDisplayName} from '@utils/channel'; +import {getFullErrorMessage, isErrorWithStatusCode} from '@utils/errors'; import {isTablet} from '@utils/helpers'; import {logDebug} from '@utils/log'; import {processIsCRTEnabled} from '@utils/thread'; -import type ClientError from '@client/rest/error'; import type {Database, Model} from '@nozbe/watermelondb'; export type AppEntryData = { @@ -45,7 +45,7 @@ export type AppEntryData = { } export type AppEntryError = { - error: Error | ClientError | string; + error: unknown; } export type EntryResponse = { @@ -74,8 +74,8 @@ export const teamsToRemove = async (serverUrl: string, removeTeamIds?: string[]) if (!operator) { return []; } - const {database} = operator; + if (removeTeamIds?.length) { // Immediately delete myTeams so that the UI renders only teams the user is a member of. const removeMyTeams = await queryMyTeamsByIds(database, removeTeamIds).fetch(); @@ -95,7 +95,6 @@ export const entryRest = async (serverUrl: string, teamId?: string, channelId?: if (!operator) { return {error: `${serverUrl} database not found`}; } - const {database} = operator; const lastDisconnectedAt = since || await getWebSocketLastDisconnected(database); @@ -106,8 +105,8 @@ export const entryRest = async (serverUrl: string, teamId?: string, channelId?: } const {initialTeamId, teamData, chData, prefData, meData, removeTeamIds, removeChannelIds, isCRTEnabled} = fetchedData; - const chError = chData?.error as ClientError | undefined; - if (chError?.status_code === 403) { + const chError = chData?.error; + if (isErrorWithStatusCode(chError) && chError.status_code === 403) { // if the user does not have appropriate permissions, which means the user those not belong to the team, // we set it as there is no errors, so that the teams and others can be properly handled chData!.error = undefined; @@ -209,8 +208,8 @@ export const fetchAppEntryData = async (serverUrl: string, sinceArg: number, ini } const inTeam = teamData.teams?.find((t) => t.id === initialTeamId); - const chError = chData?.error as ClientError | undefined; - if ((!inTeam && !teamData.error) || chError?.status_code === 403) { + const chError = chData?.error; + if ((!inTeam && !teamData.error) || (isErrorWithStatusCode(chError) && chError.status_code === 403)) { // User is no longer a member of the current team if (!removeTeamIds.includes(initialTeamId)) { removeTeamIds.push(initialTeamId); @@ -254,8 +253,8 @@ export const fetchAlternateTeamData = async ( for (const teamId of availableTeamIds) { // eslint-disable-next-line no-await-in-loop chData = await fetchMyChannelsForTeam(serverUrl, teamId, includeDeleted, since, fetchOnly, false, isCRTEnabled); - const chError = chData.error as ClientError | undefined; - if (chError?.status_code === 403) { + const chError = chData.error; + if (isErrorWithStatusCode(chError) && chError.status_code === 403) { removeTeamIds.push(teamId); } else { initialTeamId = teamId; @@ -357,50 +356,38 @@ export async function restDeferredAppEntryActions( } export const registerDeviceToken = async (serverUrl: string) => { - let client; try { - client = NetworkManager.getClient(serverUrl); + const client = NetworkManager.getClient(serverUrl); + + const deviceToken = await getDeviceToken(); + if (deviceToken) { + client.attachDevice(deviceToken); + } + return {}; } catch (error) { + logDebug('error on registerDeviceToken', getFullErrorMessage(error)); return {error}; } - - const deviceToken = await getDeviceToken(); - if (deviceToken) { - client.attachDevice(deviceToken); - } - - return {error: undefined}; }; export async function verifyPushProxy(serverUrl: string) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } - - const {database} = operator; - - const ppVerification = await getPushVerificationStatus(database); - if ( - ppVerification !== PUSH_PROXY_STATUS_UNKNOWN && - ppVerification !== '' - ) { - return; - } - const deviceId = await getDeviceToken(); if (!deviceId) { return; } - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (err) { - return; - } + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { + const ppVerification = await getPushVerificationStatus(database); + if ( + ppVerification !== PUSH_PROXY_STATUS_UNKNOWN && + ppVerification !== '' + ) { + return; + } + + const client = NetworkManager.getClient(serverUrl); const response = await client.ping(deviceId); const canReceiveNotifications = response?.data?.CanReceiveNotifications; switch (canReceiveNotifications) { @@ -412,7 +399,9 @@ export async function verifyPushProxy(serverUrl: string) { default: operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.PUSH_VERIFICATION_STATUS, value: PUSH_PROXY_STATUS_VERIFIED}], prepareRecordsOnly: false}); } - } catch (err) { + } catch (error) { + logDebug('error on verifyPushProxy', getFullErrorMessage(error)); + // Do nothing } } diff --git a/app/actions/remote/entry/gql_common.ts b/app/actions/remote/entry/gql_common.ts index 90751367a..acecb84d0 100644 --- a/app/actions/remote/entry/gql_common.ts +++ b/app/actions/remote/entry/gql_common.ts @@ -17,6 +17,7 @@ import {queryAllChannels, queryAllChannelsForTeam} from '@queries/servers/channe import {prepareModels, truncateCrtRelatedTables} from '@queries/servers/entry'; import {getHasCRTChanged} from '@queries/servers/preference'; import {getConfig, getIsDataRetentionEnabled} from '@queries/servers/system'; +import {getFullErrorMessage} from '@utils/errors'; import {filterAndTransformRoles, getMemberChannelsFromGQLQuery, getMemberTeamsFromGQLQuery, gqlToClientChannelMembership, gqlToClientPreference, gqlToClientSidebarCategory, gqlToClientTeamMembership, gqlToClientUser} from '@utils/graphql'; import {logDebug} from '@utils/log'; import {processIsCRTEnabled} from '@utils/thread'; @@ -24,7 +25,6 @@ import {processIsCRTEnabled} from '@utils/thread'; import {teamsToRemove, FETCH_UNREADS_TIMEOUT, entryRest, type EntryResponse, entryInitialChannelId, restDeferredAppEntryActions, getRemoveTeamIds} from './common'; import type {MyChannelsRequest} from '@actions/remote/channel'; -import type ClientError from '@client/rest/error'; import type {Database} from '@nozbe/watermelondb'; import type ChannelModel from '@typings/database/models/servers/channel'; @@ -141,7 +141,7 @@ const getChannelData = async (serverUrl: string, initialTeamId: string, userId: const request = exclude ? gqlOtherChannels : gqlEntryChannels; response = await request(serverUrl, initialTeamId); } catch (error) { - return {error: (error as ClientError).message}; + return {error: getFullErrorMessage(error)}; } if ('error' in response) { @@ -175,7 +175,7 @@ export const entryGQL = async (serverUrl: string, currentTeamId?: string, curren try { response = await gqlEntry(serverUrl); } catch (error) { - return {error: (error as ClientError).message}; + return {error: getFullErrorMessage(error)}; } if ('error' in response) { diff --git a/app/actions/remote/entry/login.ts b/app/actions/remote/entry/login.ts index 6d45f02ef..6206e03e3 100644 --- a/app/actions/remote/entry/login.ts +++ b/app/actions/remote/entry/login.ts @@ -10,7 +10,7 @@ type AfterLoginArgs = { serverUrl: string; } -export async function loginEntry({serverUrl}: AfterLoginArgs): Promise<{error?: any}> { +export async function loginEntry({serverUrl}: AfterLoginArgs): Promise<{error?: unknown}> { const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; if (!operator) { return {error: `${serverUrl} database not found`}; diff --git a/app/actions/remote/entry/notification.ts b/app/actions/remote/entry/notification.ts index 442aeea61..180637564 100644 --- a/app/actions/remote/entry/notification.ts +++ b/app/actions/remote/entry/notification.ts @@ -15,24 +15,25 @@ import {getCurrentTeamId} from '@queries/servers/system'; import {getMyTeamById} from '@queries/servers/team'; import {getIsCRTEnabled} from '@queries/servers/thread'; import EphemeralStore from '@store/ephemeral_store'; +import {isErrorWithStatusCode} from '@utils/errors'; import {emitNotificationError} from '@utils/notification'; import {setThemeDefaults, updateThemeIfNeeded} from '@utils/theme'; -import type ClientError from '@client/rest/error'; import type MyChannelModel from '@typings/database/models/servers/my_channel'; import type MyTeamModel from '@typings/database/models/servers/my_team'; import type PostModel from '@typings/database/models/servers/post'; export async function pushNotificationEntry(serverUrl: string, notification: NotificationData) { + // We only reach this point if we have a channel Id in the notification payload + const channelId = notification.channel_id!; + const rootId = notification.root_id!; + const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; if (!operator) { return {error: `${serverUrl} database not found`}; } - - // We only reach this point if we have a channel Id in the notification payload - const channelId = notification.channel_id!; - const rootId = notification.root_id!; const {database} = operator; + const currentTeamId = await getCurrentTeamId(database); const currentServerUrl = await DatabaseManager.getActiveServerUrl(); @@ -65,7 +66,7 @@ export async function pushNotificationEntry(serverUrl: string, notification: Not if (!myTeam) { const resp = await fetchMyTeam(serverUrl, teamId); if (resp.error) { - if ((resp.error as ClientError).status_code === 403) { + if (isErrorWithStatusCode(resp.error) && resp.error.status_code === 403) { emitNotificationError('Team'); } else { emitNotificationError('Connection'); @@ -78,7 +79,7 @@ export async function pushNotificationEntry(serverUrl: string, notification: Not if (!myChannel) { const resp = await fetchMyChannel(serverUrl, teamId, channelId); if (resp.error) { - if ((resp.error as ClientError).status_code === 403) { + if (isErrorWithStatusCode(resp.error) && resp.error.status_code === 403) { emitNotificationError('Channel'); } else { emitNotificationError('Connection'); diff --git a/app/actions/remote/file.ts b/app/actions/remote/file.ts index e0f9235ac..14461f108 100644 --- a/app/actions/remote/file.ts +++ b/app/actions/remote/file.ts @@ -3,12 +3,12 @@ import {DOWNLOAD_TIMEOUT} from '@constants/network'; import NetworkManager from '@managers/network_manager'; +import {getFullErrorMessage} from '@utils/errors'; import {logDebug} from '@utils/log'; import {forceLogoutIfNecessary} from './session'; import type {Client} from '@client/rest'; -import type ClientError from '@client/rest/error'; import type {ClientResponse, ClientResponseError} from '@mattermost/react-native-network-client'; export const downloadFile = (serverUrl: string, fileId: string, desitnation: string) => { // Let it throw and handle it accordingly @@ -30,29 +30,23 @@ export const uploadFile = ( onError: (response: ClientResponseError) => void = () => {/*Do Nothing*/}, skipBytes = 0, ) => { - let client: Client; try { - client = NetworkManager.getClient(serverUrl); + const client = NetworkManager.getClient(serverUrl); return {cancel: client.uploadPostAttachment(file, channelId, onProgress, onComplete, onError, skipBytes)}; } catch (error) { - logDebug('uploadFile', error); - return {error: error as ClientError}; + logDebug('error on uploadFile', getFullErrorMessage(error)); + return {error}; } }; export const fetchPublicLink = async (serverUrl: string, fileId: string) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error: error as ClientError}; - } - try { + const client = NetworkManager.getClient(serverUrl); const publicLink = await client!.getFilePublicLink(fileId); return publicLink; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchPublicLink', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; diff --git a/app/actions/remote/general.ts b/app/actions/remote/general.ts index c3d4b755b..d076eeec5 100644 --- a/app/actions/remote/general.ts +++ b/app/actions/remote/general.ts @@ -8,6 +8,7 @@ import {t} from '@i18n'; import NetworkManager from '@managers/network_manager'; import {getDeviceToken} from '@queries/app/global'; import {getExpandedLinks, getPushVerificationStatus} from '@queries/servers/system'; +import {getFullErrorMessage} from '@utils/errors'; import {logDebug} from '@utils/log'; import {forceLogoutIfNecessary} from './session'; @@ -69,7 +70,7 @@ export const doPing = async (serverUrl: string, verifyPushProxy: boolean, timeou return {error: {intl: pingError}}; } } catch (error) { - logDebug('Server ping threw an exception', error); + logDebug('Server ping threw an exception', getFullErrorMessage(error)); NetworkManager.invalidateClient(serverUrl); return {error: {intl: pingError}}; } @@ -82,29 +83,19 @@ export const doPing = async (serverUrl: string, verifyPushProxy: boolean, timeou canReceiveNotifications = PUSH_PROXY_RESPONSE_VERIFIED; } - return {canReceiveNotifications, error: undefined}; + return {canReceiveNotifications}; } - return {error: undefined}; + return {}; }; export const getRedirectLocation = async (serverUrl: string, link: string) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const expandedLink = await client.getRedirectLocation(link); if (expandedLink?.location) { - const storedLinks = await getExpandedLinks(operator.database); + const storedLinks = await getExpandedLinks(database); storedLinks[link] = expandedLink.location; const expanded: IdValue = { id: SYSTEM_IDENTIFIERS.EXPANDED_LINKS, @@ -118,7 +109,8 @@ export const getRedirectLocation = async (serverUrl: string, link: string) => { return {expandedLink}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on getRedirectLocation', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; diff --git a/app/actions/remote/groups.ts b/app/actions/remote/groups.ts index aceb67560..205c38b75 100644 --- a/app/actions/remote/groups.ts +++ b/app/actions/remote/groups.ts @@ -5,6 +5,8 @@ import DatabaseManager from '@database/manager'; import NetworkManager from '@managers/network_manager'; import {getChannelById} from '@queries/servers/channel'; import {getTeamById} from '@queries/servers/team'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import {forceLogoutIfNecessary} from './session'; @@ -20,7 +22,8 @@ export const fetchGroup = async (serverUrl: string, id: string, fetchOnly = fals // Save locally return operator.handleGroups({groups: [group], prepareRecordsOnly: fetchOnly}); } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchGroup', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; @@ -37,7 +40,8 @@ export const fetchGroupsForAutocomplete = async (serverUrl: string, query: strin return operator.handleGroups({groups: response, prepareRecordsOnly: fetchOnly}); } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchGroupsForAutocomplete', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; @@ -62,7 +66,8 @@ export const fetchGroupsByNames = async (serverUrl: string, names: string[], fet return operator.handleGroups({groups, prepareRecordsOnly: fetchOnly}); } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchGroupsByNames', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; @@ -88,7 +93,8 @@ export const fetchGroupsForChannel = async (serverUrl: string, channelId: string return {groups, groupChannels}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchGroupsForChannel', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; @@ -115,6 +121,7 @@ export const fetchGroupsForTeam = async (serverUrl: string, teamId: string, fetc return {groups, groupTeams}; } catch (error) { + logDebug('error on fetchGroupsForTeam', getFullErrorMessage(error)); return {error}; } }; @@ -141,36 +148,25 @@ export const fetchGroupsForMember = async (serverUrl: string, userId: string, fe return {groups, groupMemberships}; } catch (error) { + logDebug('error on fetchGroupsForMember', getFullErrorMessage(error)); return {error}; } }; export const fetchFilteredTeamGroups = async (serverUrl: string, searchTerm: string, teamId: string) => { - try { - const groups = await fetchGroupsForTeam(serverUrl, teamId); - - if (groups && Array.isArray(groups)) { - return groups.filter((g) => g.name.toLowerCase().includes(searchTerm.toLowerCase())); - } - - throw groups.error; - } catch (error) { - return {error}; + const res = await fetchGroupsForTeam(serverUrl, teamId); + if ('error' in res) { + return {error: res.error}; } + return res.groups.filter((g) => g.name.toLowerCase().includes(searchTerm.toLowerCase())); }; export const fetchFilteredChannelGroups = async (serverUrl: string, searchTerm: string, channelId: string) => { - try { - const groups = await fetchGroupsForChannel(serverUrl, channelId); - - if (groups && Array.isArray(groups)) { - return groups.filter((g) => g.name.toLowerCase().includes(searchTerm.toLowerCase())); - } - - throw groups.error; - } catch (error) { - return {error}; + const res = await fetchGroupsForChannel(serverUrl, channelId); + if ('error' in res) { + return {error: res.error}; } + return res.groups.filter((g) => g.name.toLowerCase().includes(searchTerm.toLowerCase())); }; export const fetchGroupsForTeamIfConstrained = async (serverUrl: string, teamId: string, fetchOnly = false) => { diff --git a/app/actions/remote/integrations.ts b/app/actions/remote/integrations.ts index f60651c51..c9bd84051 100644 --- a/app/actions/remote/integrations.ts +++ b/app/actions/remote/integrations.ts @@ -6,45 +6,31 @@ import DatabaseManager from '@database/manager'; import IntegrationsMananger from '@managers/integrations_manager'; import NetworkManager from '@managers/network_manager'; import {getCurrentChannelId, getCurrentTeamId} from '@queries/servers/system'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import {forceLogoutIfNecessary} from './session'; -import type {Client} from '@client/rest'; - export const submitInteractiveDialog = async (serverUrl: string, submission: DialogSubmission) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - submission.channel_id = await getCurrentChannelId(database); - submission.team_id = await getCurrentTeamId(database); - - try { + submission.channel_id = await getCurrentChannelId(database); + submission.team_id = await getCurrentTeamId(database); const data = await client.submitInteractiveDialog(submission); return {data}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on submitInteractiveDialog', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const postActionWithCookie = async (serverUrl: string, postId: string, actionId: string, actionCookie: string, selectedOption = '') => { - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); - try { const data = await client.doPostActionWithCookie(postId, actionId, actionCookie, selectedOption); if (data?.trigger_id) { IntegrationsMananger.getManager(serverUrl)?.setTriggerId(data.trigger_id); @@ -52,7 +38,8 @@ export const postActionWithCookie = async (serverUrl: string, postId: string, ac return {data}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on postActionWithCookie', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; diff --git a/app/actions/remote/notifications.ts b/app/actions/remote/notifications.ts index b4ad2fdfb..f90bd1a87 100644 --- a/app/actions/remote/notifications.ts +++ b/app/actions/remote/notifications.ts @@ -25,19 +25,15 @@ import {processPostsFetched} from '@utils/post'; import type {Model} from '@nozbe/watermelondb'; const fetchNotificationData = async (serverUrl: string, notification: NotificationWithData, skipEvents = false) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; + const channelId = notification.payload?.channel_id; + + if (!channelId) { + return {error: 'No chanel Id was specified'}; } try { - const channelId = notification.payload?.channel_id; + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - if (!channelId) { - return {error: 'No chanel Id was specified'}; - } - - const {database} = operator; const currentTeamId = await getCurrentTeamId(database); let teamId = notification.payload?.team_id; let isDirectChannel = false; @@ -96,7 +92,7 @@ const fetchNotificationData = async (serverUrl: string, notification: Notificati } return {}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; @@ -184,16 +180,12 @@ export const backgroundNotification = async (serverUrl: string, notification: No }; export const openNotification = async (serverUrl: string, notification: NotificationWithData) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } + EphemeralStore.setNotificationTapped(true); + const channelId = notification.payload!.channel_id!; + const rootId = notification.payload!.root_id!; try { - EphemeralStore.setNotificationTapped(true); - const {database} = operator; - const channelId = notification.payload!.channel_id!; - const rootId = notification.payload!.root_id!; + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const isCRTEnabled = await getIsCRTEnabled(database); const isThreadNotification = isCRTEnabled && Boolean(rootId); @@ -232,7 +224,7 @@ export const openNotification = async (serverUrl: string, notification: Notifica } return switchToChannelById(serverUrl, channelId, teamId); } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; diff --git a/app/actions/remote/nps.ts b/app/actions/remote/nps.ts index 2b7da4ea1..3dcfe795c 100644 --- a/app/actions/remote/nps.ts +++ b/app/actions/remote/nps.ts @@ -3,6 +3,8 @@ import {General} from '@constants'; import NetworkManager from '@managers/network_manager'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; export const isNPSEnabled = async (serverUrl: string) => { try { @@ -15,6 +17,7 @@ export const isNPSEnabled = async (serverUrl: string) => { } return false; } catch (error) { + logDebug('error on isNPSEnabled', getFullErrorMessage(error)); return false; } }; @@ -25,6 +28,7 @@ export const giveFeedbackAction = async (serverUrl: string) => { const post = await client.npsGiveFeedbackAction(); return {post}; } catch (error) { + logDebug('error on giveFeedbackAction', getFullErrorMessage(error)); return {error}; } }; diff --git a/app/actions/remote/permalink.ts b/app/actions/remote/permalink.ts index 19ef622cc..4fa6ce606 100644 --- a/app/actions/remote/permalink.ts +++ b/app/actions/remote/permalink.ts @@ -9,12 +9,9 @@ import {displayPermalink} from '@utils/permalink'; import type TeamModel from '@typings/database/models/servers/team'; export const showPermalink = async (serverUrl: string, teamName: string, postId: string, openAsPermalink = true) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - try { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + let name = teamName; let team: TeamModel | undefined; if (!name || name === DeepLink.Redirect) { @@ -26,7 +23,7 @@ export const showPermalink = async (serverUrl: string, teamName: string, postId: await displayPermalink(name, postId, openAsPermalink); - return {error: undefined}; + return {}; } catch (error) { return {error}; } diff --git a/app/actions/remote/post.ts b/app/actions/remote/post.ts index 6e9e1ff96..e826df679 100644 --- a/app/actions/remote/post.ts +++ b/app/actions/remote/post.ts @@ -24,7 +24,7 @@ import {queryAllUsers} from '@queries/servers/user'; import EphemeralStore from '@store/ephemeral_store'; import {setFetchingThreadState} from '@store/fetching_thread_store'; import {getValidEmojis, matchEmoticons} from '@utils/emoji/helpers'; -import {isServerError} from '@utils/errors'; +import {getFullErrorMessage, isServerError} from '@utils/errors'; import {logDebug, logError} from '@utils/log'; import {processPostsFetched} from '@utils/post'; import {getPostIdsForCombinedUserActivityPost} from '@utils/post_list'; @@ -59,6 +59,7 @@ export async function createPost(serverUrl: string, post: Partial, files: if (!operator) { return {error: `${serverUrl} database not found`}; } + const {database} = operator; let client: Client; try { @@ -67,8 +68,6 @@ export async function createPost(serverUrl: string, post: Partial, files: return {error}; } - const {database} = operator; - const currentUserId = await getCurrentUserId(database); const timestamp = Date.now(); const pendingPostId = post.pending_post_id || `${currentUserId}:${timestamp}`; @@ -135,7 +134,7 @@ export async function createPost(serverUrl: string, post: Partial, files: try { created = await client.createPost(newPost); } catch (error) { - logDebug('Error sending a post', error); + logDebug('Error sending a post', getFullErrorMessage(error)); const errorPost = { ...newPost, id: pendingPostId, @@ -204,6 +203,7 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => { if (!operator) { return {error: `${serverUrl} database not found`}; } + const {database} = operator; let client: Client; try { @@ -212,7 +212,6 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => { return {error}; } - const {database} = operator; const isCRTEnabled = await getIsCRTEnabled(database); try { @@ -255,6 +254,7 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => { } await operator.batchRecords(models, 'retryFailedPost - success update'); } catch (error) { + logDebug('error on retryFailedPost', getFullErrorMessage(error)); if (isServerError(error) && ( error.server_error_id === ServerErrors.DELETED_ROOT_POST_ERROR || error.server_error_id === ServerErrors.TOWN_SQUARE_READ_ONLY_ERROR || @@ -274,7 +274,7 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => { return {error}; } - return {error: undefined}; + return {}; }; export const fetchPostsForCurrentChannel = async (serverUrl: string) => { @@ -326,7 +326,7 @@ export async function fetchPostsForChannel(serverUrl: string, channelId: string, return {posts: data.posts, order: data.order, authors, actionType, previousPostId: data.previousPostId}; } catch (error) { - logError('FetchPostsForChannel', error); + logDebug('error on fetchPostsForChannel', getFullErrorMessage(error)); return {error}; } finally { if (!fetchOnly) { @@ -336,20 +336,14 @@ export async function fetchPostsForChannel(serverUrl: string, channelId: string, } export const fetchPostsForUnreadChannels = async (serverUrl: string, channels: Channel[], memberships: ChannelMembership[], excludeChannelId?: string) => { - try { - const promises = []; - for (const member of memberships) { - const channel = channels.find((c) => c.id === member.channel_id); - if (channel && (channel.total_msg_count - member.msg_count) > 0 && channel.id !== excludeChannelId) { - promises.push(fetchPostsForChannel(serverUrl, channel.id)); - } + const promises = []; + for (const member of memberships) { + const channel = channels.find((c) => c.id === member.channel_id); + if (channel && (channel.total_msg_count - member.msg_count) > 0 && channel.id !== excludeChannelId) { + promises.push(fetchPostsForChannel(serverUrl, channel.id)); } - await Promise.all(promises); - } catch (error) { - return {error}; } - - return {error: undefined}; + await Promise.all(promises); }; export async function fetchPosts(serverUrl: string, channelId: string, page = 0, perPage = General.POST_CHUNK_SIZE, fetchOnly = false): Promise { @@ -357,9 +351,9 @@ export async function fetchPosts(serverUrl: string, channelId: string, page = 0, if (!fetchOnly) { EphemeralStore.addLoadingMessagesForChannel(serverUrl, channelId); } - const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const {operator, database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const client = NetworkManager.getClient(serverUrl); - const isCRTEnabled = await getIsCRTEnabled(operator.database); + const isCRTEnabled = await getIsCRTEnabled(database); const data = await client.getPosts(channelId, page, perPage, isCRTEnabled, isCRTEnabled); const result = processPostsFetched(data); if (!fetchOnly && result.posts.length) { @@ -388,7 +382,8 @@ export async function fetchPosts(serverUrl: string, channelId: string, page = 0, } return result; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchPosts', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } finally { if (!fetchOnly) { @@ -398,23 +393,13 @@ export async function fetchPosts(serverUrl: string, channelId: string, page = 0, } export async function fetchPostsBefore(serverUrl: string, channelId: string, postId: string, perPage = General.POST_CHUNK_SIZE, fetchOnly = false) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { if (!fetchOnly) { EphemeralStore.addLoadingMessagesForChannel(serverUrl, channelId); } - const isCRTEnabled = await getIsCRTEnabled(operator.database); + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const isCRTEnabled = await getIsCRTEnabled(database); const data = await client.getPostsBefore(channelId, postId, 0, perPage, isCRTEnabled, isCRTEnabled); const result = processPostsFetched(data); @@ -448,6 +433,7 @@ export async function fetchPostsBefore(serverUrl: string, channelId: string, pos } return result; } catch (error) { + logDebug('error on fetchPostsBefore', getFullErrorMessage(error)); forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); return {error}; } finally { @@ -458,23 +444,14 @@ export async function fetchPostsBefore(serverUrl: string, channelId: string, pos } export async function fetchPostsSince(serverUrl: string, channelId: string, since: number, fetchOnly = false): Promise { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { if (!fetchOnly) { EphemeralStore.addLoadingMessagesForChannel(serverUrl, channelId); } - const isCRTEnabled = await getIsCRTEnabled(operator.database); + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + + const isCRTEnabled = await getIsCRTEnabled(database); const data = await client.getPostsSince(channelId, since, isCRTEnabled, isCRTEnabled); const result = await processPostsFetched(data); if (!fetchOnly) { @@ -503,11 +480,13 @@ export async function fetchPostsSince(serverUrl: string, channelId: string, sinc } return result; } catch (error) { + logDebug('error on fetchPostsSince', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + return {error}; + } finally { if (!fetchOnly) { EphemeralStore.stopLoadingMessagesForChannel(serverUrl, channelId); } - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); - return {error}; } } @@ -537,7 +516,6 @@ export const fetchPostAuthors = async (serverUrl: string, posts: Post[], fetchOn userIdsToLoad.add(user_id); } } - const promises: Array> = []; if (userIdsToLoad.size) { promises.push(client.getProfilesByIds(Array.from(userIdsToLoad))); @@ -569,29 +547,20 @@ export const fetchPostAuthors = async (serverUrl: string, posts: Post[], fetchOn return {authors: [] as UserProfile[]}; } catch (error) { - logError('FETCH AUTHORS ERROR', error); - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchPostAuthors', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export async function fetchPostThread(serverUrl: string, postId: string, options?: FetchPaginatedThreadOptions, fetchOnly = false) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + setFetchingThreadState(postId, true); + const client = NetworkManager.getClient(serverUrl); - setFetchingThreadState(postId, true); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { - const isCRTEnabled = await getIsCRTEnabled(operator.database); + const isCRTEnabled = await getIsCRTEnabled(database); // Not doing any version check as server versions below 6.7 will ignore the additional params from the client. const data = await client.getPostThread(postId, { @@ -630,26 +599,18 @@ export async function fetchPostThread(serverUrl: string, postId: string, options setFetchingThreadState(postId, false); return {posts: extractRecordsForTable(posts, MM_TABLES.SERVER.POST)}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchPostThread', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); setFetchingThreadState(postId, false); return {error}; } } export async function fetchPostsAround(serverUrl: string, channelId: string, postId: string, perPage = General.POST_AROUND_CHUNK_SIZE, isCRTEnabled = false) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const [after, post, before] = await Promise.all([ client.getPostsAfter(channelId, postId, 0, perPage, isCRTEnabled, isCRTEnabled), client.getPostThread(postId, { @@ -706,27 +667,18 @@ export async function fetchPostsAround(serverUrl: string, channelId: string, pos return {posts: extractRecordsForTable(posts, MM_TABLES.SERVER.POST)}; } catch (error) { - logError('FETCH POSTS AROUND ERROR', error); - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchPostsAround', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function fetchMissingChannelsFromPosts(serverUrl: string, posts: Post[], fetchOnly = false) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { - const channelIds = new Set(await queryAllMyChannel(operator.database).fetchIds()); + const channelIds = new Set(await queryAllMyChannel(database).fetchIds()); const channelPromises: Array> = []; const userPromises: Array> = []; @@ -743,7 +695,7 @@ export async function fetchMissingChannelsFromPosts(serverUrl: string, posts: Po const channelMemberships = await Promise.all(userPromises); if (!fetchOnly && channels.length && channelMemberships.length) { - const isCRTEnabled = await getIsCRTEnabled(operator.database); + const isCRTEnabled = await getIsCRTEnabled(database); const modelPromises = prepareMissingChannelsForAllTeams(operator, channels, channelMemberships, isCRTEnabled); if (modelPromises.length) { const channelModelsArray = await Promise.all(modelPromises); @@ -766,25 +718,16 @@ export async function fetchMissingChannelsFromPosts(serverUrl: string, posts: Po channelMemberships, }; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchMissingChannelsFromPosts', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function fetchPostById(serverUrl: string, postId: string, fetchOnly = false) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const post = await client.getPost(postId); if (!fetchOnly) { const models: Model[] = []; @@ -805,7 +748,7 @@ export async function fetchPostById(serverUrl: string, postId: string, fetchOnly models.push(...users); } - const isCRTEnabled = await getIsCRTEnabled(operator.database); + const isCRTEnabled = await getIsCRTEnabled(database); if (isCRTEnabled) { const threadModels = await prepareThreadsFromReceivedPosts(operator, [post], false); if (threadModels?.length) { @@ -818,25 +761,17 @@ export async function fetchPostById(serverUrl: string, postId: string, fetchOnly return {post}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchPostById', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export const togglePinPost = async (serverUrl: string, postId: string) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const post = await getPostById(database, postId); if (post) { const isPinned = post.isPinned; @@ -851,25 +786,15 @@ export const togglePinPost = async (serverUrl: string, postId: string) => { } return {post}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on togglePinPost', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const deletePost = async (serverUrl: string, postToDelete: PostModel | Post) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); if (postToDelete.type === Post.POST_TYPES.COMBINED_USER_ACTIVITY && postToDelete.props?.system_post_ids) { const systemPostIds = getPostIdsForCombinedUserActivityPost(postToDelete.id); const promises = systemPostIds.map((id) => client.deletePost(id)); @@ -881,24 +806,17 @@ export const deletePost = async (serverUrl: string, postToDelete: PostModel | Po const post = await removePost(serverUrl, postToDelete); return {post}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on deletePost', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const markPostAsUnread = async (serverUrl: string, postId: string) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const [userId, post] = await Promise.all([getCurrentUserId(database), getPostById(database, postId)]); if (post && userId) { await client.markPostAsUnread(userId, postId); @@ -922,33 +840,22 @@ export const markPostAsUnread = async (serverUrl: string, postId: string) => { const messageCount = totalMessages - messages; await markChannelAsUnread(serverUrl, channelId, messageCount, mentionCount, post.createAt); - return { - post, - }; + return {post}; } } - return { - post, - }; + return {post}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on markPostAsUnread', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const editPost = async (serverUrl: string, postId: string, postMessage: string) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const post = await getPostById(database, postId); if (post) { const {update_at, edit_at, message: updatedMessage} = await client.patchPost({message: postMessage, id: postId}); @@ -960,29 +867,20 @@ export const editPost = async (serverUrl: string, postId: string, postMessage: s }); }); } - return { - post, - }; + return {post}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on editPost', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export async function fetchSavedPosts(serverUrl: string, teamId?: string, channelId?: string, page?: number, perPage?: number) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { - const userId = await getCurrentUserId(operator.database); + const userId = await getCurrentUserId(database); const data = await client.getSavedPosts(userId, channelId, teamId, page, perPage); const posts = data.posts || {}; const order = data.order || []; @@ -1010,7 +908,7 @@ export async function fetchSavedPosts(serverUrl: string, teamId?: string, channe } if (channels?.length && channelMemberships?.length) { - const isCRTEnabled = await getIsCRTEnabled(operator.database); + const isCRTEnabled = await getIsCRTEnabled(database); const channelPromises = prepareMissingChannelsForAllTeams(operator, channels, channelMemberships, isCRTEnabled); if (channelPromises.length) { promises.push(...channelPromises); @@ -1027,7 +925,7 @@ export async function fetchSavedPosts(serverUrl: string, teamId?: string, channe }), ); - const isCRTEnabled = await getIsCRTEnabled(operator.database); + const isCRTEnabled = await getIsCRTEnabled(database); if (isCRTEnabled) { promises.push(prepareThreadsFromReceivedPosts(operator, postsArray, false)); } @@ -1047,24 +945,17 @@ export async function fetchSavedPosts(serverUrl: string, teamId?: string, channe posts: postsArray, }; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchSavedPosts', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function fetchPinnedPosts(serverUrl: string, channelId: string) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const data = await client.getPinnedPosts(channelId); const posts = data.posts || {}; const order = data.order || []; @@ -1078,7 +969,6 @@ export async function fetchPinnedPosts(serverUrl: string, channelId: string) { } const promises: Array> = []; - const {database} = operator; const isCRTEnabled = await getIsCRTEnabled(database); const {authors} = await fetchPostAuthors(serverUrl, postsArray, true); @@ -1129,7 +1019,8 @@ export async function fetchPinnedPosts(serverUrl: string, channelId: string) { posts: postsArray, }; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchPinnedPosts', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } diff --git a/app/actions/remote/preference.ts b/app/actions/remote/preference.ts index 6f1f4a650..c69207baa 100644 --- a/app/actions/remote/preference.ts +++ b/app/actions/remote/preference.ts @@ -12,6 +12,8 @@ import {truncateCrtRelatedTables} from '@queries/servers/entry'; import {querySavedPostsPreferences} from '@queries/servers/preference'; import {getCurrentUserId} from '@queries/servers/system'; import EphemeralStore from '@store/ephemeral_store'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import {getUserIdFromChannelName} from '@utils/user'; import {forceLogoutIfNecessary} from './session'; @@ -22,42 +24,32 @@ export type MyPreferencesRequest = { }; export const fetchMyPreferences = async (serverUrl: string, fetchOnly = false): Promise => { - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const preferences = await client.getMyPreferences(); if (!fetchOnly) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (operator) { - await operator.handlePreferences({ - prepareRecordsOnly: false, - preferences, - sync: true, - }); - } + await operator.handlePreferences({ + prepareRecordsOnly: false, + preferences, + sync: true, + }); } return {preferences}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchMyPreferences', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const saveFavoriteChannel = async (serverUrl: string, channelId: string, isFavorite: boolean) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - try { - const userId = await getCurrentUserId(operator.database); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const userId = await getCurrentUserId(database); const favPref: PreferenceType = { category: Preferences.CATEGORIES.FAVORITE_CHANNEL, name: channelId, @@ -71,13 +63,10 @@ export const saveFavoriteChannel = async (serverUrl: string, channelId: string, }; export const savePostPreference = async (serverUrl: string, postId: string) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - try { - const userId = await getCurrentUserId(operator.database); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + + const userId = await getCurrentUserId(database); const pref: PreferenceType = { user_id: userId, category: Preferences.CATEGORIES.SAVED_POST, @@ -91,20 +80,11 @@ export const savePostPreference = async (serverUrl: string, postId: string) => { }; export const savePreference = async (serverUrl: string, preferences: PreferenceType[]) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { - const userId = await getCurrentUserId(operator.database); + const userId = await getCurrentUserId(database); client.savePreferences(userId, preferences); await operator.handlePreferences({ preferences, @@ -113,7 +93,8 @@ export const savePreference = async (serverUrl: string, preferences: PreferenceT return {preferences}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on savePreference', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; @@ -141,18 +122,15 @@ export const deleteSavedPost = async (serverUrl: string, postId: string) => { preference: pref, }; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on deleteSavedPost', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const setDirectChannelVisible = async (serverUrl: string, channelId: string, visible = true) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - try { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const channel = await getChannelById(database, channelId); if (channel?.type === General.DM_CHANNEL || channel?.type === General.GM_CHANNEL) { const userId = await getCurrentUserId(database); @@ -168,9 +146,9 @@ export const setDirectChannelVisible = async (serverUrl: string, channelId: stri return savePreference(serverUrl, [pref]); } - return {error: undefined}; + return {}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; diff --git a/app/actions/remote/reactions.ts b/app/actions/remote/reactions.ts index cfb20f0b0..d710fc868 100644 --- a/app/actions/remote/reactions.ts +++ b/app/actions/remote/reactions.ts @@ -8,30 +8,22 @@ import {getRecentPostsInChannel, getRecentPostsInThread} from '@queries/servers/ import {queryReaction} from '@queries/servers/reaction'; import {getCurrentChannelId, getCurrentUserId} from '@queries/servers/system'; import {getEmojiFirstAlias} from '@utils/emoji/helpers'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import {forceLogoutIfNecessary} from './session'; -import type {Client} from '@client/rest'; import type {Model} from '@nozbe/watermelondb'; import type PostModel from '@typings/database/models/servers/post'; export async function addReaction(serverUrl: string, postId: string, emojiName: string) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { - const currentUserId = await getCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(database); const emojiAlias = getEmojiFirstAlias(emojiName); - const reacted = await queryReaction(operator.database, emojiAlias, postId, currentUserId).fetchCount() > 0; + const reacted = await queryReaction(database, emojiAlias, postId, currentUserId).fetchCount() > 0; if (!reacted) { const reaction = await client.addReaction(currentUserId, postId, emojiAlias); const models: Model[] = []; @@ -64,25 +56,17 @@ export async function addReaction(serverUrl: string, postId: string, emojiName: } as Reaction, }; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on addReaction', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export const removeReaction = async (serverUrl: string, postId: string, emojiName: string) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const currentUserId = await getCurrentUserId(database); const emojiAlias = getEmojiFirstAlias(emojiName); await client.removeReaction(currentUserId, postId, emojiAlias); @@ -98,24 +82,21 @@ export const removeReaction = async (serverUrl: string, postId: string, emojiNam return {reaction}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on removeReaction', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const handleReactionToLatestPost = async (serverUrl: string, emojiName: string, add: boolean, rootId?: string) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - try { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); let posts: PostModel[]; if (rootId) { - posts = await getRecentPostsInThread(operator.database, rootId); + posts = await getRecentPostsInThread(database, rootId); } else { - const channelId = await getCurrentChannelId(operator.database); - posts = await getRecentPostsInChannel(operator.database, channelId); + const channelId = await getCurrentChannelId(database); + posts = await getRecentPostsInChannel(database, channelId); } if (add) { diff --git a/app/actions/remote/retry.ts b/app/actions/remote/retry.ts index 835e7bf06..6be03390e 100644 --- a/app/actions/remote/retry.ts +++ b/app/actions/remote/retry.ts @@ -30,7 +30,6 @@ export async function retryInitialTeamAndChannel(serverUrl: string) { if (!operator) { return {error: `${serverUrl} database not found`}; } - const {database} = operator; try { @@ -139,7 +138,6 @@ export async function retryInitialChannel(serverUrl: string, teamId: string) { if (!operator) { return {error: `${serverUrl} database not found`}; } - const {database} = operator; try { diff --git a/app/actions/remote/role.ts b/app/actions/remote/role.ts index b166d40aa..ecbc29bd8 100644 --- a/app/actions/remote/role.ts +++ b/app/actions/remote/role.ts @@ -4,6 +4,8 @@ import DatabaseManager from '@database/manager'; import NetworkManager from '@managers/network_manager'; import {queryRoles} from '@queries/servers/role'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import {forceLogoutIfNecessary} from './session'; @@ -17,43 +19,29 @@ export const fetchRolesIfNeeded = async (serverUrl: string, updatedRoles: string return {roles: []}; } - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - let database; - let operator; - try { - const result = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - database = result.database; - operator = result.operator; - } catch (e) { - return {error: `${serverUrl} database not found`}; - } + let newRoles; + if (force) { + newRoles = updatedRoles; + } else { + const existingRoles = await queryRoles(database).fetch(); - let newRoles; - if (force) { - newRoles = updatedRoles; - } else { - const existingRoles = await queryRoles(database).fetch(); + const roleNames = new Set(existingRoles.map((role) => { + return role.name; + })); - const roleNames = new Set(existingRoles.map((role) => { - return role.name; - })); + newRoles = updatedRoles.filter((newRole) => { + return !roleNames.has(newRole); + }); + } - newRoles = updatedRoles.filter((newRole) => { - return !roleNames.has(newRole); - }); - } + if (!newRoles.length) { + return {roles: []}; + } - if (!newRoles.length) { - return {roles: []}; - } - - try { const roles = await client.getRolesByNames(newRoles); if (!fetchOnly) { await operator.handleRole({ @@ -64,7 +52,8 @@ export const fetchRolesIfNeeded = async (serverUrl: string, updatedRoles: string return {roles}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchRolesIfNeeded', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; diff --git a/app/actions/remote/search.ts b/app/actions/remote/search.ts index 8523b67b7..e870b3b28 100644 --- a/app/actions/remote/search.ts +++ b/app/actions/remote/search.ts @@ -7,7 +7,8 @@ import NetworkManager from '@managers/network_manager'; import {prepareMissingChannelsForAllTeams} from '@queries/servers/channel'; import {getIsCRTEnabled, prepareThreadsFromReceivedPosts} from '@queries/servers/thread'; import {getCurrentUser} from '@queries/servers/user'; -import {logError} from '@utils/log'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import {fetchPostAuthors, fetchMissingChannelsFromPosts} from './post'; import {forceLogoutIfNecessary} from './session'; @@ -48,7 +49,7 @@ export async function fetchRecentMentions(serverUrl: string): Promise => { try { - const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const client = NetworkManager.getClient(serverUrl); let postsArray: Post[] = []; @@ -60,7 +61,7 @@ export const searchPosts = async (serverUrl: string, teamId: string, params: Pos const promises: Array> = []; postsArray = order.map((id) => posts[id]); if (postsArray.length) { - const isCRTEnabled = await getIsCRTEnabled(operator.database); + const isCRTEnabled = await getIsCRTEnabled(database); if (isCRTEnabled) { promises.push(prepareThreadsFromReceivedPosts(operator, postsArray, false)); } @@ -109,8 +110,8 @@ export const searchPosts = async (serverUrl: string, teamId: string, params: Pos posts: postsArray, }; } catch (error) { - logError('Failed: searchPosts', error); - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on searchPosts', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; @@ -129,7 +130,8 @@ export const searchFiles = async (serverUrl: string, teamId: string, params: Fil const channels = [...new Set(allChannelIds)]; return {files, channels}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on searchFiles', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; diff --git a/app/actions/remote/session.ts b/app/actions/remote/session.ts index d586feb82..467e2b5d6 100644 --- a/app/actions/remote/session.ts +++ b/app/actions/remote/session.ts @@ -14,13 +14,13 @@ import {getServerDisplayName} from '@queries/app/servers'; import {getCurrentUserId, getExpiredSession} from '@queries/servers/system'; import {getCurrentUser} from '@queries/servers/user'; import EphemeralStore from '@store/ephemeral_store'; +import {getFullErrorMessage, isErrorWithStatusCode, isErrorWithUrl} from '@utils/errors'; import {logWarning, logError, logDebug} from '@utils/log'; import {scheduleExpiredNotification} from '@utils/notification'; import {getCSRFFromCookie} from '@utils/security'; import {loginEntry} from './entry'; -import type ClientError from '@client/rest/error'; import type {LoginArgs} from '@typings/database/database'; const HTTP_UNAUTHORIZED = 401; @@ -44,7 +44,7 @@ export const addPushProxyVerificationStateFromLogin = async (serverUrl: string) logDebug('error setting the push proxy verification state on login', error); } }; -export const forceLogoutIfNecessary = async (serverUrl: string, err: ClientErrorProps) => { +export const forceLogoutIfNecessary = async (serverUrl: string, err: unknown) => { const database = DatabaseManager.serverDatabases[serverUrl]?.database; if (!database) { return {error: `${serverUrl} database not found`}; @@ -52,7 +52,7 @@ export const forceLogoutIfNecessary = async (serverUrl: string, err: ClientError const currentUserId = await getCurrentUserId(database); - if ('status_code' in err && err.status_code === HTTP_UNAUTHORIZED && err?.url?.indexOf('/login') === -1 && currentUserId) { + if (isErrorWithStatusCode(err) && err.status_code === HTTP_UNAUTHORIZED && isErrorWithUrl(err) && err.url?.indexOf('/login') === -1 && currentUserId) { await logout(serverUrl); } @@ -69,9 +69,9 @@ export const fetchSessions = async (serverUrl: string, currentUserId: string) => try { return await client.getSessions(currentUserId); - } catch (e) { - logError('fetchSessions', e); - await forceLogoutIfNecessary(serverUrl, e as ClientError); + } catch (error) { + logDebug('error on fetchSessions', getFullErrorMessage(error)); + await forceLogoutIfNecessary(serverUrl, error); } return undefined; @@ -86,14 +86,8 @@ export const login = async (serverUrl: string, {ldapOnly = false, loginId, mfaTo return {error: 'App database not found.', failed: true}; } - let client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error: error as Error, failed: true}; - } - try { + const client = NetworkManager.getClient(serverUrl); deviceToken = await getDeviceToken(); user = await client.login( loginId, @@ -123,16 +117,17 @@ export const login = async (serverUrl: string, {ldapOnly = false, loginId, mfaTo const csrfToken = await getCSRFFromCookie(serverUrl); client.setCSRFToken(csrfToken); } catch (error) { - return {error: error as Error, failed: true}; + logDebug('error on login', getFullErrorMessage(error)); + return {error, failed: true}; } try { await addPushProxyVerificationStateFromLogin(serverUrl); const {error} = await loginEntry({serverUrl}); await DatabaseManager.setActiveServerDatabase(serverUrl); - return {error: error as ClientError, failed: false}; + return {error, failed: false}; } catch (error) { - return {error: error as ClientError, failed: false}; + return {error, failed: false}; } }; @@ -143,7 +138,7 @@ export const logout = async (serverUrl: string, skipServerLogout = false, remove await client.logout(); } catch (error) { // We want to log the user even if logging out from the server failed - logWarning('An error occurred logging out from the server', serverUrl, error); + logWarning('An error occurred logging out from the server', serverUrl, getFullErrorMessage(error)); } } @@ -203,50 +198,34 @@ export const scheduleSessionNotification = async (serverUrl: string) => { } } catch (e) { logError('scheduleExpiredNotification', e); - await forceLogoutIfNecessary(serverUrl, e as ClientError); + await forceLogoutIfNecessary(serverUrl, e); } }; export const sendPasswordResetEmail = async (serverUrl: string, email: string) => { - let client; try { - client = NetworkManager.getClient(serverUrl); + const client = NetworkManager.getClient(serverUrl); + const response = await client.sendPasswordResetEmail(email); + return {status: response.status}; } catch (error) { + logDebug('error on sendPasswordResetEmail', getFullErrorMessage(error)); return {error}; } - - let response; - try { - response = await client.sendPasswordResetEmail(email); - } catch (error) { - return {error}; - } - return { - status: response.status, - error: undefined, - }; }; export const ssoLogin = async (serverUrl: string, serverDisplayName: string, serverIdentifier: string, bearerToken: string, csrfToken: string): Promise => { - let user; - const database = DatabaseManager.appDatabase?.database; if (!database) { return {error: 'App database not found', failed: true}; } - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error: error as Error, failed: true}; - } + const client = NetworkManager.getClient(serverUrl); - client.setBearerToken(bearerToken); - client.setCSRFToken(csrfToken); + client.setBearerToken(bearerToken); + client.setCSRFToken(csrfToken); - // Setting up active database for this SSO login flow - try { + // Setting up active database for this SSO login flow const server = await DatabaseManager.createServerDatabase({ config: { dbName: serverUrl, @@ -255,7 +234,7 @@ export const ssoLogin = async (serverUrl: string, serverDisplayName: string, ser displayName: serverDisplayName, }, }); - user = await client.getMe(); + const user = await client.getMe(); await server?.operator.handleUsers({users: [user], prepareRecordsOnly: false}); await server?.operator.handleSystem({ systems: [{ @@ -264,17 +243,18 @@ export const ssoLogin = async (serverUrl: string, serverDisplayName: string, ser }], prepareRecordsOnly: false, }); - } catch (e) { - return {error: e as ClientError, failed: true}; + } catch (error) { + logDebug('error on ssoLogin', getFullErrorMessage(error)); + return {error, failed: true}; } try { await addPushProxyVerificationStateFromLogin(serverUrl); const {error} = await loginEntry({serverUrl}); await DatabaseManager.setActiveServerDatabase(serverUrl); - return {error: error as ClientError, failed: false}; + return {error, failed: false}; } catch (error) { - return {error: error as ClientError, failed: false}; + return {error, failed: false}; } }; diff --git a/app/actions/remote/systems.ts b/app/actions/remote/systems.ts index d8280b194..0a4c8a8b8 100644 --- a/app/actions/remote/systems.ts +++ b/app/actions/remote/systems.ts @@ -6,8 +6,8 @@ import {forceLogoutIfNecessary} from '@actions/remote/session'; import DatabaseManager from '@database/manager'; import NetworkManager from '@managers/network_manager'; import {getCurrentUserId} from '@queries/servers/system'; - -import type ClientError from '@client/rest/error'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; export type ConfigAndLicenseRequest = { config?: ClientConfig; @@ -23,51 +23,36 @@ export type DataRetentionPoliciesRequest = { } export const fetchDataRetentionPolicy = async (serverUrl: string, fetchOnly = false): Promise => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } + const {data: globalPolicy, error: globalPolicyError} = await fetchGlobalDataRetentionPolicy(serverUrl); + const {data: teamPolicies, error: teamPoliciesError} = await fetchAllGranularDataRetentionPolicies(serverUrl); + const {data: channelPolicies, error: channelPoliciesError} = await fetchAllGranularDataRetentionPolicies(serverUrl, true); - try { - const {data: globalPolicy, error: globalPolicyError} = await fetchGlobalDataRetentionPolicy(serverUrl); - const {data: teamPolicies, error: teamPoliciesError} = await fetchAllGranularDataRetentionPolicies(serverUrl); - const {data: channelPolicies, error: channelPoliciesError} = await fetchAllGranularDataRetentionPolicies(serverUrl, true); - - const hasError = globalPolicyError || teamPoliciesError || channelPoliciesError; - if (hasError) { - return hasError; - } - - const data = { - globalPolicy, - teamPolicies: teamPolicies as TeamDataRetentionPolicy[], - channelPolicies: channelPolicies as ChannelDataRetentionPolicy[], - }; - - if (!fetchOnly) { - await storeDataRetentionPolicies(serverUrl, data); - } - - return data; - } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + const error = globalPolicyError || teamPoliciesError || channelPoliciesError; + if (error) { return {error}; } + + const data = { + globalPolicy, + teamPolicies: teamPolicies as TeamDataRetentionPolicy[], + channelPolicies: channelPolicies as ChannelDataRetentionPolicy[], + }; + + if (!fetchOnly) { + await storeDataRetentionPolicies(serverUrl, data); + } + + return data; }; export const fetchGlobalDataRetentionPolicy = async (serverUrl: string): Promise<{data?: GlobalDataRetentionPolicy; error?: unknown}> => { - let client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const data = await client.getGlobalDataRetentionPolicy(); return {data}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchGlobalDataRetentionPolicy', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; @@ -78,43 +63,31 @@ export const fetchAllGranularDataRetentionPolicies = async ( page = 0, policies: Array = [], ): Promise<{data?: Array; error?: unknown}> => { - let client; try { - client = NetworkManager.getClient(serverUrl); + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + + const currentUserId = await getCurrentUserId(database); + let data; + if (isChannel) { + data = await client.getChannelDataRetentionPolicies(currentUserId, page); + } else { + data = await client.getTeamDataRetentionPolicies(currentUserId, page); + } + policies.push(...data.policies); + if (policies.length < data.total_count) { + await fetchAllGranularDataRetentionPolicies(serverUrl, isChannel, page + 1, policies); + } + return {data: policies}; } catch (error) { + logDebug('error on fetchAllGranularDataRetentionPolicies', getFullErrorMessage(error)); return {error}; } - - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - const {database} = operator; - - const currentUserId = await getCurrentUserId(database); - let data; - if (isChannel) { - data = await client.getChannelDataRetentionPolicies(currentUserId, page); - } else { - data = await client.getTeamDataRetentionPolicies(currentUserId, page); - } - policies.push(...data.policies); - if (policies.length < data.total_count) { - await fetchAllGranularDataRetentionPolicies(serverUrl, isChannel, page + 1, policies); - } - return {data: policies}; }; export const fetchConfigAndLicense = async (serverUrl: string, fetchOnly = false): Promise => { - let client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const [config, license]: [ClientConfig, ClientLicense] = await Promise.all([ client.getClientConfigOld(), client.getClientLicenseOld(), @@ -126,7 +99,8 @@ export const fetchConfigAndLicense = async (serverUrl: string, fetchOnly = false return {config, license}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchConfigAndLicense', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; diff --git a/app/actions/remote/team.ts b/app/actions/remote/team.ts index c7342e5c4..06de9530f 100644 --- a/app/actions/remote/team.ts +++ b/app/actions/remote/team.ts @@ -16,6 +16,7 @@ import {addTeamToTeamHistory, prepareDeleteTeam, prepareMyTeams, getNthLastChann import {dismissAllModalsAndPopToRoot} from '@screens/navigation'; import EphemeralStore from '@store/ephemeral_store'; import {setTeamLoading} from '@store/team_load_store'; +import {getFullErrorMessage} from '@utils/errors'; import {isTablet} from '@utils/helpers'; import {logDebug} from '@utils/log'; @@ -26,7 +27,6 @@ import {fetchRolesIfNeeded} from './role'; import {forceLogoutIfNecessary} from './session'; import type {Client} from '@client/rest'; -import type ClientError from '@client/rest/error'; import type {Model} from '@nozbe/watermelondb'; export type MyTeamsRequest = { @@ -52,15 +52,11 @@ export async function addCurrentUserToTeam(serverUrl: string, teamId: string, fe } export async function addUserToTeam(serverUrl: string, teamId: string, userId: string, fetchOnly = false) { - let client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - let loadEventSent = false; try { + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + EphemeralStore.startAddingToTeam(teamId); const team = await client.getTeam(teamId); const member = await client.addToTeam(teamId, userId); @@ -71,45 +67,40 @@ export async function addUserToTeam(serverUrl: string, teamId: string, userId: s fetchRolesIfNeeded(serverUrl, member.roles.split(' ')); const {channels, memberships: channelMembers, categories} = await fetchMyChannelsForTeam(serverUrl, teamId, false, 0, true); - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (operator) { - const myTeams: MyTeam[] = [{ - id: member.team_id, - roles: member.roles, - }]; + const myTeams: MyTeam[] = [{ + id: member.team_id, + roles: member.roles, + }]; - const models: Model[] = (await Promise.all([ - operator.handleTeam({teams: [team], prepareRecordsOnly: true}), - operator.handleMyTeam({myTeams, prepareRecordsOnly: true}), - operator.handleTeamMemberships({teamMemberships: [member], prepareRecordsOnly: true}), - ...await prepareMyChannelsForTeam(operator, teamId, channels || [], channelMembers || []), - prepareCategoriesAndCategoriesChannels(operator, categories || [], true), - ])).flat(); + const models: Model[] = (await Promise.all([ + operator.handleTeam({teams: [team], prepareRecordsOnly: true}), + operator.handleMyTeam({myTeams, prepareRecordsOnly: true}), + operator.handleTeamMemberships({teamMemberships: [member], prepareRecordsOnly: true}), + ...await prepareMyChannelsForTeam(operator, teamId, channels || [], channelMembers || []), + prepareCategoriesAndCategoriesChannels(operator, categories || [], true), + ])).flat(); - await operator.batchRecords(models, 'addUserToTeam'); - setTeamLoading(serverUrl, false); - loadEventSent = false; + await operator.batchRecords(models, 'addUserToTeam'); + setTeamLoading(serverUrl, false); + loadEventSent = false; - if (await isTablet()) { - const channel = await getDefaultChannelForTeam(operator.database, teamId); - if (channel) { - fetchPostsForChannel(serverUrl, channel.id); - } + if (await isTablet()) { + const channel = await getDefaultChannelForTeam(database, teamId); + if (channel) { + fetchPostsForChannel(serverUrl, channel.id); } - } else { - setTeamLoading(serverUrl, false); - loadEventSent = false; } } EphemeralStore.finishAddingToTeam(teamId); updateCanJoinTeams(serverUrl); return {member}; } catch (error) { + logDebug('error on addUserToTeam', getFullErrorMessage(error)); if (loadEventSent) { setTeamLoading(serverUrl, false); } EphemeralStore.finishAddingToTeam(teamId); - forceLogoutIfNecessary(serverUrl, error as ClientError); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } @@ -141,11 +132,12 @@ export async function addUsersToTeam(serverUrl: string, teamId: string, userIds: EphemeralStore.finishAddingToTeam(teamId); return {members}; } catch (error) { + logDebug('error on addUsersToTeam', getFullErrorMessage(error)); if (EphemeralStore.isAddingToTeam(teamId)) { EphemeralStore.finishAddingToTeam(teamId); } - forceLogoutIfNecessary(serverUrl, error as ClientError); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } @@ -157,27 +149,23 @@ export async function sendEmailInvitesToTeam(serverUrl: string, teamId: string, return {members}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on sendEmailInvitesToTeam', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function fetchMyTeams(serverUrl: string, fetchOnly = false): Promise { - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const [teams, memberships]: [Team[], TeamMembership[]] = await Promise.all([ client.getMyTeams(), client.getMyTeamMembers(), ]); if (!fetchOnly) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; const modelPromises: Array> = []; if (operator) { const removeTeamIds = new Set(memberships.filter((m) => m.delete_at > 0).map((m) => m.team_id)); @@ -189,7 +177,7 @@ export async function fetchMyTeams(serverUrl: string, fetchOnly = false): Promis if (removeTeamIds.size) { // Immediately delete myTeams so that the UI renders only teams the user is a member of. - const removeTeams = await queryTeamsById(operator.database, Array.from(removeTeamIds)).fetch(); + const removeTeams = await queryTeamsById(database, Array.from(removeTeamIds)).fetch(); removeTeams.forEach((team) => { modelPromises.push(prepareDeleteTeam(team)); }); @@ -207,41 +195,36 @@ export async function fetchMyTeams(serverUrl: string, fetchOnly = false): Promis return {teams, memberships}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchMyTeams', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export async function fetchMyTeam(serverUrl: string, teamId: string, fetchOnly = false): Promise { - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const [team, membership] = await Promise.all([ client.getTeam(teamId), client.getTeamMember(teamId, 'me'), ]); if (!fetchOnly) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (operator) { - const modelPromises = prepareMyTeams(operator, [team], [membership]); - if (modelPromises.length) { - const models = await Promise.all(modelPromises); - const flattenedModels = models.flat(); - if (flattenedModels?.length > 0) { - await operator.batchRecords(flattenedModels, 'fetchMyTeam'); - } + const modelPromises = prepareMyTeams(operator, [team], [membership]); + if (modelPromises.length) { + const models = await Promise.all(modelPromises); + const flattenedModels = models.flat(); + if (flattenedModels?.length > 0) { + await operator.batchRecords(flattenedModels, 'fetchMyTeam'); } } } return {teams: [team], memberships: [membership]}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchMyTeam', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } @@ -252,7 +235,8 @@ export const fetchAllTeams = async (serverUrl: string, page = 0, perPage = PER_P const teams = await client.getTeams(page, perPage); return {teams}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchAllTeams', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; @@ -321,8 +305,9 @@ export const updateCanJoinTeams = async (serverUrl: string) => { EphemeralStore.setCanJoinOtherTeams(serverUrl, canJoin); return {}; } catch (error) { + logDebug('error on updateCanJoinTeams', getFullErrorMessage(error)); EphemeralStore.setCanJoinOtherTeams(serverUrl, false); - forceLogoutIfNecessary(serverUrl, error as ClientError); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; @@ -344,31 +329,25 @@ export const fetchTeamsChannelsAndUnreadPosts = async (serverUrl: string, since: } } - return {error: undefined}; + return {}; }; export async function fetchTeamByName(serverUrl: string, teamName: string, fetchOnly = false) { - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const team = await client.getTeamByName(teamName); if (!fetchOnly) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (operator) { - const models = await operator.handleTeam({teams: [team], prepareRecordsOnly: true}); - await operator.batchRecords(models, 'fetchTeamByName'); - } + const models = await operator.handleTeam({teams: [team], prepareRecordsOnly: true}); + await operator.batchRecords(models, 'fetchTeamByName'); } return {team}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchTeamByName', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } @@ -384,14 +363,8 @@ export const removeCurrentUserFromTeam = async (serverUrl: string, teamId: strin }; export const removeUserFromTeam = async (serverUrl: string, teamId: string, userId: string, fetchOnly = false) => { - let client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); await client.removeFromTeam(teamId, userId); if (!fetchOnly) { @@ -399,9 +372,10 @@ export const removeUserFromTeam = async (serverUrl: string, teamId: string, user updateCanJoinTeams(serverUrl); } - return {error: undefined}; + return {}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on removeUserFromTeam', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; @@ -411,8 +385,8 @@ export async function handleTeamChange(serverUrl: string, teamId: string) { if (!operator) { return; } - const {database} = operator; + const currentTeamId = await getCurrentTeamId(database); if (currentTeamId === teamId) { @@ -496,7 +470,8 @@ export async function getTeamMembersByIds(serverUrl: string, teamId: string, use return {members}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on getTeamMembersByIds', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } diff --git a/app/actions/remote/terms_of_service.ts b/app/actions/remote/terms_of_service.ts index da92f24e0..7a67ee46f 100644 --- a/app/actions/remote/terms_of_service.ts +++ b/app/actions/remote/terms_of_service.ts @@ -4,24 +4,19 @@ import DatabaseManager from '@database/manager'; import NetworkManager from '@managers/network_manager'; import {getCurrentUser} from '@queries/servers/user'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import {forceLogoutIfNecessary} from './session'; -import type ClientError from '@client/rest/error'; - export async function fetchTermsOfService(serverUrl: string): Promise<{terms?: TermsOfService; error?: any}> { - let client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const terms = await client.getTermsOfService(); return {terms}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchTermsOfService', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } @@ -47,7 +42,8 @@ export async function updateTermsOfServiceStatus(serverUrl: string, id: string, } return {resp}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on updateTermsOfServiceStatus', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } diff --git a/app/actions/remote/thread.ts b/app/actions/remote/thread.ts index b9edb9d9b..a72142b93 100644 --- a/app/actions/remote/thread.ts +++ b/app/actions/remote/thread.ts @@ -12,6 +12,8 @@ import {getPostById} from '@queries/servers/post'; import {getConfigValue, getCurrentChannelId, getCurrentTeamId} from '@queries/servers/system'; import {getIsCRTEnabled, getThreadById, getTeamThreadsSyncData} from '@queries/servers/thread'; import {getCurrentUser} from '@queries/servers/user'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import {showThreadFollowingSnackbar} from '@utils/snack_bar'; import {getThreadsListEdges} from '@utils/thread'; @@ -76,34 +78,23 @@ export const fetchAndSwitchToThread = async (serverUrl: string, rootId: string, }; export const fetchThread = async (serverUrl: string, teamId: string, threadId: string, extended?: boolean) => { - let client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const thread = await client.getThread('me', teamId, threadId, extended); await processReceivedThreads(serverUrl, [thread], teamId); return {data: thread}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on fetchThread', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const updateTeamThreadsAsRead = async (serverUrl: string, teamId: string) => { - let client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const data = await client.updateTeamThreadsAsRead('me', teamId); // Update locally @@ -111,26 +102,17 @@ export const updateTeamThreadsAsRead = async (serverUrl: string, teamId: string) return {data}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on updateTeamThreadsAsRead', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const markThreadAsRead = async (serverUrl: string, teamId: string | undefined, threadId: string, updateLastViewed = true) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - - if (!database) { - return {error: `${serverUrl} database not found`}; - } - - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const timestamp = Date.now(); // DM/GM doesn't have a teamId, so we pass the current team id @@ -159,26 +141,17 @@ export const markThreadAsRead = async (serverUrl: string, teamId: string | undef return {data}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on markThreadAsRead', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const markThreadAsUnread = async (serverUrl: string, teamId: string, threadId: string, postId: string) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - - if (!database) { - return {error: `${serverUrl} database not found`}; - } - - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { // DM/GM doesn't have a teamId, so we pass the current team id let threadTeamId = teamId; if (!threadTeamId) { @@ -198,32 +171,23 @@ export const markThreadAsUnread = async (serverUrl: string, teamId: string, thre return {data}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on markThreadAsUnread', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const updateThreadFollowing = async (serverUrl: string, teamId: string, threadId: string, state: boolean, showSnackBar: boolean) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - - if (!database) { - return {error: `${serverUrl} database not found`}; - } - - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - // DM/GM doesn't have a teamId, so we pass the current team id - let threadTeamId = teamId; - if (!threadTeamId) { - threadTeamId = await getCurrentTeamId(database); - } + // DM/GM doesn't have a teamId, so we pass the current team id + let threadTeamId = teamId; + if (!threadTeamId) { + threadTeamId = await getCurrentTeamId(database); + } - try { const data = await client.updateThreadFollow('me', threadTeamId, threadId, state); // Update locally @@ -236,7 +200,8 @@ export const updateThreadFollowing = async (serverUrl: string, teamId: string, t return {data}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on updateThreadFollowing', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; @@ -249,10 +214,10 @@ export const fetchThreads = async ( pages?: number, ) => { const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { return {error: `${serverUrl} database not found`}; } + const {database} = operator; let client: Client; try { @@ -263,12 +228,12 @@ export const fetchThreads = async ( const fetchDirection = direction ?? Direction.Up; - const currentUser = await getCurrentUser(operator.database); + const currentUser = await getCurrentUser(database); if (!currentUser) { return {error: 'currentUser not found'}; } - const version = await getConfigValue(operator.database, 'Version'); + const version = await getConfigValue(database, 'Version'); const threadsData: Thread[] = []; let currentPage = 0; @@ -302,6 +267,7 @@ export const fetchThreads = async ( try { await fetchThreadsFunc(options); } catch (error) { + logDebug('error on fetchThreads', getFullErrorMessage(error)); if (__DEV__) { throw error; } @@ -312,13 +278,9 @@ export const fetchThreads = async ( }; export const syncTeamThreads = async (serverUrl: string, teamId: string, prepareRecordsOnly = false) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - try { - const syncData = await getTeamThreadsSyncData(operator.database, teamId); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const syncData = await getTeamThreadsSyncData(database, teamId); const syncDataUpdate = { id: teamId, } as TeamThreadsSync; @@ -418,12 +380,9 @@ export const syncTeamThreads = async (serverUrl: string, teamId: string, prepare }; export const loadEarlierThreads = async (serverUrl: string, teamId: string, lastThreadId: string, prepareRecordsOnly = false) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - try { + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + /* * - We will fetch one page of old threads * - Update the sync data with the earliest thread last_reply_at timestamp @@ -476,7 +435,7 @@ export const loadEarlierThreads = async (serverUrl: string, teamId: string, last } } - return {error: false, models, threads}; + return {models, threads}; } catch (error) { return {error}; } diff --git a/app/actions/remote/user.ts b/app/actions/remote/user.ts index d4de4940e..25fcbe7ec 100644 --- a/app/actions/remote/user.ts +++ b/app/actions/remote/user.ts @@ -16,15 +16,14 @@ import {getMembersCountByChannelsId, queryChannelsByTypes} from '@queries/server import {queryGroupsByNames} from '@queries/servers/group'; import {getConfig, getCurrentUserId} from '@queries/servers/system'; import {getCurrentUser, prepareUsers, queryAllUsers, queryUsersById, queryUsersByIdsOrUsernames, queryUsersByUsername} from '@queries/servers/user'; -import {logError} from '@utils/log'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import {getDeviceTimezone, isTimezoneEnabled} from '@utils/timezone'; import {getUserTimezoneProps, removeUserFromList} from '@utils/user'; import {fetchGroupsByNames} from './groups'; import {forceLogoutIfNecessary} from './session'; -import type {Client} from '@client/rest'; -import type ClientError from '@client/rest/error'; import type {Model} from '@nozbe/watermelondb'; import type UserModel from '@typings/database/models/servers/user'; @@ -45,14 +44,10 @@ export type ProfilesInChannelRequest = { } export const fetchMe = async (serverUrl: string, fetchOnly = false): Promise => { - let client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const [user, userStatus] = await Promise.all<[Promise, Promise]>([ client.getMe(), client.getStatus('me'), @@ -61,34 +56,27 @@ export const fetchMe = async (serverUrl: string, fetchOnly = false): Promise { - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {channelId, error}; - } + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const users = await client.getProfilesInChannel(channelId, options); const uniqueUsers = Array.from(new Set(users)); const filteredUsers = uniqueUsers.filter((u) => u.id !== excludeUserId); if (!fetchOnly) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (operator && filteredUsers.length) { + if (filteredUsers.length) { const modelPromises: Array> = []; const membership = filteredUsers.map((u) => ({ channel_id: channelId, @@ -108,28 +96,17 @@ export async function fetchProfilesInChannel(serverUrl: string, channelId: strin return {channelId, users: filteredUsers}; } catch (error) { - logError('fetchProfilesInChannel', error); - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchProfilesInChannel', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {channelId, error}; } } export async function fetchProfilesInGroupChannels(serverUrl: string, groupChannelIds: string[], fetchOnly = false): Promise { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - const {database} = operator; - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { // let's filter those channels that we already have the users const membersCount = await getMembersCountByChannelsId(database, groupChannelIds); const channelsToFetch = groupChannelIds.filter((c) => membersCount[c] <= 1); @@ -183,16 +160,14 @@ export async function fetchProfilesInGroupChannels(serverUrl: string, groupChann return {data}; } catch (error) { + logDebug('error on fetchProfilesInGroupChannels', getFullErrorMessage(error)); return {error}; } } export async function fetchProfilesPerChannels(serverUrl: string, channelIds: string[], excludeUserId?: string, fetchOnly = false): Promise { try { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); // Batch fetching profiles per channel by chunks of 250 const channels = chunk(channelIds, 250); @@ -235,39 +210,31 @@ export async function fetchProfilesPerChannels(serverUrl: string, channelIds: st return {data}; } catch (error) { + logDebug('error on fetchProfilesPerChannels', getFullErrorMessage(error)); return {error}; } } export const updateMe = async (serverUrl: string, user: Partial) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - let client; try { - client = NetworkManager.getClient(serverUrl); + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + + const data = await client.patchMe(user); + + if (data) { + operator.handleUsers({prepareRecordsOnly: false, users: [data]}); + + const updatedRoles: string[] = data.roles.split(' '); + await fetchRolesIfNeeded(serverUrl, updatedRoles); + } + + return {data}; } catch (error) { + logDebug('error on updateMe', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } - - let data: UserProfile; - try { - data = await client.patchMe(user); - } catch (e) { - forceLogoutIfNecessary(serverUrl, e as ClientError); - return {error: e}; - } - - if (data) { - operator.handleUsers({prepareRecordsOnly: false, users: [data]}); - - const updatedRoles: string[] = data.roles.split(' '); - await fetchRolesIfNeeded(serverUrl, updatedRoles); - } - - return {data}; }; let ids: string[] = []; @@ -323,79 +290,58 @@ const fetchUserOrGroupsByMentionNames = async (serverUrl: string, mentions: stri if (groupsToFetch.length) { await fetchGroupsByNames(serverUrl, groupsToFetch, false); } - return {data: true}; - } catch (e) { - return {error: e}; + return {}; + } catch (error) { + logDebug('error on fetchUserOrGroupsByMentionNames', getFullErrorMessage(error)); + return {error}; } }; export async function fetchStatusByIds(serverUrl: string, userIds: string[], fetchOnly = false) { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } if (!userIds.length) { return {statuses: []}; } - let database; - let operator; try { - const result = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - database = result.database; - operator = result.operator; - } catch (e) { - return {error: `${serverUrl} database not found`}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const statuses = await client.getStatusesByIds(userIds); - if (!fetchOnly && DatabaseManager.serverDatabases[serverUrl]) { - if (operator) { - const users = await queryUsersById(database, userIds).fetch(); - const userStatuses = statuses.reduce((result: Record, s) => { - result[s.user_id] = s; - return result; - }, {}); + if (!fetchOnly) { + const users = await queryUsersById(database, userIds).fetch(); + const userStatuses = statuses.reduce((result: Record, s) => { + result[s.user_id] = s; + return result; + }, {}); - for (const user of users) { - const status = userStatuses[user.id]; - user.prepareStatus(status?.status || General.OFFLINE); - } - - await operator.batchRecords(users, 'fetchStatusByIds'); + for (const user of users) { + const status = userStatuses[user.id]; + user.prepareStatus(status?.status || General.OFFLINE); } + + await operator.batchRecords(users, 'fetchStatusByIds'); } return {statuses}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchStatusByIds', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } } export const fetchUsersByIds = async (serverUrl: string, userIds: string[], fetchOnly = false) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } if (!userIds.length) { return {users: [], existingUsers: []}; } - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - try { - const currentUser = await getCurrentUser(operator.database); - const existingUsers = await queryUsersById(operator.database, userIds).fetch(); + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + + const currentUser = await getCurrentUser(database); + const existingUsers = await queryUsersById(database, userIds).fetch(); if (userIds.includes(currentUser!.id)) { existingUsers.push(currentUser!); } @@ -417,30 +363,22 @@ export const fetchUsersByIds = async (serverUrl: string, userIds: string[], fetc return {users, existingUsers}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchUsersByIds', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const fetchUsersByUsernames = async (serverUrl: string, usernames: string[], fetchOnly = false) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } if (!usernames.length) { return {users: []}; } - - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - try { - const currentUser = await getCurrentUser(operator.database); - const existingUsers = await queryUsersByUsername(operator.database, usernames).fetch(); + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + + const currentUser = await getCurrentUser(database); + const existingUsers = await queryUsersByUsername(database, usernames).fetch(); const exisitingUsersMap = existingUsers.reduce((result: Record, u) => { result[u.username] = u; return result; @@ -461,29 +399,21 @@ export const fetchUsersByUsernames = async (serverUrl: string, usernames: string return {users}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchUsersByUsernames', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const fetchProfiles = async (serverUrl: string, page = 0, perPage: number = General.PROFILE_CHUNK_SIZE, options: any = {}, fetchOnly = false) => { - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - try { const users = await client.getProfiles(page, perPage, options); if (!fetchOnly) { - const currentUserId = await getCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(database); const toStore = removeUserFromList(currentUserId, users); if (toStore.length) { await operator.handleUsers({ @@ -495,29 +425,21 @@ export const fetchProfiles = async (serverUrl: string, page = 0, perPage: number return {users}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchProfiles', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const fetchProfilesInTeam = async (serverUrl: string, teamId: string, page = 0, perPage: number = General.PROFILE_CHUNK_SIZE, sort = '', options: any = {}, fetchOnly = false) => { - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - try { const users = await client.getProfilesInTeam(teamId, page, perPage, sort, options); if (!fetchOnly) { - const currentUserId = await getCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(database); const toStore = removeUserFromList(currentUserId, users); if (toStore.length) { await operator.handleUsers({ @@ -529,7 +451,8 @@ export const fetchProfilesInTeam = async (serverUrl: string, teamId: string, pag return {users}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchProfilesInTeam', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; @@ -544,12 +467,12 @@ export const fetchProfilesNotInChannel = async ( fetchOnly = false, ) => { try { - const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const {operator, database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const client = NetworkManager.getClient(serverUrl); const users = await client.getProfilesNotInChannel(teamId, channelId, groupConstrained, page, perPage); if (!fetchOnly && users.length) { - const currentUserId = await getCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(database); const toStore = removeUserFromList(currentUserId, users); await operator.handleUsers({ users: toStore, @@ -559,30 +482,21 @@ export const fetchProfilesNotInChannel = async ( return {users}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on fetchProfilesNotInChannel', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const searchProfiles = async (serverUrl: string, term: string, options: SearchUserOptions, fetchOnly = false) => { - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - try { - const currentUserId = await getCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(database); const users = await client.searchUsers(term, options); if (!fetchOnly) { - const {database} = operator; const existing = await queryUsersById(database, users.map((u) => u.id)).fetchIds(); const existingSet = new Set(existing); const usersToAdd = users.filter((u) => !existingSet.has(u.id)); @@ -597,48 +511,28 @@ export const searchProfiles = async (serverUrl: string, term: string, options: S return {data: users}; } catch (error) { - logError('searchProfiles', error); - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on searchProfiles', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const fetchMissingProfilesByIds = async (serverUrl: string, userIds: string[]) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - try { - const {users} = await fetchUsersByIds(serverUrl, userIds); - if (users) { - const statusToLoad = users.map((u) => u.id); - fetchStatusByIds(serverUrl, statusToLoad); - } - return {users}; - } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); - return {error}; + const {users} = await fetchUsersByIds(serverUrl, userIds); + if (users) { + const statusToLoad = users.map((u) => u.id); + fetchStatusByIds(serverUrl, statusToLoad); } + return {users}; }; export const fetchMissingProfilesByUsernames = async (serverUrl: string, usernames: string[]) => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - - try { - const {users} = await fetchUsersByUsernames(serverUrl, usernames); - if (users) { - const statusToLoad = users.map((u) => u.id); - fetchStatusByIds(serverUrl, statusToLoad); - } - return {users}; - } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); - return {error}; + const {users} = await fetchUsersByUsernames(serverUrl, usernames); + if (users) { + const statusToLoad = users.map((u) => u.id); + fetchStatusByIds(serverUrl, statusToLoad); } + return {users}; }; export async function updateAllUsersSince(serverUrl: string, since: number, fetchOnly = false) { @@ -646,23 +540,13 @@ export async function updateAllUsersSince(serverUrl: string, since: number, fetc return {users: []}; } - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - const database = operator.database; - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - - const currentUserId = await getCurrentUserId(database); - const userIds = (await queryAllUsers(database).fetchIds()).filter((id) => id !== currentUserId); let userUpdates: UserProfile[] = []; try { + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + + const currentUserId = await getCurrentUserId(database); + const userIds = (await queryAllUsers(database).fetchIds()).filter((id) => id !== currentUserId); userUpdates = await client.getProfilesByIds(userIds, {since}); if (userUpdates.length && !fetchOnly) { const modelsToBatch: Model[] = []; @@ -676,7 +560,9 @@ export async function updateAllUsersSince(serverUrl: string, since: number, fetc await operator.batchRecords(modelsToBatch, 'updateAllUsersSince'); } - } catch { + } catch (error) { + logDebug('error on updateAllUsersSince', getFullErrorMessage(error)); + // Do nothing } @@ -684,25 +570,16 @@ export async function updateAllUsersSince(serverUrl: string, since: number, fetc } export async function updateUsersNoLongerVisible(serverUrl: string, prepareRecordsOnly = false): Promise<{error?: unknown; models?: Model[]}> { - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - const serverDatabase = DatabaseManager.serverDatabases[serverUrl]; - if (!serverDatabase) { - return {error: `${serverUrl} database not found`}; - } - - const models: Model[] = []; - try { + const models: Model[] = []; const knownUsers = new Set(await client.getKnownUsers()); - const currentUserId = await getCurrentUserId(serverDatabase.database); + const currentUserId = await getCurrentUserId(database); knownUsers.add(currentUserId); - const allUsers = await queryAllUsers(serverDatabase.database).fetch(); + const allUsers = await queryAllUsers(database).fetch(); for (const user of allUsers) { if (!knownUsers.has(user.id)) { user.prepareDestroyPermanently(); @@ -710,131 +587,85 @@ export async function updateUsersNoLongerVisible(serverUrl: string, prepareRecor } } if (models.length && !prepareRecordsOnly) { - serverDatabase.operator.batchRecords(models, 'updateUsersNoLongerVisible'); + operator.batchRecords(models, 'updateUsersNoLongerVisible'); } + return {models}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on updateUsersNoLongerVisible', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } - - return {models}; } export const setStatus = async (serverUrl: string, status: UserStatus) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const data = await client.updateStatus(status); await updateLocalUser(serverUrl, {status: status.status}); - return { - data, - }; + return {data}; } catch (error) { - forceLogoutIfNecessary(serverUrl, error as ClientErrorProps); + logDebug('error on setStatus', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); return {error}; } }; export const updateCustomStatus = async (serverUrl: string, customStatus: UserCustomStatus) => { - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); - try { if (!customStatus.duration) { delete customStatus.expires_at; } await client.updateCustomStatus(customStatus); - return {data: true}; + return {}; } catch (error) { + logDebug('error on updateCustomStatus', getFullErrorMessage(error)); return {error}; } }; export const removeRecentCustomStatus = async (serverUrl: string, customStatus: UserCustomStatus) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - updateRecentCustomStatuses(serverUrl, customStatus, false, true); - try { + const client = NetworkManager.getClient(serverUrl); await client.removeRecentCustomStatus(customStatus); + return {}; } catch (error) { + logDebug('error on removeRecentCustomStatus', getFullErrorMessage(error)); return {error}; } - - return {data: true}; }; export const unsetCustomStatus = async (serverUrl: string) => { - let client: Client; - - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); await client.unsetCustomStatus(); + return {}; } catch (error) { + logDebug('error on unsetCustomStatus', getFullErrorMessage(error)); return {error}; } - - return {data: true}; }; export const setDefaultProfileImage = async (serverUrl: string, userId: string) => { - let client: Client; - - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); await client.setDefaultProfileImage(userId); updateLocalUser(serverUrl, {last_picture_update: Date.now()}); + return {}; } catch (error) { + logDebug('error on setDefaultProfileImage', getFullErrorMessage(error)); return {error}; } - - return {data: true}; }; export const uploadUserProfileImage = async (serverUrl: string, localPath: string) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } + const client = NetworkManager.getClient(serverUrl); + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - try { const currentUser = await getCurrentUser(database); if (currentUser) { const endpoint = `${client.getUserRoute(currentUser.id)}/image`; @@ -847,42 +678,31 @@ export const uploadUserProfileImage = async (serverUrl: string, localPath: strin }, }); } - } catch (e) { - return {error: e}; + return {}; + } catch (error) { + logDebug('error on uploadUserProfileImage', getFullErrorMessage(error)); + return {error}; } - return {error: undefined}; }; export const searchUsers = async (serverUrl: string, term: string, teamId: string, channelId?: string) => { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return {error: `${serverUrl} database not found`}; - } - - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const users = await client.autocompleteUsers(term, teamId, channelId); return {users}; } catch (error) { + logDebug('error on searchUsers', getFullErrorMessage(error)); return {error}; } }; export const buildProfileImageUrl = (serverUrl: string, userId: string, timestamp = 0) => { - let client: Client; try { - client = NetworkManager.getClient(serverUrl); + const client = NetworkManager.getClient(serverUrl); + return client.getProfilePictureUrl(userId, timestamp); } catch (error) { return ''; } - - return client.getProfilePictureUrl(userId, timestamp); }; export const autoUpdateTimezone = async (serverUrl: string) => { @@ -891,14 +711,14 @@ export const autoUpdateTimezone = async (serverUrl: string) => { const result = DatabaseManager.getServerDatabaseAndOperator(serverUrl); database = result.database; } catch (e) { - return {error: `${serverUrl} database not found`}; + return; } const config = await getConfig(database); const currentUser = await getCurrentUser(database); if (!currentUser || !config || !isTimezoneEnabled(config)) { - return null; + return; } // Set timezone @@ -911,26 +731,13 @@ export const autoUpdateTimezone = async (serverUrl: string) => { const timezone = {useAutomaticTimezone: 'true', automaticTimezone: deviceTimezone, manualTimezone: currentTimezone.manualTimezone}; await updateMe(serverUrl, {timezone}); } - return null; }; export const fetchTeamAndChannelMembership = async (serverUrl: string, userId: string, teamId: string, channelId?: string) => { - let operator; try { - const result = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - operator = result.operator; - } catch (e) { - return {error: `${serverUrl} database not found`}; - } + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - - try { const requests = await Promise.all([ client.getTeamMember(teamId, userId), channelId ? client.getChannelMember(channelId, userId) : undefined, @@ -951,8 +758,9 @@ export const fetchTeamAndChannelMembership = async (serverUrl: string, userId: s const models = await Promise.all(modelPromises); await operator.batchRecords(models.flat(), 'fetchTeamAndChannelMembership'); - return {error: undefined}; + return {}; } catch (error) { + logDebug('error on searchUsers', getFullErrorMessage(error)); return {error}; } }; @@ -963,7 +771,7 @@ export const getAllSupportedTimezones = async (serverUrl: string) => { const allTzs = await client.getTimezones(); return allTzs; } catch (error) { - logError('FAILED TO GET ALL TIMEZONES', error); + logDebug('error on getAllSupportedTimezones', getFullErrorMessage(error)); return []; } }; diff --git a/app/actions/websocket/category.ts b/app/actions/websocket/category.ts index 2d4b06daa..56fa25139 100644 --- a/app/actions/websocket/category.ts +++ b/app/actions/websocket/category.ts @@ -78,13 +78,7 @@ export async function handleCategoryDeletedEvent(serverUrl: string, msg: Websock export async function handleCategoryOrderUpdatedEvent(serverUrl: string, msg: WebsocketCategoriesMessage) { try { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - - if (!operator) { - return; - } - - const {database} = operator; + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); // Update category order if (msg.data.order?.length) { diff --git a/app/actions/websocket/channel.ts b/app/actions/websocket/channel.ts index 08ac0e8da..aa10a0fa5 100644 --- a/app/actions/websocket/channel.ts +++ b/app/actions/websocket/channel.ts @@ -24,18 +24,14 @@ import type {Model} from '@nozbe/watermelondb'; // Received when current user created a channel in a different client export async function handleChannelCreatedEvent(serverUrl: string, msg: any) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } - const {database} = operator; - const {team_id: teamId, channel_id: channelId} = msg.data; if (EphemeralStore.creatingChannel) { return; // We probably don't need to handle this WS because we provoked it } try { + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const channel = await getChannelById(database, channelId); if (channel) { return; // We already have this channel @@ -76,12 +72,9 @@ export async function handleChannelUnarchiveEvent(serverUrl: string, msg: any) { } export async function handleChannelConvertedEvent(serverUrl: string, msg: any) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } - try { + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const channelId = msg.data.channel_id; if (EphemeralStore.isConvertingChannel(channelId)) { return; @@ -97,12 +90,9 @@ export async function handleChannelConvertedEvent(serverUrl: string, msg: any) { } export async function handleChannelUpdatedEvent(serverUrl: string, msg: any) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } - try { + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const updatedChannel = JSON.parse(msg.data.channel); const models: Model[] = await operator.handleChannel({channels: [updatedChannel], prepareRecordsOnly: true}); const infoModel = await updateChannelInfoFromChannel(serverUrl, updatedChannel, true); @@ -117,10 +107,7 @@ export async function handleChannelUpdatedEvent(serverUrl: string, msg: any) { export async function handleChannelViewedEvent(serverUrl: string, msg: any) { try { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return; - } + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const {channel_id: channelId} = msg.data; @@ -137,12 +124,9 @@ export async function handleChannelViewedEvent(serverUrl: string, msg: any) { // This event is triggered by changes in the notify props or in the roles. export async function handleChannelMemberUpdatedEvent(serverUrl: string, msg: any) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } - try { + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const models: Model[] = []; const updatedChannelMember: ChannelMembership = JSON.parse(msg.data.channelMember); @@ -172,13 +156,6 @@ export async function handleChannelMemberUpdatedEvent(serverUrl: string, msg: an } export async function handleDirectAddedEvent(serverUrl: string, msg: WebSocketMessage) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } - - const {database} = operator; - if (EphemeralStore.creatingDMorGMTeammates.length) { let userList: string[] | undefined; if ('teammate_ids' in msg.data) { // GM @@ -199,6 +176,8 @@ export async function handleDirectAddedEvent(serverUrl: string, msg: WebSocketMe } try { + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const {channel_id: channelId} = msg.broadcast; const channel = await getChannelById(database, channelId); if (channel) { @@ -242,17 +221,13 @@ export async function handleDirectAddedEvent(serverUrl: string, msg: WebSocketMe } export async function handleUserAddedToChannelEvent(serverUrl: string, msg: any) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } + const userId = msg.data.user_id || msg.broadcast.userId; + const channelId = msg.data.channel_id || msg.broadcast.channel_id; + const {team_id: teamId} = msg.data; try { - const {database} = operator; + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const currentUser = await getCurrentUser(database); - const userId = msg.data.user_id || msg.broadcast.userId; - const channelId = msg.data.channel_id || msg.broadcast.channel_id; - const {team_id: teamId} = msg.data; const models: Model[] = []; if (userId === currentUser?.id) { @@ -363,32 +338,26 @@ export async function handleUserRemovedFromChannelEvent(serverUrl: string, msg: } export async function handleChannelDeletedEvent(serverUrl: string, msg: WebSocketMessage) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { + const {channel_id: channelId, delete_at: deleteAt} = msg.data; + if (EphemeralStore.isLeavingChannel(channelId) || EphemeralStore.isArchivingChannel(channelId)) { return; } - try { - const {database} = operator; - const {channel_id: channelId, delete_at: deleteAt} = msg.data; - if (EphemeralStore.isLeavingChannel(channelId) || EphemeralStore.isArchivingChannel(channelId)) { - return; - } + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - const currentChannel = await getCurrentChannel(database); const user = await getCurrentUser(database); if (!user) { return; } - const config = await getConfig(database); - await setChannelDeleteAt(serverUrl, channelId, deleteAt); - if (user.isGuest) { updateUsersNoLongerVisible(serverUrl); } + const currentChannel = await getCurrentChannel(database); + const config = await getConfig(database); + if (config?.ExperimentalViewArchivedChannels !== 'true') { if (currentChannel && currentChannel.id === channelId) { await handleKickFromChannel(serverUrl, channelId, Events.CHANNEL_ARCHIVED); diff --git a/app/actions/websocket/posts.ts b/app/actions/websocket/posts.ts index 21e113ae4..18b7f98ba 100644 --- a/app/actions/websocket/posts.ts +++ b/app/actions/websocket/posts.ts @@ -195,6 +195,7 @@ export async function handlePostEdited(serverUrl: string, msg: WebSocketMessage) if (!operator) { return; } + const {database} = operator; let post: Post; try { @@ -204,7 +205,6 @@ export async function handlePostEdited(serverUrl: string, msg: WebSocketMessage) } const models: Model[] = []; - const {database} = operator; const oldPost = await getPostById(database, post.id); if (!oldPost) { @@ -223,7 +223,7 @@ export async function handlePostEdited(serverUrl: string, msg: WebSocketMessage) } let actionType: string = ActionType.POSTS.RECEIVED_NEW; - const isCRTEnabled = await getIsCRTEnabled(operator.database); + const isCRTEnabled = await getIsCRTEnabled(database); if (isCRTEnabled && post.root_id) { actionType = ActionType.POSTS.RECEIVED_IN_THREAD; } @@ -240,12 +240,8 @@ export async function handlePostEdited(serverUrl: string, msg: WebSocketMessage) } export async function handlePostDeleted(serverUrl: string, msg: WebSocketMessage) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } try { - const {database} = operator; + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const post: Post = JSON.parse(msg.data.post); diff --git a/app/actions/websocket/reactions.ts b/app/actions/websocket/reactions.ts index f3c4b680d..2950470ec 100644 --- a/app/actions/websocket/reactions.ts +++ b/app/actions/websocket/reactions.ts @@ -5,12 +5,9 @@ import DatabaseManager from '@database/manager'; import {queryReaction} from '@queries/servers/reaction'; export async function handleAddCustomEmoji(serverUrl: string, msg: WebSocketMessage): Promise { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } - try { + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const emoji: CustomEmoji = JSON.parse(msg.data.emoji); await operator.handleCustomEmojis({ prepareRecordsOnly: false, @@ -22,12 +19,9 @@ export async function handleAddCustomEmoji(serverUrl: string, msg: WebSocketMess } export async function handleReactionAddedToPostEvent(serverUrl: string, msg: WebSocketMessage): Promise { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } - try { + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const reaction: Reaction = JSON.parse(msg.data.reaction); await operator.handleReactions({ prepareRecordsOnly: false, @@ -43,12 +37,9 @@ export async function handleReactionAddedToPostEvent(serverUrl: string, msg: Web } export async function handleReactionRemovedFromPostEvent(serverUrl: string, msg: WebSocketMessage): Promise { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return; - } - try { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const msgReaction: Reaction = JSON.parse(msg.data.reaction); const reaction = await queryReaction(database, msgReaction.emoji_name, msgReaction.post_id, msgReaction.user_id).fetch(); diff --git a/app/actions/websocket/roles.ts b/app/actions/websocket/roles.ts index ee535f3ac..bee718666 100644 --- a/app/actions/websocket/roles.ts +++ b/app/actions/websocket/roles.ts @@ -10,15 +10,12 @@ import {getCurrentUser} from '@queries/servers/user'; import type {Model} from '@nozbe/watermelondb'; export async function handleRoleUpdatedEvent(serverUrl: string, msg: WebSocketMessage): Promise { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } - - // only update Role records that exist in the Role Table try { + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + + // only update Role records that exist in the Role Table const role: Role = JSON.parse(msg.data.role); - const dbRole = await getRoleById(operator.database, role.id); + const dbRole = await getRoleById(database, role.id); if (!dbRole) { return; } @@ -37,8 +34,9 @@ export async function handleUserRoleUpdatedEvent(serverUrl: string, msg: WebSock if (!operator) { return; } + const {database} = operator; - const currentUserId = await getCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(database); if (currentUserId !== msg.data.user_id) { return; } @@ -58,7 +56,7 @@ export async function handleUserRoleUpdatedEvent(serverUrl: string, msg: WebSock } // update User Table record - const user = await getCurrentUser(operator.database); + const user = await getCurrentUser(database); if (user) { user!.prepareUpdate((u) => { u.roles = msg.data.roles; @@ -70,19 +68,16 @@ export async function handleUserRoleUpdatedEvent(serverUrl: string, msg: WebSock } export async function handleTeamMemberRoleUpdatedEvent(serverUrl: string, msg: WebSocketMessage): Promise { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } - try { + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const member: TeamMembership = JSON.parse(msg.data.member); if (member.delete_at > 0) { return; } - const currentUserId = await getCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(database); if (currentUserId !== member.user_id) { return; } diff --git a/app/actions/websocket/system.ts b/app/actions/websocket/system.ts index ed3bb8cb2..638760708 100644 --- a/app/actions/websocket/system.ts +++ b/app/actions/websocket/system.ts @@ -8,16 +8,13 @@ import DatabaseManager from '@database/manager'; import {getConfig, getLicense} from '@queries/servers/system'; export async function handleLicenseChangedEvent(serverUrl: string, msg: WebSocketMessage): Promise { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } - try { + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const license = msg.data.license; const systems: IdValue[] = [{id: SYSTEM_IDENTIFIERS.LICENSE, value: JSON.stringify(license)}]; - const prevLicense = await getLicense(operator.database); + const prevLicense = await getLicense(database); await operator.handleSystem({systems, prepareRecordsOnly: false}); if (license?.LockTeammateNameDisplay && (prevLicense?.LockTeammateNameDisplay !== license.LockTeammateNameDisplay)) { @@ -29,14 +26,10 @@ export async function handleLicenseChangedEvent(serverUrl: string, msg: WebSocke } export async function handleConfigChangedEvent(serverUrl: string, msg: WebSocketMessage): Promise { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return; - } - try { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const config = msg.data.config; - const prevConfig = await getConfig(operator.database); + const prevConfig = await getConfig(database); await storeConfig(serverUrl, config); if (config?.LockTeammateNameDisplay && (prevConfig?.LockTeammateNameDisplay !== config.LockTeammateNameDisplay)) { updateDmGmDisplayName(serverUrl); diff --git a/app/actions/websocket/teams.ts b/app/actions/websocket/teams.ts index 8150edfd4..52fd4d649 100644 --- a/app/actions/websocket/teams.ts +++ b/app/actions/websocket/teams.ts @@ -14,6 +14,7 @@ import {getCurrentTeam, prepareMyTeams, queryMyTeamsByIds} from '@queries/server import {getCurrentUser} from '@queries/servers/user'; import EphemeralStore from '@store/ephemeral_store'; import {setTeamLoading} from '@store/team_load_store'; +import {getFullErrorMessage} from '@utils/errors'; import {logDebug} from '@utils/log'; import type ServerDataOperator from '@database/operator/server_data_operator'; @@ -73,7 +74,7 @@ export async function handleTeamRestored(serverUrl: string, msg: WebSocketMessag if (markedAsLoading) { setTeamLoading(serverUrl, false); } - logDebug('cannot handle restore team websocket event', error); + logDebug('cannot handle restore team websocket event', getFullErrorMessage(error)); } } @@ -106,14 +107,11 @@ export async function handleLeaveTeamEvent(serverUrl: string, msg: WebSocketMess } export async function handleUpdateTeamEvent(serverUrl: string, msg: WebSocketMessage) { - const database = DatabaseManager.serverDatabases[serverUrl]; - if (!database) { - return; - } - try { + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const team: Team = JSON.parse(msg.data.team); - database.operator.handleTeam({ + operator.handleTeam({ teams: [team], prepareRecordsOnly: false, }); diff --git a/app/actions/websocket/threads.ts b/app/actions/websocket/threads.ts index b4b90b35b..030ade2d5 100644 --- a/app/actions/websocket/threads.ts +++ b/app/actions/websocket/threads.ts @@ -8,10 +8,8 @@ import EphemeralStore from '@store/ephemeral_store'; export async function handleThreadUpdatedEvent(serverUrl: string, msg: WebSocketMessage): Promise { try { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { - return; - } + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const thread: Thread = JSON.parse(msg.data.thread); let teamId = msg.broadcast.team_id; diff --git a/app/client/graphQL/entry.ts b/app/client/graphQL/entry.ts index 7e6fb90ec..80e6575ce 100644 --- a/app/client/graphQL/entry.ts +++ b/app/client/graphQL/entry.ts @@ -2,23 +2,18 @@ // See LICENSE.txt for license information. import {MEMBERS_PER_PAGE} from '@constants/graphql'; import NetworkManager from '@managers/network_manager'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import QueryNames from './constants'; -import type {Client} from '@client/rest'; - const doGQLQuery = async (serverUrl: string, query: string, variables: {[name: string]: any}, operationName: string) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const response = await client.doFetch('/api/v5/graphql', {method: 'post', body: {query, variables, operationName}}) as GQLResponse; return response; } catch (error) { + logDebug('error on doGQLQuery', getFullErrorMessage(error)); return {error}; } }; diff --git a/app/client/rest/base.ts b/app/client/rest/base.ts index 95a8476fa..343cf8f3d 100644 --- a/app/client/rest/base.ts +++ b/app/client/rest/base.ts @@ -291,6 +291,7 @@ export default class ClientBase { defaultMessage: 'Received invalid response from the server.', }, url, + details: error, }); } @@ -312,8 +313,8 @@ export default class ClientBase { } throw new ClientError(this.apiClient.baseUrl, { - message: response.data?.message || '', - server_error_id: response.data?.id, + message: response.data?.message as string || `Response with status code ${response.code}`, + server_error_id: response.data?.id as string, status_code: response.code, url, }); diff --git a/app/client/rest/error.ts b/app/client/rest/error.ts index c6b7cc5f9..90dee80f8 100644 --- a/app/client/rest/error.ts +++ b/app/client/rest/error.ts @@ -5,11 +5,11 @@ import {cleanUrlForLogging} from '@utils/url'; export default class ClientError extends Error { url: string; - intl: { defaultMessage: string; id: string; values?: any }; - server_error_id: any; - status_code: any; - details: Error; - constructor(baseUrl: string, data: any) { + intl?: ClientErrorIntl; + server_error_id?: string; + status_code?: number; + details?: unknown; + constructor(baseUrl: string, data: ClientErrorProps) { super(data.message + ': ' + cleanUrlForLogging(baseUrl, data.url)); this.message = data.message; diff --git a/app/components/autocomplete/slash_suggestion/slash_suggestion.tsx b/app/components/autocomplete/slash_suggestion/slash_suggestion.tsx index d5597d6f2..52b367278 100644 --- a/app/components/autocomplete/slash_suggestion/slash_suggestion.tsx +++ b/app/components/autocomplete/slash_suggestion/slash_suggestion.tsx @@ -98,7 +98,7 @@ const SlashSuggestion = ({ if (!mounted.current) { return; } - if (res.error) { + if ('error' in res) { updateSuggestions(emptySuggestionList); } else if (res.suggestions.length === 0) { updateSuggestions(emptySuggestionList); diff --git a/app/components/channel_actions/manage_members_label/manage_members_label.tsx b/app/components/channel_actions/manage_members_label/manage_members_label.tsx index 10e677205..26d40f6cc 100644 --- a/app/components/channel_actions/manage_members_label/manage_members_label.tsx +++ b/app/components/channel_actions/manage_members_label/manage_members_label.tsx @@ -90,7 +90,7 @@ const ManageMembersLabel = ({canRemoveUser, channelId, manageOption, testID, use const updateChannelMemberSchemeRole = useCallback(async (schemeAdmin: boolean) => { const result = await updateChannelMemberSchemeRoles(serverUrl, channelId, userId, true, schemeAdmin); - if (result.error) { + if ('error' in result) { alertErrorWithFallback(intl, result.error, messages.role_change_error); } await dismissBottomSheet(); diff --git a/app/components/error_text/error_text.test.tsx b/app/components/error_text/error_text.test.tsx index 011d4f8c0..e9f06529a 100644 --- a/app/components/error_text/error_text.test.tsx +++ b/app/components/error_text/error_text.test.tsx @@ -1,10 +1,10 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {render} from '@testing-library/react-native'; import React from 'react'; import {Preferences} from '@constants'; +import {renderWithIntl} from '@test/intl-test-helper'; import ErrorTextComponent from './index'; @@ -20,7 +20,7 @@ describe('ErrorText', () => { }; test('should match snapshot', () => { - const wrapper = render( + const wrapper = renderWithIntl( , ); diff --git a/app/components/error_text/index.tsx b/app/components/error_text/index.tsx index f30b5e685..eee5c6d51 100644 --- a/app/components/error_text/index.tsx +++ b/app/components/error_text/index.tsx @@ -2,14 +2,15 @@ // See LICENSE.txt for license information. import React from 'react'; +import {useIntl} from 'react-intl'; import {type StyleProp, Text, type TextStyle, type ViewStyle} from 'react-native'; -import FormattedText from '@components/formatted_text'; import {useTheme} from '@context/theme'; +import {getErrorMessage} from '@utils/errors'; import {makeStyleSheetFromTheme} from '@utils/theme'; type ErrorProps = { - error: ErrorText; + error: unknown; testID?: string; textStyle?: StyleProp | StyleProp; } @@ -17,20 +18,8 @@ type ErrorProps = { const ErrorTextComponent = ({error, testID, textStyle}: ErrorProps) => { const theme = useTheme(); const style = getStyleSheet(theme); - const message = typeof (error) === 'string' ? error : error.message; - - if (typeof (error) !== 'string' && error.intl) { - const {intl} = error; - return ( - - ); - } + const intl = useIntl(); + const message = getErrorMessage(error, intl); return ( (({background setDownloading(false); setProgress(0); - if ((error as Error).message !== 'cancelled') { + if (!isErrorWithMessage(error) || error.message !== 'cancelled') { + logDebug('error on downloadAndPreviewFile', getFullErrorMessage(error)); alertDownloadFailed(intl); } } diff --git a/app/components/post_draft/send_handler/send_handler.tsx b/app/components/post_draft/send_handler/send_handler.tsx index 3c6e3fd10..dd901b59a 100644 --- a/app/components/post_draft/send_handler/send_handler.tsx +++ b/app/components/post_draft/send_handler/send_handler.tsx @@ -19,6 +19,7 @@ import {useServerUrl} from '@context/server'; import DraftUploadManager from '@managers/draft_upload_manager'; import * as DraftUtils from '@utils/draft'; import {isReactionMatch} from '@utils/emoji/helpers'; +import {getFullErrorMessage} from '@utils/errors'; import {preventDoubleTap} from '@utils/tap'; import {confirmOutOfOfficeDisabled} from '@utils/user'; @@ -197,7 +198,7 @@ export default function SendHandler({ setSendingMessage(false); if (error) { - const errorMessage = typeof (error) === 'string' ? error : error.message; + const errorMessage = getFullErrorMessage(error); DraftUtils.alertSlashCommandFailed(intl, errorMessage); return; } diff --git a/app/managers/apps_manager.ts b/app/managers/apps_manager.ts index dfb9b03b1..aa2c4f48b 100644 --- a/app/managers/apps_manager.ts +++ b/app/managers/apps_manager.ts @@ -8,6 +8,7 @@ import DatabaseManager from '@database/manager'; import {getChannelById} from '@queries/servers/channel'; import {getConfig, getCurrentChannelId, getCurrentTeamId, getCurrentUserId, observeConfigBooleanValue} from '@queries/servers/system'; import {validateBindings} from '@utils/apps'; +import {getFullErrorMessage} from '@utils/errors'; import {logDebug} from '@utils/log'; import NetworkManager from './network_manager'; @@ -129,7 +130,7 @@ class AppsManager { this.commandForms[serverUrl] = {}; } } catch (error) { - logDebug('Error fetching apps', error); + logDebug('error on fetchBindings', getFullErrorMessage(error)); this.handleError(serverUrl); } }; diff --git a/app/managers/draft_upload_manager/index.ts b/app/managers/draft_upload_manager/index.ts index ec6f77fe9..ba9e7461a 100644 --- a/app/managers/draft_upload_manager/index.ts +++ b/app/managers/draft_upload_manager/index.ts @@ -6,6 +6,7 @@ import {AppState, type AppStateStatus} from 'react-native'; import {updateDraftFile} from '@actions/local/draft'; import {uploadFile} from '@actions/remote/file'; import {PROGRESS_TIME_TO_STORE} from '@constants/files'; +import {getFullErrorMessage} from '@utils/errors'; import type {ClientResponse, ClientResponseError} from '@mattermost/react-native-network-client'; @@ -63,7 +64,7 @@ class DraftUploadManager { const {error, cancel} = uploadFile(serverUrl, file, channelId, onProgress, onComplete, onError, skipBytes); if (error) { - this.handleError(error.message, file.clientId!); + this.handleError(getFullErrorMessage(error), file.clientId!); return; } this.handlers[file.clientId!].cancel = cancel; diff --git a/app/managers/global_event_handler.ts b/app/managers/global_event_handler.ts index e743c7be7..51568a8c2 100644 --- a/app/managers/global_event_handler.ts +++ b/app/managers/global_event_handler.ts @@ -22,8 +22,6 @@ import {setScreensOrientation} from '@screens/navigation'; import {handleDeepLink} from '@utils/deep_link'; import {logError} from '@utils/log'; -import type {jsAndNativeErrorHandler} from '@typings/global/error_handling'; - type LinkingCallbackArg = {url: string}; const {SplitView} = NativeModules; diff --git a/app/managers/integrations_manager.ts b/app/managers/integrations_manager.ts index 438057350..aa11bca8d 100644 --- a/app/managers/integrations_manager.ts +++ b/app/managers/integrations_manager.ts @@ -27,7 +27,7 @@ class ServerIntegrationsManager { try { const res = await fetchCommands(this.serverUrl, teamId); - if (res.error) { + if ('error' in res) { return []; } this.commands[teamId] = res.commands; diff --git a/app/managers/network_manager.ts b/app/managers/network_manager.ts index f2cfe812d..9e06a327e 100644 --- a/app/managers/network_manager.ts +++ b/app/managers/network_manager.ts @@ -88,6 +88,7 @@ class NetworkManager { defaultMessage: 'Can’t find this server. Check spelling and URL format.', }, url: serverUrl, + details: error, }); } diff --git a/app/products/calls/actions/calls.ts b/app/products/calls/actions/calls.ts index a609f4748..cb0c227b1 100644 --- a/app/products/calls/actions/calls.ts +++ b/app/products/calls/actions/calls.ts @@ -36,21 +36,19 @@ import {getThreadById} from '@queries/servers/thread'; import {getCurrentUser, getUserById} from '@queries/servers/user'; import {dismissAllModalsAndPopToScreen} from '@screens/navigation'; import NavigationStore from '@store/navigation_store'; -import {logWarning} from '@utils/log'; +import {getFullErrorMessage} from '@utils/errors'; +import {logDebug} from '@utils/log'; import {displayUsername, getUserIdFromChannelName, isSystemAdmin} from '@utils/user'; import {newConnection} from '../connection/connection'; import type { - ApiResp, AudioDevice, Call, CallParticipant, CallsConnection, } from '@calls/types/calls'; -import type {Client} from '@client/rest'; -import type ClientError from '@client/rest/error'; -import type {CallChannelState, CallRecordingState, CallState, EmojiData} from '@mattermost/calls/lib/types'; +import type {CallChannelState, CallState, EmojiData} from '@mattermost/calls/lib/types'; import type {IntlShape} from 'react-intl'; let connection: CallsConnection | null = null; @@ -67,38 +65,27 @@ export const loadConfig = async (serverUrl: string, force = false) => { } } - let client: Client; try { - client = NetworkManager.getClient(serverUrl); + const client = NetworkManager.getClient(serverUrl); + const data = await client.getCallsConfig(); + const nextConfig = {...data, last_retrieved_at: now}; + setConfig(serverUrl, nextConfig); + return {data: nextConfig}; } catch (error) { + logDebug('error on loadConfig', getFullErrorMessage(error)); + await forceLogoutIfNecessary(serverUrl, error); return {error}; } - - let data; - try { - data = await client.getCallsConfig(); - } catch (error) { - await forceLogoutIfNecessary(serverUrl, error as ClientError); - return {error}; - } - - const nextConfig = {...data, last_retrieved_at: now}; - setConfig(serverUrl, nextConfig); - return {data: nextConfig}; }; export const loadCalls = async (serverUrl: string, userId: string) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } let resp: CallChannelState[] = []; try { + const client = NetworkManager.getClient(serverUrl); resp = await client.getCalls() || []; } catch (error) { - await forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on loadCalls', getFullErrorMessage(error)); + await forceLogoutIfNecessary(serverUrl, error); return {error}; } @@ -127,18 +114,13 @@ export const loadCalls = async (serverUrl: string, userId: string) => { }; export const loadCallForChannel = async (serverUrl: string, channelId: string) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - let resp: CallChannelState; try { + const client = NetworkManager.getClient(serverUrl); resp = await client.getCallForChannel(channelId); } catch (error) { - await forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on loadCallForChannel', getFullErrorMessage(error)); + await forceLogoutIfNecessary(serverUrl, error); return {error}; } @@ -189,18 +171,13 @@ export const loadConfigAndCalls = async (serverUrl: string, userId: string) => { }; export const checkIsCallsPluginEnabled = async (serverUrl: string) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - let data: ClientPluginManifest[] = []; try { + const client = NetworkManager.getClient(serverUrl); data = await client.getPluginsManifests(); } catch (error) { - await forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on checkIsCallsPluginEnabled', getFullErrorMessage(error)); + await forceLogoutIfNecessary(serverUrl, error); return {error}; } @@ -214,24 +191,18 @@ export const checkIsCallsPluginEnabled = async (serverUrl: string) => { }; export const enableChannelCalls = async (serverUrl: string, channelId: string, enable: boolean) => { - let client: Client; - try { - client = NetworkManager.getClient(serverUrl); - } catch (error) { - return {error}; - } - try { + const client = NetworkManager.getClient(serverUrl); const res = await client.enableChannelCalls(channelId, enable); if (res.enabled === enable) { setChannelEnabled(serverUrl, channelId, enable); } + return {}; } catch (error) { - await forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on enableChannelCalls', getFullErrorMessage(error)); + await forceLogoutIfNecessary(serverUrl, error); return {error}; } - - return {}; }; export const joinCall = async ( @@ -240,7 +211,7 @@ export const joinCall = async ( userId: string, hasMicPermission: boolean, title?: string, -): Promise<{ error?: string | Error; data?: string }> => { +): Promise<{ error?: unknown; data?: string }> => { // Edge case: calls was disabled when app loaded, and then enabled, but app hasn't // reconnected its websocket since then (i.e., hasn't called batchLoadCalls yet) const {data: enabled} = await checkIsCallsPluginEnabled(serverUrl); @@ -259,9 +230,9 @@ export const joinCall = async ( connection = await newConnection(serverUrl, channelId, () => { myselfLeftCall(); }, setScreenShareURL, hasMicPermission, title); - } catch (error: unknown) { - await forceLogoutIfNecessary(serverUrl, error as ClientError); - return {error: error as Error}; + } catch (error) { + await forceLogoutIfNecessary(serverUrl, error); + return {error}; } try { @@ -419,52 +390,43 @@ export const getEndCallMessage = async (serverUrl: string, channelId: string, cu }; export const endCall = async (serverUrl: string, channelId: string) => { - const client = NetworkManager.getClient(serverUrl); - - let data: ApiResp; try { - data = await client.endCall(channelId); + const client = NetworkManager.getClient(serverUrl); + const data = await client.endCall(channelId); + return data; } catch (error) { - await forceLogoutIfNecessary(serverUrl, error as ClientError); + logDebug('error on endCall', getFullErrorMessage(error)); + await forceLogoutIfNecessary(serverUrl, error); throw error; } - - return data; }; export const startCallRecording = async (serverUrl: string, callId: string) => { needsRecordingErrorAlert(); - - const client = NetworkManager.getClient(serverUrl); - - let data: ApiResp | CallRecordingState; try { - data = await client.startCallRecording(callId); + const client = NetworkManager.getClient(serverUrl); + const data = await client.startCallRecording(callId); + return data; } catch (error) { - await forceLogoutIfNecessary(serverUrl, error as ClientError); - logWarning('start call recording returned:', error); + logDebug('error on startCallRecording', getFullErrorMessage(error)); + await forceLogoutIfNecessary(serverUrl, error); return error; } - - return data; }; export const stopCallRecording = async (serverUrl: string, callId: string) => { needsRecordingWillBePostedAlert(); needsRecordingErrorAlert(); - const client = NetworkManager.getClient(serverUrl); - - let data: ApiResp | CallRecordingState; try { - data = await client.stopCallRecording(callId); + const client = NetworkManager.getClient(serverUrl); + const data = await client.stopCallRecording(callId); + return data; } catch (error) { - await forceLogoutIfNecessary(serverUrl, error as ClientError); - logWarning('stop call recording returned:', error); + logDebug('error on stopCallRecording', getFullErrorMessage(error)); + await forceLogoutIfNecessary(serverUrl, error); return error; } - - return data; }; // handleCallsSlashCommand will return true if the slash command was handled @@ -610,7 +572,7 @@ const handleEndCall = async (serverUrl: string, channelId: string, currentUserId try { await endCall(serverUrl, channelId); } catch (e) { - const err = (e as ClientError).message || 'unable to complete command, see server logs'; + const err = getFullErrorMessage(e); Alert.alert('Error', `Error: ${err}`); } }, diff --git a/app/products/calls/connection/connection.ts b/app/products/calls/connection/connection.ts index de04f1264..f061da612 100644 --- a/app/products/calls/connection/connection.ts +++ b/app/products/calls/connection/connection.ts @@ -14,6 +14,7 @@ import {getICEServersConfigs} from '@calls/utils'; import {WebsocketEvents} from '@constants'; import {getServerCredentials} from '@init/credentials'; import NetworkManager from '@managers/network_manager'; +import {getFullErrorMessage} from '@utils/errors'; import {logDebug, logError, logInfo, logWarning} from '@utils/log'; import {WebSocketClient, wsReconnectionTimeoutErr} from './websocket_client'; @@ -186,7 +187,7 @@ export async function newConnection( try { config = await client.getCallsConfig(); } catch (err) { - logError('FETCHING CALLS CONFIG:', err); + logError('FETCHING CALLS CONFIG:', getFullErrorMessage(err)); return; } @@ -195,7 +196,7 @@ export async function newConnection( try { iceConfigs.push(...await client.genTURNCredentials()); } catch (err) { - logWarning('failed to fetch TURN credentials:', err); + logWarning('failed to fetch TURN credentials:', getFullErrorMessage(err)); } } diff --git a/app/products/calls/hooks.ts b/app/products/calls/hooks.ts index eba430542..e92426eab 100644 --- a/app/products/calls/hooks.ts +++ b/app/products/calls/hooks.ts @@ -14,9 +14,9 @@ import {errorAlert} from '@calls/utils'; import {useServerUrl} from '@context/server'; import {useAppState} from '@hooks/device'; import NetworkManager from '@managers/network_manager'; +import {getFullErrorMessage} from '@utils/errors'; import type {Client} from '@client/rest'; -import type ClientError from '@client/rest/error'; export const useTryCallsFunction = (fn: () => void) => { const intl = useIntl(); @@ -29,11 +29,19 @@ export const useTryCallsFunction = (fn: () => void) => { try { client = NetworkManager.getClient(serverUrl); } catch (error) { - setClientError((error as ClientError).message); + setClientError(getFullErrorMessage(error)); } } const tryFn = useCallback(async () => { - if (client && await client.getEnabled()) { + let enabled; + try { + enabled = await client?.getEnabled(); + } catch (error) { + errorAlert(getFullErrorMessage(error), intl); + return; + } + + if (enabled) { setMsgPostfix(''); fn(); return; diff --git a/app/queries/servers/categories.ts b/app/queries/servers/categories.ts index 4f2ffdf66..c3bbc778f 100644 --- a/app/queries/servers/categories.ts +++ b/app/queries/servers/categories.ts @@ -36,6 +36,7 @@ export const queryCategoriesByTeamIds = (database: Database, teamIds: string[]) export async function prepareCategoriesAndCategoriesChannels(operator: ServerDataOperator, categories: CategoryWithChannels[], prune = false) { try { + const {database} = operator; const modelPromises: Array> = [ prepareCategories(operator, categories), prepareCategoryChannels(operator, categories), @@ -49,7 +50,7 @@ export async function prepareCategoriesAndCategoriesChannels(operator: ServerDat // If the passed categories have more than one team, we want to update across teams const teamIds = pluckUnique('team_id')(categories) as string[]; - const localCategories = await queryCategoriesByTeamIds(operator.database, teamIds).fetch(); + const localCategories = await queryCategoriesByTeamIds(database, teamIds).fetch(); const customCategories = localCategories.filter((c) => c.type === 'custom'); for await (const custom of customCategories) { if (!remoteCategoryIds.has(custom.id)) { diff --git a/app/queries/servers/channel.ts b/app/queries/servers/channel.ts index e679ea64d..cdb7e3646 100644 --- a/app/queries/servers/channel.ts +++ b/app/queries/servers/channel.ts @@ -332,7 +332,8 @@ export const observeCurrentChannel = (database: Database) => { export async function deleteChannelMembership(operator: ServerDataOperator, userId: string, channelId: string, prepareRecordsOnly = false) { try { - const channelMembership = await operator.database.get(CHANNEL_MEMBERSHIP).query(Q.where('user_id', Q.eq(userId)), Q.where('channel_id', Q.eq(channelId))).fetch(); + const {database} = operator; + const channelMembership = await database.get(CHANNEL_MEMBERSHIP).query(Q.where('user_id', Q.eq(userId)), Q.where('channel_id', Q.eq(channelId))).fetch(); const models: Model[] = []; for (const membership of channelMembership) { models.push(membership.prepareDestroyPermanently()); diff --git a/app/queries/servers/system.ts b/app/queries/servers/system.ts index 0af3d5938..06a2e04ff 100644 --- a/app/queries/servers/system.ts +++ b/app/queries/servers/system.ts @@ -327,7 +327,8 @@ export const observeWebsocketLastDisconnected = (database: Database) => { }; export const resetWebSocketLastDisconnected = async (operator: ServerDataOperator, prepareRecordsOnly = false) => { - const lastDisconnectedAt = await getWebSocketLastDisconnected(operator.database); + const {database} = operator; + const lastDisconnectedAt = await getWebSocketLastDisconnected(database); if (lastDisconnectedAt) { return operator.handleSystem({systems: [{ diff --git a/app/queries/servers/team.ts b/app/queries/servers/team.ts index d5a7fe96a..b13f95909 100644 --- a/app/queries/servers/team.ts +++ b/app/queries/servers/team.ts @@ -46,14 +46,16 @@ export const addChannelToTeamHistory = async (operator: ServerDataOperator, team let tch: TeamChannelHistory|undefined; try { + const {database} = operator; + // Exlude GLOBAL_THREADS from channel check if (channelId !== Screens.GLOBAL_THREADS) { - const myChannel = (await operator.database.get(MY_CHANNEL).find(channelId)); + const myChannel = (await database.get(MY_CHANNEL).find(channelId)); if (!myChannel) { return []; } } - const teamChannelHistory = await getTeamChannelHistory(operator.database, teamId); + const teamChannelHistory = await getTeamChannelHistory(database, teamId); const channelIdSet = new Set(teamChannelHistory); if (channelIdSet.has(channelId)) { channelIdSet.delete(channelId); @@ -111,7 +113,8 @@ export const removeChannelFromTeamHistory = async (operator: ServerDataOperator, let tch: TeamChannelHistory; try { - const teamChannelHistory = await getTeamChannelHistory(operator.database, teamId); + const {database} = operator; + const teamChannelHistory = await getTeamChannelHistory(database, teamId); const channelIdSet = new Set(teamChannelHistory); if (channelIdSet.has(channelId)) { channelIdSet.delete(channelId); @@ -132,7 +135,8 @@ export const removeChannelFromTeamHistory = async (operator: ServerDataOperator, }; export const addTeamToTeamHistory = async (operator: ServerDataOperator, teamId: string, prepareRecordsOnly = false) => { - const teamHistory = (await getTeamHistory(operator.database)); + const {database} = operator; + const teamHistory = (await getTeamHistory(database)); const teamHistorySet = new Set(teamHistory); if (teamHistorySet.has(teamId)) { teamHistorySet.delete(teamId); @@ -144,7 +148,8 @@ export const addTeamToTeamHistory = async (operator: ServerDataOperator, teamId: }; export const removeTeamFromTeamHistory = async (operator: ServerDataOperator, teamId: string, prepareRecordsOnly = false) => { - const teamHistory = (await getTeamHistory(operator.database)); + const {database} = operator; + const teamHistory = (await getTeamHistory(database)); const teamHistorySet = new Set(teamHistory); if (!teamHistorySet.has(teamId)) { return undefined; diff --git a/app/queries/servers/thread.ts b/app/queries/servers/thread.ts index e7506c142..287d72209 100644 --- a/app/queries/servers/thread.ts +++ b/app/queries/servers/thread.ts @@ -114,6 +114,7 @@ export const prepareThreadsFromReceivedPosts = async (operator: ServerDataOperat const models: Model[] = []; const threads: ThreadWithLastFetchedAt[] = []; const toUpdate: {[rootId: string]: number | undefined} = {}; + const {database} = operator; let processedThreads: Set | undefined; posts.forEach((post: Post) => { @@ -139,7 +140,7 @@ export const prepareThreadsFromReceivedPosts = async (operator: ServerDataOperat const toUpdateKeys = Object.keys(toUpdate); if (toUpdateKeys.length) { - const toUpdateThreads = await Promise.all(toUpdateKeys.map((key) => getThreadById(operator.database, key))); + const toUpdateThreads = await Promise.all(toUpdateKeys.map((key) => getThreadById(database, key))); for (const thread of toUpdateThreads) { if (thread && !processedThreads?.has(thread.id)) { const model = thread.prepareUpdate((record) => { diff --git a/app/screens/apps_form/index.tsx b/app/screens/apps_form/index.tsx index e9fd225fd..2270c6760 100644 --- a/app/screens/apps_form/index.tsx +++ b/app/screens/apps_form/index.tsx @@ -68,11 +68,11 @@ function AppsFormContainer({ const creq = createCallRequest(currentForm.submit, context, {}, submission); const res = await doAppSubmit(serverUrl, creq, intl); - if (res.error) { + if ('error' in res) { return res; } - const callResp = res.data!; + const callResp = res.data; switch (callResp.type) { case AppCallResponseTypes.OK: if (callResp.text) { diff --git a/app/screens/custom_status/custom_status.tsx b/app/screens/custom_status/custom_status.tsx index ec949973c..0ca8ba7b1 100644 --- a/app/screens/custom_status/custom_status.tsx +++ b/app/screens/custom_status/custom_status.tsx @@ -270,9 +270,9 @@ const CustomStatus = ({ dispatchStatus({type: 'fromUserCustomStatus', status}); } } else if (storedStatus?.emoji) { - const unsetResponse = await unsetCustomStatus(serverUrl); + const {error} = await unsetCustomStatus(serverUrl); - if (unsetResponse?.data) { + if (!error) { updateLocalCustomStatus(serverUrl, currentUser, undefined); } } diff --git a/app/screens/edit_profile/components/profile_error.tsx b/app/screens/edit_profile/components/profile_error.tsx index b8a1742ba..05a073beb 100644 --- a/app/screens/edit_profile/components/profile_error.tsx +++ b/app/screens/edit_profile/components/profile_error.tsx @@ -11,7 +11,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; type DisplayErrorProps = { - error: Partial | string; + error: unknown; } const getStyleSheet = makeStyleSheetFromTheme((theme) => { diff --git a/app/screens/edit_profile/edit_profile.tsx b/app/screens/edit_profile/edit_profile.tsx index fe6a52de0..6b3c5662e 100644 --- a/app/screens/edit_profile/edit_profile.tsx +++ b/app/screens/edit_profile/edit_profile.tsx @@ -61,7 +61,7 @@ const EditProfile = ({ username: currentUser.username, }); const [canSave, setCanSave] = useState(false); - const [error, setError] = useState(); + const [error, setError] = useState(); const [updating, setUpdating] = useState(false); const buttonText = intl.formatMessage({id: 'mobile.account.settings.save', defaultMessage: 'Save'}); @@ -135,7 +135,7 @@ const EditProfile = ({ const now = Date.now(); const {error: uploadError} = await uploadUserProfileImage(serverUrl, localPath); if (uploadError) { - resetScreen(uploadError as Error); + resetScreen(uploadError); return; } updateLocalUser(serverUrl, {last_picture_update: now}); @@ -146,7 +146,7 @@ const EditProfile = ({ if (hasUpdateUserInfo.current) { const {error: reqError} = await updateMe(serverUrl, newUserInfo); if (reqError) { - resetScreen(reqError as Error); + resetScreen(reqError); return; } } @@ -154,7 +154,7 @@ const EditProfile = ({ close(); return; } catch (e) { - resetScreen(e as Error); + resetScreen(e); } }), [userInfo, enableSaveButton]); @@ -179,8 +179,8 @@ const EditProfile = ({ enableSaveButton(didChange); }, [userInfo, currentUser, enableSaveButton]); - const resetScreen = useCallback((resetError: Error) => { - setError(resetError?.message); + const resetScreen = useCallback((resetError: unknown) => { + setError(resetError); Keyboard.dismiss(); setUpdating(false); enableSaveButton(true); @@ -217,7 +217,7 @@ const EditProfile = ({ style={styles.flex} > {updating && } - {Boolean(error) && } + {Boolean(error) && } { - const {error} = await joinChannelIfNeeded(serverUrl, c.id); + const res = await joinChannelIfNeeded(serverUrl, c.id); const displayName = 'display_name' in c ? c.display_name : c.displayName; - if (error) { + if ('error' in res) { Alert.alert( '', formatMessage({ diff --git a/app/screens/gallery/footer/download_with_action/index.tsx b/app/screens/gallery/footer/download_with_action/index.tsx index ac804ca8f..100e673ad 100644 --- a/app/screens/gallery/footer/download_with_action/index.tsx +++ b/app/screens/gallery/footer/download_with_action/index.tsx @@ -21,9 +21,11 @@ import Toast from '@components/toast'; import {GALLERY_FOOTER_HEIGHT} from '@constants/gallery'; import {useServerUrl} from '@context/server'; import {alertFailedToOpenDocument} from '@utils/document'; +import {getFullErrorMessage} from '@utils/errors'; import {fileExists, getLocalFilePathFromFile, hasWriteStoragePermission} from '@utils/file'; import {pathWithPrefix} from '@utils/files'; import {galleryItemToFileInfo} from '@utils/gallery'; +import {logDebug} from '@utils/log'; import {typography} from '@utils/typography'; import type {ClientResponse, ProgressPromise} from '@mattermost/react-native-network-client'; @@ -286,6 +288,7 @@ const DownloadWithAction = ({action, item, onDownloadSuccess, setAction, gallery } } } catch (e) { + logDebug('error on startDownload', getFullErrorMessage(e)); setShowToast(false); } }; diff --git a/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx b/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx index afe81ff9b..efe10083e 100644 --- a/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx +++ b/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx @@ -240,14 +240,14 @@ const ServerItem = ({ setSwitching(true); const result = await doPing(server.url, true); if (result.error) { - alertServerError(intl, result.error as ClientErrorProps); + alertServerError(intl, result.error); setSwitching(false); return; } const data = await fetchConfigAndLicense(server.url, true); if (data.error) { - alertServerError(intl, data.error as ClientErrorProps); + alertServerError(intl, data.error); setSwitching(false); return; } diff --git a/app/screens/login/form.tsx b/app/screens/login/form.tsx index a9d91d902..fca72a13b 100644 --- a/app/screens/login/form.tsx +++ b/app/screens/login/form.tsx @@ -8,7 +8,6 @@ import {Keyboard, TextInput, TouchableOpacity, View} from 'react-native'; import Button from 'react-native-button'; import {login} from '@actions/remote/session'; -import ClientError from '@client/rest/error'; import CompassIcon from '@components/compass_icon'; import FloatingTextInput from '@components/floating_text_input_label'; import FormattedText from '@components/formatted_text'; @@ -17,7 +16,7 @@ import {FORGOT_PASSWORD, MFA} from '@constants/screens'; import {t} from '@i18n'; import {goToScreen, loginAnimationOptions, resetToHome} from '@screens/navigation'; import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; -import {isServerError} from '@utils/errors'; +import {getFullErrorMessage, isErrorWithMessage, isServerError} from '@utils/errors'; import {preventDoubleTap} from '@utils/tap'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; @@ -99,11 +98,11 @@ const LoginForm = ({config, extra, serverDisplayName, launchError, launchType, l const signIn = async () => { const result: LoginActionResponse = await login(serverUrl!, {serverDisplayName, loginId: loginId.toLowerCase(), password, config, license}); if (checkLoginResponse(result)) { - goToHome(result.error as never); + goToHome(result.error); } }; - const goToHome = (loginError?: never) => { + const goToHome = (loginError?: unknown) => { const hasError = launchError || Boolean(loginError); resetToHome({extra, launchError: hasError, launchType, serverUrl}); }; @@ -136,18 +135,10 @@ const LoginForm = ({config, extra, serverDisplayName, launchError, launchType, l goToScreen(MFA, '', {goToHome, loginId, password, config, serverDisplayName, license, serverUrl, theme}, loginAnimationOptions()); }; - const getLoginErrorMessage = (loginError: string | ClientErrorProps | Error) => { - if (typeof loginError === 'string') { - return loginError; - } - - if (loginError instanceof ClientError) { + const getLoginErrorMessage = (loginError: unknown) => { + if (isServerError(loginError)) { const errorId = loginError.server_error_id; - if (!errorId && loginError.message) { - return loginError.message; - } - - if (errorId === 'api.user.login.invalid_credentials_email_username' || !errorId) { + if (errorId === 'api.user.login.invalid_credentials_email_username' || (!isErrorWithMessage(loginError) && typeof loginError !== 'string')) { return intl.formatMessage({ id: 'login.invalid_credentials', defaultMessage: 'The email and password combination is incorrect', @@ -155,7 +146,7 @@ const LoginForm = ({config, extra, serverDisplayName, launchError, launchType, l } } - return loginError.message; + return getFullErrorMessage(loginError); }; const createLoginPlaceholder = () => { diff --git a/app/screens/mfa/index.tsx b/app/screens/mfa/index.tsx index 134631f41..91781076d 100644 --- a/app/screens/mfa/index.tsx +++ b/app/screens/mfa/index.tsx @@ -11,7 +11,6 @@ import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-nati import {SafeAreaView} from 'react-native-safe-area-context'; import {login} from '@actions/remote/session'; -import ClientError from '@client/rest/error'; import FloatingTextInput from '@components/floating_text_input_label'; import FormattedText from '@components/formatted_text'; import Loading from '@components/loading'; @@ -21,6 +20,7 @@ import {t} from '@i18n'; import Background from '@screens/background'; import {popTopScreen} from '@screens/navigation'; import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; +import {getErrorMessage} from '@utils/errors'; import {preventDoubleTap} from '@utils/tap'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; @@ -32,7 +32,7 @@ import type {AvailableScreens} from '@typings/screens/navigation'; type MFAProps = { componentId: AvailableScreens; config: Partial; - goToHome: (error?: never) => void; + goToHome: (error?: unknown) => void; license: Partial; loginId: string; password: string; @@ -143,20 +143,10 @@ const MFA = ({componentId, config, goToHome, license, loginId, password, serverD const result: LoginActionResponse = await login(serverUrl, {loginId, password, mfaToken: token, config, license, serverDisplayName}); setIsLoading(false); if (result?.error && result.failed) { - if (typeof result.error == 'string') { - setError(result?.error); - return; - } - - if (result.error instanceof ClientError && result.error.intl) { - setError(intl.formatMessage({id: result.error.intl.id, defaultMessage: result.error.intl.defaultMessage}, result.error.intl.values)); - return; - } - - setError(result.error.message); + setError(getErrorMessage(error, intl)); return; } - goToHome(result.error as never); + goToHome(result.error); }), [token]); const transform = useAnimatedStyle(() => { diff --git a/app/screens/server/index.tsx b/app/screens/server/index.tsx index 48bbfa33b..52cf20d3e 100644 --- a/app/screens/server/index.tsx +++ b/app/screens/server/index.tsx @@ -22,7 +22,7 @@ import NetworkManager from '@managers/network_manager'; import {getServerByDisplayName, getServerByIdentifier} from '@queries/app/servers'; import Background from '@screens/background'; import {dismissModal, goToScreen, loginAnimationOptions, popTopScreen} from '@screens/navigation'; -import {getErrorMessage} from '@utils/client_error'; +import {getErrorMessage} from '@utils/errors'; import {canReceiveNotifications} from '@utils/push_proxy'; import {loginOptions} from '@utils/server'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; @@ -31,7 +31,6 @@ import {getServerUrlAfterRedirect, isValidUrl, sanitizeUrl} from '@utils/url'; import ServerForm from './form'; import ServerHeader from './header'; -import type ClientError from '@client/rest/error'; import type {DeepLinkWithData, LaunchProps} from '@typings/launch'; import type {AvailableScreens} from '@typings/screens/navigation'; @@ -301,7 +300,7 @@ const Server = ({ const nurl = serverUrl.replace('https:', 'http:'); pingServer(nurl, false); } else { - setUrlError(getErrorMessage(result.error as ClientError, intl)); + setUrlError(getErrorMessage(result.error, intl)); setButtonDisabled(true); setConnecting(false); } @@ -312,7 +311,7 @@ const Server = ({ const data = await fetchConfigAndLicense(serverUrl, true); if (data.error) { setButtonDisabled(true); - setUrlError(getErrorMessage(data.error as ClientError, intl)); + setUrlError(getErrorMessage(data.error, intl)); setConnecting(false); return; } diff --git a/app/screens/sso/index.tsx b/app/screens/sso/index.tsx index c0b06bffd..0897168ba 100644 --- a/app/screens/sso/index.tsx +++ b/app/screens/sso/index.tsx @@ -9,13 +9,13 @@ import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-nati import {SafeAreaView} from 'react-native-safe-area-context'; import {ssoLogin} from '@actions/remote/session'; -import ClientError from '@client/rest/error'; import {Screens, Sso} from '@constants'; import useAndroidHardwareBackHandler from '@hooks/android_back_handler'; import useNavButtonPressed from '@hooks/navigation_button_pressed'; import NetworkManager from '@managers/network_manager'; import Background from '@screens/background'; import {dismissModal, popTopScreen, resetToHome} from '@screens/navigation'; +import {getFullErrorMessage, isErrorWithUrl} from '@utils/errors'; import {logWarning} from '@utils/log'; import SSOWithRedirectURL from './sso_with_redirect_url'; @@ -85,15 +85,15 @@ const SSO = ({ break; } - const onLoadEndError = (e: ClientErrorProps | Error | string) => { + const onLoadEndError = (e: unknown) => { logWarning('Failed to set store from local data', e); if (typeof e === 'string') { setLoginError(e); return; } - let errorMessage = e.message; - if (e instanceof ClientError && e.url) { + let errorMessage = getFullErrorMessage(e); + if (isErrorWithUrl(e) && e.url) { errorMessage += `\nURL: ${e.url}`; } setLoginError(errorMessage); @@ -105,10 +105,10 @@ const SSO = ({ onLoadEndError(result.error); return; } - goToHome(result.error as never); + goToHome(result.error); }; - const goToHome = (error?: never) => { + const goToHome = (error?: unknown) => { const hasError = launchError || Boolean(error); resetToHome({extra, launchError: hasError, launchType, serverUrl}); }; diff --git a/app/screens/sso/sso_with_redirect_url.tsx b/app/screens/sso/sso_with_redirect_url.tsx index 5eb39f7a6..7458363b9 100644 --- a/app/screens/sso/sso_with_redirect_url.tsx +++ b/app/screens/sso/sso_with_redirect_url.tsx @@ -12,6 +12,7 @@ import FormattedText from '@components/formatted_text'; import {Sso} from '@constants'; import NetworkManager from '@managers/network_manager'; import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; +import {isErrorWithMessage} from '@utils/errors'; import {isBetaApp} from '@utils/general'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; @@ -82,9 +83,9 @@ const SSOWithRedirectURL = ({doSSOLogin, loginError, loginUrl, serverUrl, setLog parsedUrl.set('query', qs.stringify(query)); const url = parsedUrl.toString(); - const onError = (e: Error) => { + const onError = (e: unknown) => { let message; - if (e && Platform.OS === 'android' && e?.message?.match(/no activity found to handle intent/i)) { + if (e && Platform.OS === 'android' && isErrorWithMessage(e) && e.message.match(/no activity found to handle intent/i)) { message = intl.formatMessage({ id: 'mobile.oauth.failed_to_open_link_no_browser', defaultMessage: 'The link failed to open. Please verify that a browser is installed on the device.', diff --git a/app/utils/client_error.ts b/app/utils/client_error.ts deleted file mode 100644 index 111e72cd2..000000000 --- a/app/utils/client_error.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {cleanUrlForLogging} from '@utils/url'; - -import type {IntlShape} from 'react-intl'; - -export class ClientError extends Error { - details: Error; - intl?: {defaultMessage?: string; id: string} | { defaultMessage?: string; id: string } | { id: string; defaultMessage?: string; values: any } | { id: string; defaultMessage?: string }; - url: string; - server_error_id?: string | number; - status_code?: number; - - constructor(baseUrl: string, data: ClientErrorProps) { - super(`${data.message}: ${cleanUrlForLogging(baseUrl, data.url)}`); - - this.details = data.details; - this.intl = data.intl; - this.message = data.message; - this.server_error_id = data.server_error_id; - this.status_code = data.status_code; - this.url = data.url; - - // Ensure message is treated as a property of this class when object spreading. Without this, - // copying the object by using `{...error}` would not include the message. - Object.defineProperty(this, 'message', {enumerable: true}); - } -} - -export const getErrorMessage = (error: Error | string, intl: IntlShape) => { - const intlError = error as ClientError; - if (intlError.intl) { - return intl.formatMessage(intlError.intl); - } else if (error instanceof Error) { - return error.message; - } - - return error; -}; diff --git a/app/utils/error_handling.ts b/app/utils/error_handling.ts index 6c4f0c806..689279f8e 100644 --- a/app/utils/error_handling.ts +++ b/app/utils/error_handling.ts @@ -2,11 +2,7 @@ // See LICENSE.txt for license information. import {Alert} from 'react-native'; -import { - setJSExceptionHandler, - - // setNativeExceptionHandler -} from 'react-native-exception-handler'; +import {setJSExceptionHandler} from 'react-native-exception-handler'; import {DEFAULT_LOCALE, getTranslations, t} from '@i18n'; import {dismissAllModals, dismissAllOverlays} from '@screens/navigation'; @@ -19,8 +15,6 @@ import { import {logWarning} from './log'; -import type {ClientError} from '@utils/client_error'; - class JavascriptAndNativeErrorHandler { initializeErrorHandling = () => { initializeSentry(); @@ -34,7 +28,7 @@ class JavascriptAndNativeErrorHandler { captureException(e); }; - errorHandler = (e: Error | ClientError, isFatal: boolean) => { + errorHandler = (e: unknown, isFatal: boolean) => { if (__DEV__ && !e && !isFatal) { // react-native-exception-handler redirects console.error to call this, and React calls // console.error without an exception when prop type validation fails, so this ends up diff --git a/app/utils/errors.ts b/app/utils/errors.ts index 30db52c4b..f0451c048 100644 --- a/app/utils/errors.ts +++ b/app/utils/errors.ts @@ -1,11 +1,92 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -export function isServerError(obj: unknown): obj is {server_error_id: string; message?: string} { +import type {IntlShape} from 'react-intl'; + +export function isServerError(obj: unknown): obj is {server_error_id?: string} { return ( typeof obj === 'object' && obj !== null && - 'server_error_id' in obj && - typeof obj.server_error_id === 'string' + ( + !('server_error_id' in obj) || + typeof obj.server_error_id === 'string' || + typeof obj.server_error_id === 'undefined' + ) ); } + +export function isErrorWithMessage(obj: unknown): obj is {message: string} { + return ( + typeof obj === 'object' && + obj !== null && + 'message' in obj && + typeof obj.message === 'string' + ); +} + +export function isErrorWithDetails(obj: unknown): obj is {details: Error} { + return ( + typeof obj === 'object' && + obj !== null && + 'details' in obj + ); +} + +export function isErrorWithIntl(obj: unknown): obj is {intl: ClientErrorIntl} { + return ( + typeof obj === 'object' && + obj !== null && + 'intl' in obj && + typeof obj.intl === 'object' && + obj.intl !== null + ); +} + +export function isErrorWithStatusCode(obj: unknown): obj is {status_code: number} { + return ( + typeof obj === 'object' && + obj !== null && + 'status_code' in obj && + typeof obj.status_code === 'number' + ); +} + +export function isErrorWithUrl(obj: unknown): obj is {url?: string} { + return ( + typeof obj === 'object' && + obj !== null && + ( + !('url' in obj) || + typeof obj.url === 'string' || + typeof obj.url === 'undefined' + ) + ); +} + +export const getFullErrorMessage = (error: unknown, intl?: IntlShape, depth = 0): string => { + const message = getErrorMessage(error, intl); + if (isErrorWithDetails(error)) { + if (depth > 2) { + return `${message}; ${getErrorMessage(error, intl)}`; + } + + return `${message}; ${getFullErrorMessage(error.details, intl, depth + 1)}`; + } + + return message; +}; + +export const getErrorMessage = (error: unknown, intl?: IntlShape) => { + if (typeof error === 'string') { + return error; + } + if (isErrorWithIntl(error)) { + return intl ? intl.formatMessage({id: error.intl.id, defaultMessage: error.intl.defaultMessage}, error.intl.values) : error.intl.defaultMessage; + } + + if (isErrorWithMessage(error)) { + return error.message; + } + + return 'Unknown error'; +}; diff --git a/app/utils/navigation/index.ts b/app/utils/navigation/index.ts index 4572c5c7b..e3acae967 100644 --- a/app/utils/navigation/index.ts +++ b/app/utils/navigation/index.ts @@ -5,7 +5,7 @@ import {Alert} from 'react-native'; import {Navigation, type Options} from 'react-native-navigation'; import {Screens, ServerErrors} from '@constants'; -import {isServerError} from '@utils/errors'; +import {isErrorWithMessage, isServerError} from '@utils/errors'; import type {AvailableScreens} from '@typings/screens/navigation'; import type {IntlShape} from 'react-intl'; @@ -85,7 +85,7 @@ export function alertTeamAddError(error: unknown, intl: IntlShape) { id: 'join_team.error.group_error', defaultMessage: 'You need to be a member of a linked group to join this team.', }); - } else if (error.message) { + } else if (isErrorWithMessage(error) && error.message) { errMsg = error.message; } } diff --git a/app/utils/sentry.ts b/app/utils/sentry.ts index aa88b2c2e..0aebbf9ae 100644 --- a/app/utils/sentry.ts +++ b/app/utils/sentry.ts @@ -5,12 +5,13 @@ import {Platform} from 'react-native'; import {Navigation} from 'react-native-navigation'; import Config from '@assets/config.json'; +import ClientError from '@client/rest/error'; import DatabaseManager from '@database/manager'; import {getConfig} from '@queries/servers/system'; import {getCurrentUser} from '@queries/servers/user'; +import {getFullErrorMessage} from '@utils/errors'; import {isBetaApp} from '@utils/general'; -import {ClientError} from './client_error'; import {logError, logWarning} from './log'; import type {Database} from '@nozbe/watermelondb'; @@ -83,7 +84,7 @@ function getDsn() { return ''; } -export function captureException(error: Error | string) { +export function captureException(error: unknown) { if (!Config.SentryEnabled) { return; } @@ -95,7 +96,7 @@ export function captureException(error: Error | string) { Sentry.captureException(error); } -export function captureJSException(error: Error | ClientError, isFatal: boolean) { +export function captureJSException(error: unknown, isFatal: boolean) { if (!Config.SentryEnabled) { return; } @@ -120,14 +121,9 @@ function captureClientErrorAsBreadcrumb(error: ClientError, isFatal: boolean) { isFatal: String(isFatal), }, level: 'warning', + message: getFullErrorMessage(error), }; - if (error.intl?.defaultMessage) { - breadcrumb.message = error.intl.defaultMessage; - } else { - breadcrumb.message = error.message; - } - if (breadcrumb.data) { if (error.server_error_id) { breadcrumb.data.server_error_id = error.server_error_id; diff --git a/app/utils/server/index.ts b/app/utils/server/index.ts index fb6e4a966..870500010 100644 --- a/app/utils/server/index.ts +++ b/app/utils/server/index.ts @@ -6,7 +6,7 @@ import {Alert, type AlertButton} from 'react-native'; import CompassIcon from '@components/compass_icon'; import {Screens, Sso, SupportedServer, Launch} from '@constants'; import {dismissBottomSheet, showModal} from '@screens/navigation'; -import {getErrorMessage} from '@utils/client_error'; +import {getErrorMessage} from '@utils/errors'; import {isMinimumServerVersion} from '@utils/helpers'; import {changeOpacity} from '@utils/theme'; import {tryOpenURL} from '@utils/url'; @@ -175,7 +175,7 @@ export async function alertServerRemove(displayName: string, onPress: () => void ); } -export function alertServerError(intl: IntlShape, error: ClientErrorProps) { +export function alertServerError(intl: IntlShape, error: unknown) { const message = getErrorMessage(error, intl); Alert.alert( intl.formatMessage({ diff --git a/app/utils/url/index.ts b/app/utils/url/index.ts index c4af29937..57e08dc7a 100644 --- a/app/utils/url/index.ts +++ b/app/utils/url/index.ts @@ -176,7 +176,7 @@ export function getYouTubeVideoId(link?: string) { return ''; } -export function tryOpenURL(url: string, onError = emptyFunction, onSuccess = emptyFunction) { +export function tryOpenURL(url: string, onError: (error: unknown) => void = emptyFunction, onSuccess = emptyFunction) { Linking.openURL(url). then(onSuccess). catch(onError); diff --git a/package-lock.json b/package-lock.json index 62546a7ca..3ea2b755c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24249,7 +24249,7 @@ }, "@mattermost/calls": { "version": "git+ssh://git@github.com/mattermost/calls-common.git#d539eb36f9549075b0a0a6578174db31297c45ae", - "from": "@mattermost/calls@git://github.com/mattermost/calls-common.git#v0.14.0" + "from": "@mattermost/calls@github:mattermost/calls-common#v0.14.0" }, "@mattermost/commonmark": { "version": "0.30.1-0", diff --git a/types/api/client.d.ts b/types/api/client.d.ts index fca3e7882..eeceeb39a 100644 --- a/types/api/client.d.ts +++ b/types/api/client.d.ts @@ -11,14 +11,17 @@ type ClientOptions = { headers?: Record; }; -interface ClientErrorProps extends Error { - details: Error; - intl?: - {defaultMessage?: string; id: string; values?: Record} | - { defaultMessage?: string; id: string; values?: Record } | - { id: string; defaultMessage?: string; values?: Record } | - { id: string; defaultMessage?: string; values?: Record }; +type ClientErrorIntl = + {defaultMessage?: string; id: string; values?: Record} | + {defaultMessage?: string; id: string; values?: Record} | + {id: string; defaultMessage?: string; values?: Record} | + {id: string; defaultMessage?: string; values?: Record}; + +interface ClientErrorProps { + details?: unknown; + intl?: ClientErrorIntl; url: string; server_error_id?: string; status_code?: number; + message: string; } diff --git a/types/api/session.d.ts b/types/api/session.d.ts index 3a2c2ff80..576cb1ad4 100644 --- a/types/api/session.d.ts +++ b/types/api/session.d.ts @@ -14,6 +14,6 @@ interface Session { } interface LoginActionResponse { - error?: ClientErrorProps | Error | string; + error?: unknown; failed: boolean; } diff --git a/types/global/error_handling.ts b/types/global/error_handling.d.ts similarity index 54% rename from types/global/error_handling.ts rename to types/global/error_handling.d.ts index 37351db0e..b34ea9906 100644 --- a/types/global/error_handling.ts +++ b/types/global/error_handling.d.ts @@ -1,10 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import type {ClientError} from '@utils/client_error'; - -export interface jsAndNativeErrorHandler { +interface jsAndNativeErrorHandler { initializeErrorHandling: () => void; nativeErrorHandler: (e: string) => void; - errorHandler: (e: Error | ClientError, isFatal: boolean) => void; + errorHandler: (e: unknown, isFatal: boolean) => void; } diff --git a/types/utils/file.d.ts b/types/utils/file.d.ts index 70eb5b152..b38fd5679 100644 --- a/types/utils/file.d.ts +++ b/types/utils/file.d.ts @@ -1,8 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -type ErrorText = Partial | string; - type ExtractedFileInfo = Partial & { name: string; mime_type: string} type UploadExtractedFile = (files?: ExtractedFileInfo[]) => void;