Refactor errors around the app (#7306)

* Refactor errors around the app

* Fix recursive function

* Fix tests
This commit is contained in:
Daniel Espino García 2023-05-03 13:08:55 +02:00 committed by GitHub
parent e06f85d408
commit 86658edc30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
82 changed files with 1079 additions and 1959 deletions

View file

@ -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<Res=unknown>(serverUrl: string, binding
return doAppSubmit<Res>(serverUrl, callRequest, intl);
}
export async function doAppSubmit<Res=unknown>(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<Res=unknown>(serverUrl: string, inCall: AppCallRequest, intl: IntlShape): Promise<{data: AppCallResponse<Res>} | {error: AppCallResponse<Res>}> {
try {
const client = NetworkManager.getClient(serverUrl);
const call: AppCallRequest = {
...inCall,
context: {
@ -116,23 +111,18 @@ export async function doAppSubmit<Res=unknown>(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<Res=unknown>(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<Res>(call, false);
const responseType = res.type || AppCallResponseTypes.OK;
@ -157,11 +147,12 @@ export async function doAppFetchForm<Res=unknown>(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<Res=unknown>(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<Res>(call, false);
const responseType = res.type || AppCallResponseTypes.OK;
@ -193,10 +179,11 @@ export async function doAppLookup<Res=unknown>(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)};
}
}

View file

@ -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<CategoriesRequest> => {
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};
}
};

View file

@ -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<Channel> & {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<Chan
});
models.push(channelInfo);
}
const channel = await getChannelById(operator.database, channelData.id);
const channel = await getChannelById(database, channelData.id);
if (channel && (channel.displayName !== channelData.display_name || channel.type !== channelData.type)) {
channel.prepareUpdate((v) => {
v.displayName = channelData.display_name;
@ -302,26 +268,18 @@ export async function patchChannel(serverUrl: string, channelPatch: Partial<Chan
}
return {channel: channelData};
} catch (error) {
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
logDebug('error on patchChannel', getFullErrorMessage(error));
forceLogoutIfNecessary(serverUrl, error);
return {error};
}
}
export async function leaveChannel(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};
}
EphemeralStore.addLeavingChannel(channelId);
const client = NetworkManager.getClient(serverUrl);
const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
try {
const {database} = operator;
const isTabletDevice = await isTablet();
const user = await getCurrentUser(database);
const models: Model[] = [];
@ -330,7 +288,6 @@ export async function leaveChannel(serverUrl: string, channelId: string) {
return {error: 'current user not found'};
}
EphemeralStore.addLeavingChannel(channelId);
await client.removeFromChannel(user.id, channelId);
if (user.isGuest) {
@ -352,9 +309,10 @@ export async function leaveChannel(serverUrl: string, channelId: string) {
} else {
setCurrentChannelId(operator, '');
}
return {error: undefined};
return {};
} catch (error) {
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
logDebug('error on leaveChannel', getFullErrorMessage(error));
forceLogoutIfNecessary(serverUrl, error);
return {error};
} finally {
EphemeralStore.removeLeavingChannel(channelId);
@ -362,21 +320,12 @@ export async function leaveChannel(serverUrl: string, channelId: string) {
}
export async function fetchChannelCreator(serverUrl: string, channelId: 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};
}
const client = NetworkManager.getClient(serverUrl);
const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
try {
const currentUserId = await getCurrentUserId(operator.database);
const channel = await getChannelById(operator.database, channelId);
const currentUserId = await getCurrentUserId(database);
const channel = await getChannelById(database, channelId);
if (channel && channel.creatorId) {
const user = await client.getUser(channel.creatorId);
@ -403,28 +352,20 @@ export async function fetchChannelCreator(serverUrl: string, channelId: string,
return {user: undefined};
} catch (error) {
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
logDebug('error on fetchChannelCreator', getFullErrorMessage(error));
forceLogoutIfNecessary(serverUrl, error);
return {error};
}
}
export async function fetchChannelStats(serverUrl: string, channelId: 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};
}
const client = NetworkManager.getClient(serverUrl);
const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
try {
const stats = await client.getChannelStats(channelId);
if (!fetchOnly) {
const channel = await getChannelById(operator.database, channelId);
const channel = await getChannelById(database, channelId);
if (channel) {
const channelInfos: Array<Partial<ChannelInfo>> = [{
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<MyChannelsRequest> {
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<MyChannelsRequest> {
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<ChannelNotifyProps>) => {
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);

View file

@ -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};
}
};

View file

@ -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<string>();
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<Promise<CustomEmoji>> = [];
for (const name of names) {
promises.push(client.getCustomEmojiByName(name));
}
try {
const promises: Array<Promise<CustomEmoji>> = [];
for (const name of names) {
promises.push(client.getCustomEmojiByName(name));
}
const emojisResult = await Promise.allSettled(promises);
const emojis = emojisResult.reduce<CustomEmoji[]>((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, () => {

View file

@ -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
}
}

View file

@ -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) {

View file

@ -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`};

View file

@ -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');

View file

@ -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};
}
};

View file

@ -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};
}
};

View file

@ -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) => {

View file

@ -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};
}
};

View file

@ -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};
}
};

View file

@ -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};
}
};

View file

@ -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};
}

View file

@ -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<Post>, 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<Post>, 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<Post>, 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<PostsRequest> {
@ -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<PostsRequest> {
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<Promise<UserProfile[]>> = [];
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<PostModel>(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<PostsObjectsRequest>([
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<PostModel>(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<Promise<Channel>> = [];
const userPromises: Array<Promise<ChannelMembership>> = [];
@ -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<Promise<Model[]>> = [];
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};
}
}

View file

@ -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<MyPreferencesRequest> => {
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};
}
};

View file

@ -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) {

View file

@ -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 {

View file

@ -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};
}
};

View file

@ -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<PostSearch
export const searchPosts = async (serverUrl: string, teamId: string, params: PostSearchParams): Promise<PostSearchRequest> => {
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<Promise<Model[]>> = [];
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};
}
};

View file

@ -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<LoginActionResponse> => {
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};
}
};

View file

@ -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<DataRetentionPoliciesRequest> => {
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<TeamDataRetentionPolicy | ChannelDataRetentionPolicy> = [],
): Promise<{data?: Array<TeamDataRetentionPolicy | ChannelDataRetentionPolicy>; 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<ConfigAndLicenseRequest> => {
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};
}
};

View file

@ -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<MyTeamsRequest> {
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<Promise<Model[]>> = [];
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<MyTeamsRequest> {
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};
}
}

View file

@ -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};
}
}

View file

@ -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};
}

View file

@ -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<MyUserRequest> => {
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<UserProfile>, Promise<UserStatus>]>([
client.getMe(),
client.getStatus('me'),
@ -61,34 +56,27 @@ export const fetchMe = async (serverUrl: string, fetchOnly = false): Promise<MyU
user.status = userStatus.status;
if (!fetchOnly) {
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
if (operator) {
await operator.handleUsers({users: [user], prepareRecordsOnly: false});
}
await operator.handleUsers({users: [user], prepareRecordsOnly: false});
}
return {user};
} catch (error) {
await forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
logDebug('error on fetchMe', getFullErrorMessage(error));
await forceLogoutIfNecessary(serverUrl, error);
return {error};
}
};
export async function fetchProfilesInChannel(serverUrl: string, channelId: string, excludeUserId?: string, options?: GetUsersOptions, fetchOnly = false): Promise<ProfilesInChannelRequest> {
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<Promise<Model[]>> = [];
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<ProfilesPerChannelRequest> {
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<ProfilesPerChannelRequest> {
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<UserProfile>) => {
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<string, UserStatus>, s) => {
result[s.user_id] = s;
return result;
}, {});
if (!fetchOnly) {
const users = await queryUsersById(database, userIds).fetch();
const userStatuses = statuses.reduce((result: Record<string, UserStatus>, 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<string, UserModel>, 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 [];
}
};

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -5,12 +5,9 @@ import DatabaseManager from '@database/manager';
import {queryReaction} from '@queries/servers/reaction';
export async function handleAddCustomEmoji(serverUrl: string, msg: WebSocketMessage): Promise<void> {
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<void> {
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<void> {
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();

View file

@ -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<void> {
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<void> {
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;
}

View file

@ -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<void> {
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<void> {
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);

View file

@ -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,
});

View file

@ -8,10 +8,8 @@ import EphemeralStore from '@store/ephemeral_store';
export async function handleThreadUpdatedEvent(serverUrl: string, msg: WebSocketMessage): Promise<void> {
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;

View file

@ -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};
}
};

View file

@ -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,
});

View file

@ -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;

View file

@ -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);

View file

@ -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();

View file

@ -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(
<ErrorTextComponent {...baseProps}/>,
);

View file

@ -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<ViewStyle> | StyleProp<TextStyle>;
}
@ -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 (
<FormattedText
testID={testID}
id={intl.id}
defaultMessage={intl.defaultMessage}
values={intl.values}
style={[style.errorLabel, textStyle]}
/>
);
}
const intl = useIntl();
const message = getErrorMessage(error, intl);
return (
<Text

View file

@ -14,8 +14,10 @@ import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import NetworkManager from '@managers/network_manager';
import {alertDownloadDocumentDisabled, alertDownloadFailed, alertFailedToOpenDocument} from '@utils/document';
import {getFullErrorMessage, isErrorWithMessage} from '@utils/errors';
import {fileExists, getLocalFilePathFromFile} from '@utils/file';
import {emptyFunction} from '@utils/general';
import {logDebug} from '@utils/log';
import FileIcon from './file_icon';
@ -90,7 +92,8 @@ const DocumentFile = forwardRef<DocumentFileRef, DocumentFileProps>(({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);
}
}

View file

@ -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;
}

View file

@ -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);
}
};

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -88,6 +88,7 @@ class NetworkManager {
defaultMessage: 'Cant find this server. Check spelling and URL format.',
},
url: serverUrl,
details: error,
});
}

View file

@ -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}`);
}
},

View file

@ -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));
}
}

View file

@ -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;

View file

@ -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<Promise<Model[]>> = [
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)) {

View file

@ -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<ChannelMembershipModel>(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<ChannelMembershipModel>(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());

View file

@ -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: [{

View file

@ -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<MyChannelModel>(MY_CHANNEL).find(channelId));
const myChannel = (await database.get<MyChannelModel>(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;

View file

@ -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<string> | 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) => {

View file

@ -68,11 +68,11 @@ function AppsFormContainer({
const creq = createCallRequest(currentForm.submit, context, {}, submission);
const res = await doAppSubmit<FormResponseData>(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) {

View file

@ -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);
}
}

View file

@ -11,7 +11,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
type DisplayErrorProps = {
error: Partial<ClientErrorProps> | string;
error: unknown;
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {

View file

@ -61,7 +61,7 @@ const EditProfile = ({
username: currentUser.username,
});
const [canSave, setCanSave] = useState(false);
const [error, setError] = useState<ErrorText | undefined>();
const [error, setError] = useState<unknown>();
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 && <Updating/>}
{Boolean(error) && <ProfileError error={error!}/>}
{Boolean(error) && <ProfileError error={error}/>}
<View style={styles.top}>
<UserProfilePicture
currentUser={currentUser}

View file

@ -143,9 +143,9 @@ const FilteredList = ({
};
const onJoinChannel = useCallback(async (c: Channel | ChannelModel) => {
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({

View file

@ -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);
}
};

View file

@ -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;
}

View file

@ -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 = () => {

View file

@ -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<ClientConfig>;
goToHome: (error?: never) => void;
goToHome: (error?: unknown) => void;
license: Partial<ClientLicense>;
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(() => {

View file

@ -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;
}

View file

@ -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});
};

View file

@ -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.',

View file

@ -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;
};

View file

@ -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

View file

@ -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';
};

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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({

View file

@ -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);

2
package-lock.json generated
View file

@ -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",

17
types/api/client.d.ts vendored
View file

@ -11,14 +11,17 @@ type ClientOptions = {
headers?: Record<string, any>;
};
interface ClientErrorProps extends Error {
details: Error;
intl?:
{defaultMessage?: string; id: string; values?: Record<string, any>} |
{ defaultMessage?: string; id: string; values?: Record<string, any> } |
{ id: string; defaultMessage?: string; values?: Record<string, any> } |
{ id: string; defaultMessage?: string; values?: Record<string, any> };
type ClientErrorIntl =
{defaultMessage?: string; id: string; values?: Record<string, any>} |
{defaultMessage?: string; id: string; values?: Record<string, any>} |
{id: string; defaultMessage?: string; values?: Record<string, any>} |
{id: string; defaultMessage?: string; values?: Record<string, any>};
interface ClientErrorProps {
details?: unknown;
intl?: ClientErrorIntl;
url: string;
server_error_id?: string;
status_code?: number;
message: string;
}

View file

@ -14,6 +14,6 @@ interface Session {
}
interface LoginActionResponse {
error?: ClientErrorProps | Error | string;
error?: unknown;
failed: boolean;
}

View file

@ -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;
}

View file

@ -1,8 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
type ErrorText = Partial<ClientErrorProps> | string;
type ExtractedFileInfo = Partial<FileInfo> & { name: string; mime_type: string}
type UploadExtractedFile = (files?: ExtractedFileInfo[]) => void;