forked from Ivasoft/mattermost-mobile
[Gekidou] perf improvement & fix upgrade path (#6302)
* Improve loading display name of direct channels * Improve team switching * Improve perceived performance when opening the app with an active session * Fix upgrade path from v1 * Set moment locale while formatting date * feedback review
This commit is contained in:
@@ -15,7 +15,7 @@ import {getTeammateNameDisplaySetting} from '@helpers/api/preference';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
import {prepareMyChannelsForTeam, getChannelById, getChannelByName, getMyChannel, getChannelInfo, queryMyChannelSettingsByIds} from '@queries/servers/channel';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {getCommonSystemValues, getCurrentTeamId, getCurrentUserId, setCurrentChannelId} from '@queries/servers/system';
|
||||
import {getCommonSystemValues, getConfig, getCurrentTeamId, getCurrentUserId, getLicense, setCurrentChannelId} from '@queries/servers/system';
|
||||
import {prepareMyTeams, getNthLastChannelFromTeam, getMyTeamById, getTeamById, getTeamByName, queryMyTeams} from '@queries/servers/team';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
@@ -30,7 +30,7 @@ import {setDirectChannelVisible} from './preference';
|
||||
import {fetchRolesIfNeeded} from './role';
|
||||
import {forceLogoutIfNecessary} from './session';
|
||||
import {addUserToTeam, fetchTeamByName, removeUserFromTeam} from './team';
|
||||
import {fetchProfilesPerChannels, fetchUsersByIds, updateUsersNoLongerVisible} from './user';
|
||||
import {fetchProfilesInGroupChannels, fetchProfilesPerChannels, fetchUsersByIds, updateUsersNoLongerVisible} from './user';
|
||||
|
||||
import type {Client} from '@client/rest';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
@@ -426,61 +426,94 @@ export async function fetchMyChannel(serverUrl: string, teamId: string, channelI
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchMissingSidebarInfo(serverUrl: string, directChannels: Channel[], locale?: string, teammateDisplayNameSetting?: string, currentUserId?: string, fetchOnly = false) {
|
||||
const channelIds = directChannels.sort((a, b) => b.last_post_at - a.last_post_at).map((dc) => dc.id);
|
||||
const result = await fetchProfilesPerChannels(serverUrl, channelIds, currentUserId, false);
|
||||
if (result.error) {
|
||||
return {error: result.error};
|
||||
export async function fetchMissingDirectChannelsInfo(serverUrl: string, directChannels: Channel[], locale?: string, teammateDisplayNameSetting?: string, currentUserId?: string, fetchOnly = false) {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
|
||||
const {database} = operator;
|
||||
const displayNameByChannel: Record<string, string> = {};
|
||||
const users: UserProfile[] = [];
|
||||
const updatedChannels = new Set<Channel>();
|
||||
|
||||
if (result.data) {
|
||||
result.data.forEach((data) => {
|
||||
if (data.users?.length) {
|
||||
users.push(...data.users);
|
||||
if (data.users.length > 1) {
|
||||
displayNameByChannel[data.channelId] = displayGroupMessageName(data.users, locale, teammateDisplayNameSetting, currentUserId);
|
||||
} else {
|
||||
displayNameByChannel[data.channelId] = displayUsername(data.users[0], locale, teammateDisplayNameSetting, false);
|
||||
const dms: Channel[] = [];
|
||||
const gms: Channel[] = [];
|
||||
for (const c of directChannels) {
|
||||
if (c.type === General.DM_CHANNEL) {
|
||||
dms.push(c);
|
||||
continue;
|
||||
}
|
||||
gms.push(c);
|
||||
}
|
||||
|
||||
try {
|
||||
const currentUser = await getCurrentUser(database);
|
||||
const results = await Promise.all([
|
||||
fetchProfilesPerChannels(serverUrl, dms.map((c) => c.id), currentUserId, false),
|
||||
fetchProfilesInGroupChannels(serverUrl, gms.map((c) => c.id), false),
|
||||
]);
|
||||
|
||||
const profileRequests = results.flat();
|
||||
for (const result of profileRequests) {
|
||||
result.data?.forEach((data) => {
|
||||
if (data.users?.length) {
|
||||
users.push(...data.users);
|
||||
if (data.users.length > 1) {
|
||||
displayNameByChannel[data.channelId] = displayGroupMessageName(data.users, locale, teammateDisplayNameSetting, currentUserId);
|
||||
} else {
|
||||
displayNameByChannel[data.channelId] = displayUsername(data.users[0], locale, teammateDisplayNameSetting, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
directChannels.forEach((c) => {
|
||||
const displayName = displayNameByChannel[c.id];
|
||||
if (displayName) {
|
||||
c.display_name = displayName;
|
||||
c.fake = true;
|
||||
updatedChannels.add(c);
|
||||
}
|
||||
});
|
||||
|
||||
if (currentUserId) {
|
||||
const ownDirectChannel = dms.find((dm) => dm.name === getDirectChannelName(currentUserId, currentUserId));
|
||||
if (ownDirectChannel) {
|
||||
ownDirectChannel.display_name = displayUsername(currentUser, locale, teammateDisplayNameSetting, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
directChannels.forEach((c) => {
|
||||
const displayName = displayNameByChannel[c.id];
|
||||
if (displayName) {
|
||||
c.display_name = displayName;
|
||||
c.fake = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (currentUserId) {
|
||||
const ownDirectChannel = directChannels.find((dm) => dm.name === getDirectChannelName(currentUserId, currentUserId));
|
||||
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
|
||||
if (ownDirectChannel && database) {
|
||||
const currentUser = await getCurrentUser(database);
|
||||
ownDirectChannel.display_name = displayUsername(currentUser, locale, teammateDisplayNameSetting, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fetchOnly) {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (operator) {
|
||||
const updatedChannelsArray = Array.from(updatedChannels);
|
||||
if (!fetchOnly) {
|
||||
const modelPromises: Array<Promise<Model[]>> = [];
|
||||
if (users.length) {
|
||||
modelPromises.push(operator.handleUsers({users, prepareRecordsOnly: true}));
|
||||
modelPromises.push(operator.handleChannel({channels: directChannels, prepareRecordsOnly: true}));
|
||||
modelPromises.push(operator.handleChannel({channels: updatedChannelsArray, prepareRecordsOnly: true}));
|
||||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
|
||||
return {directChannels: updatedChannelsArray, users};
|
||||
} catch (error) {
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchDirectChannelsInfo(serverUrl: string, directChannels: ChannelModel[]) {
|
||||
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
|
||||
if (!database) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
|
||||
return {directChannels, users};
|
||||
const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS).fetch();
|
||||
const config = await getConfig(database);
|
||||
const license = await getLicense(database);
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences, config, license);
|
||||
const currentUser = await getCurrentUser(database);
|
||||
const channels = directChannels.map((d) => d.toApi());
|
||||
return fetchMissingDirectChannelsInfo(serverUrl, channels, currentUser?.locale, teammateDisplayNameSetting, currentUser?.id);
|
||||
}
|
||||
|
||||
export async function joinChannel(serverUrl: string, userId: string, teamId: string, channelId?: string, channelName?: string, fetchOnly = false) {
|
||||
@@ -773,7 +806,7 @@ export async function createDirectChannel(serverUrl: string, userId: string, dis
|
||||
const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const system = await getCommonSystemValues(database);
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], system.config, system.license);
|
||||
const {directChannels, users} = await fetchMissingSidebarInfo(serverUrl, [created], currentUser.locale, teammateDisplayNameSetting, currentUser.id, true);
|
||||
const {directChannels, users} = await fetchMissingDirectChannelsInfo(serverUrl, [created], currentUser.locale, teammateDisplayNameSetting, currentUser.id, true);
|
||||
created.display_name = directChannels?.[0].display_name || created.display_name;
|
||||
if (users?.length) {
|
||||
profiles.push(...users);
|
||||
@@ -917,7 +950,7 @@ export async function createGroupChannel(serverUrl: string, userIds: string[]) {
|
||||
const preferences = await queryPreferencesByCategoryAndName(operator.database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const system = await getCommonSystemValues(operator.database);
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], system.config, system.license);
|
||||
const {directChannels, users} = await fetchMissingSidebarInfo(serverUrl, [created], currentUser.locale, teammateDisplayNameSetting, currentUser.id, true);
|
||||
const {directChannels, users} = await fetchMissingDirectChannelsInfo(serverUrl, [created], currentUser.locale, teammateDisplayNameSetting, currentUser.id, true);
|
||||
|
||||
const member = {
|
||||
channel_id: created.id,
|
||||
|
||||
@@ -11,7 +11,7 @@ import {isTablet} from '@utils/helpers';
|
||||
|
||||
import {deferredAppEntryActions, entry, registerDeviceToken, syncOtherServers, verifyPushProxy} from './common';
|
||||
|
||||
export async function appEntry(serverUrl: string, since = 0) {
|
||||
export async function appEntry(serverUrl: string, since = 0, isUpgrade = false) {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
@@ -38,7 +38,13 @@ export async function appEntry(serverUrl: string, since = 0) {
|
||||
if ('error' in entryData) {
|
||||
return {error: entryData.error};
|
||||
}
|
||||
const {models, initialTeamId, initialChannelId, prefData, teamData, chData} = entryData;
|
||||
const {models, initialTeamId, initialChannelId, prefData, teamData, chData, meData} = entryData;
|
||||
if (isUpgrade && meData?.user) {
|
||||
const me = await prepareCommonSystemValues(operator, {currentUserId: meData.user.id});
|
||||
if (me?.length) {
|
||||
await operator.batchRecords(me);
|
||||
}
|
||||
}
|
||||
|
||||
let switchToChannel = false;
|
||||
|
||||
@@ -53,10 +59,9 @@ export async function appEntry(serverUrl: string, since = 0) {
|
||||
|
||||
await operator.batchRecords(models);
|
||||
|
||||
const {id: currentUserId, locale: currentUserLocale} = (await getCurrentUser(database))!;
|
||||
const {id: currentUserId, locale: currentUserLocale} = meData?.user || (await getCurrentUser(database))!;
|
||||
const {config, license} = await getCommonSystemValues(database);
|
||||
await deferredAppEntryActions(serverUrl, lastDisconnectedAt, currentUserId, currentUserLocale, prefData.preferences, config, license, teamData, chData, initialTeamId, switchToChannel ? initialChannelId : undefined);
|
||||
|
||||
if (!since) {
|
||||
// Load data from other servers
|
||||
syncOtherServers(serverUrl);
|
||||
@@ -69,22 +74,13 @@ export async function appEntry(serverUrl: string, since = 0) {
|
||||
|
||||
export async function upgradeEntry(serverUrl: string) {
|
||||
const dt = Date.now();
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
|
||||
try {
|
||||
const configAndLicense = await fetchConfigAndLicense(serverUrl, false);
|
||||
const entryData = await appEntry(serverUrl);
|
||||
|
||||
const entryData = await appEntry(serverUrl, 0, true);
|
||||
const error = configAndLicense.error || entryData.error;
|
||||
|
||||
if (!error) {
|
||||
const models = await prepareCommonSystemValues(operator, {currentUserId: entryData.userId});
|
||||
if (models?.length) {
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
DatabaseManager.updateServerIdentifier(serverUrl, configAndLicense.config!.DiagnosticId);
|
||||
DatabaseManager.setActiveServerDatabase(serverUrl);
|
||||
deleteV1Data();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import {Model} from '@nozbe/watermelondb';
|
||||
|
||||
import {fetchMissingSidebarInfo, fetchMyChannelsForTeam, MyChannelsRequest} from '@actions/remote/channel';
|
||||
import {fetchMissingDirectChannelsInfo, fetchMyChannelsForTeam, MyChannelsRequest} from '@actions/remote/channel';
|
||||
import {fetchPostsForUnreadChannels} from '@actions/remote/post';
|
||||
import {MyPreferencesRequest, fetchMyPreferences} from '@actions/remote/preference';
|
||||
import {fetchRoles} from '@actions/remote/role';
|
||||
@@ -45,6 +45,20 @@ export type AppEntryError = {
|
||||
error: Error | ClientError | string;
|
||||
}
|
||||
|
||||
export type EntryResponse = {
|
||||
models: Model[];
|
||||
initialTeamId: string;
|
||||
initialChannelId: string;
|
||||
prefData: MyPreferencesRequest;
|
||||
teamData: MyTeamsRequest;
|
||||
chData?: MyChannelsRequest;
|
||||
meData?: MyUserRequest;
|
||||
} | {
|
||||
error: unknown;
|
||||
}
|
||||
|
||||
const FETCH_MISSING_DM_TIMEOUT = 1000;
|
||||
|
||||
export const teamsToRemove = async (serverUrl: string, removeTeamIds?: string[]) => {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
@@ -66,16 +80,6 @@ export const teamsToRemove = async (serverUrl: string, removeTeamIds?: string[])
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export type EntryResponse = {
|
||||
models: Model[];
|
||||
initialTeamId: string;
|
||||
initialChannelId: string;
|
||||
prefData: MyPreferencesRequest;
|
||||
teamData: MyTeamsRequest;
|
||||
chData?: MyChannelsRequest;
|
||||
} | {
|
||||
error: unknown;
|
||||
}
|
||||
export const entry = async (serverUrl: string, teamId?: string, channelId?: string, since = 0): Promise<EntryResponse> => {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
@@ -121,7 +125,7 @@ export const entry = async (serverUrl: string, teamId?: string, channelId?: stri
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
|
||||
return {models: models.flat(), initialChannelId, initialTeamId, prefData, teamData, chData};
|
||||
return {models: models.flat(), initialChannelId, initialTeamId, prefData, teamData, chData, meData};
|
||||
};
|
||||
|
||||
export const fetchAppEntryData = async (serverUrl: string, since: number, initialTeamId = ''): Promise<AppEntryData | AppEntryError> => {
|
||||
@@ -255,13 +259,10 @@ export async function deferredAppEntryActions(
|
||||
config: ClientConfig, license: ClientLicense, teamData: MyTeamsRequest, chData: MyChannelsRequest | undefined,
|
||||
initialTeamId?: string, initialChannelId?: string) {
|
||||
// defer sidebar DM & GM profiles
|
||||
let channelsToFetchProfiles: Set<Channel>|undefined;
|
||||
if (chData?.channels?.length && chData.memberships?.length) {
|
||||
const directChannels = chData.channels.filter(isDMorGM);
|
||||
const channelsToFetchProfiles = new Set<Channel>(directChannels);
|
||||
if (channelsToFetchProfiles.size) {
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], config, license);
|
||||
await fetchMissingSidebarInfo(serverUrl, Array.from(channelsToFetchProfiles), currentUserLocale, teammateDisplayNameSetting, currentUserId);
|
||||
}
|
||||
channelsToFetchProfiles = new Set<Channel>(directChannels);
|
||||
|
||||
// defer fetching posts for unread channels on initial team
|
||||
fetchPostsForUnreadChannels(serverUrl, chData.channels, chData.memberships, initialChannelId);
|
||||
@@ -269,7 +270,7 @@ export async function deferredAppEntryActions(
|
||||
|
||||
// defer fetch channels and unread posts for other teams
|
||||
if (teamData.teams?.length && teamData.memberships?.length) {
|
||||
await fetchTeamsChannelsAndUnreadPosts(serverUrl, since, teamData.teams, teamData.memberships, initialTeamId);
|
||||
fetchTeamsChannelsAndUnreadPosts(serverUrl, since, teamData.teams, teamData.memberships, initialTeamId);
|
||||
}
|
||||
|
||||
if (preferences && processIsCRTEnabled(preferences, config)) {
|
||||
@@ -289,6 +290,12 @@ export async function deferredAppEntryActions(
|
||||
|
||||
fetchAllTeams(serverUrl);
|
||||
updateAllUsersSince(serverUrl, since);
|
||||
setTimeout(async () => {
|
||||
if (channelsToFetchProfiles?.size) {
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], config, license);
|
||||
fetchMissingDirectChannelsInfo(serverUrl, Array.from(channelsToFetchProfiles), currentUserLocale, teammateDisplayNameSetting, currentUserId);
|
||||
}
|
||||
}, FETCH_MISSING_DM_TIMEOUT);
|
||||
}
|
||||
|
||||
export const registerDeviceToken = async (serverUrl: string) => {
|
||||
|
||||
@@ -4,18 +4,14 @@
|
||||
import {Platform} from 'react-native';
|
||||
|
||||
import {updatePostSinceCache} from '@actions/local/notification';
|
||||
import {fetchMissingSidebarInfo, fetchMyChannel, switchToChannelById} from '@actions/remote/channel';
|
||||
import {fetchDirectChannelsInfo, fetchMyChannel, switchToChannelById} from '@actions/remote/channel';
|
||||
import {forceLogoutIfNecessary} from '@actions/remote/session';
|
||||
import {fetchMyTeam} from '@actions/remote/team';
|
||||
import {Preferences} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {getTeammateNameDisplaySetting} from '@helpers/api/preference';
|
||||
import {getMyChannel, getChannelById} from '@queries/servers/channel';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {getCommonSystemValues, getWebSocketLastDisconnected} from '@queries/servers/system';
|
||||
import {getMyTeamById} from '@queries/servers/team';
|
||||
import {getIsCRTEnabled} from '@queries/servers/thread';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
import {emitNotificationError} from '@utils/notification';
|
||||
|
||||
const fetchNotificationData = async (serverUrl: string, notification: NotificationWithData, skipEvents = false) => {
|
||||
@@ -69,12 +65,9 @@ const fetchNotificationData = async (serverUrl: string, notification: Notificati
|
||||
}
|
||||
|
||||
if (isDirectChannel) {
|
||||
const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const currentUser = await getCurrentUser(database);
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], system.config, system.license);
|
||||
const channel = await getChannelById(database, channelId);
|
||||
if (channel) {
|
||||
fetchMissingSidebarInfo(serverUrl, [channel.toApi()], currentUser?.locale, teammateDisplayNameSetting, currentUser?.id);
|
||||
fetchDirectChannelsInfo(serverUrl, [channel]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import {getCurrentUser} from '@queries/servers/user';
|
||||
import TeamModel from '@typings/database/models/servers/team';
|
||||
import {isDMorGM, selectDefaultChannelForTeam} from '@utils/channel';
|
||||
|
||||
import {fetchMissingSidebarInfo, fetchMyChannelsForTeam, MyChannelsRequest} from './channel';
|
||||
import {fetchMissingDirectChannelsInfo, fetchMyChannelsForTeam, MyChannelsRequest} from './channel';
|
||||
import {fetchPostsForChannel} from './post';
|
||||
import {fetchMyPreferences, MyPreferencesRequest} from './preference';
|
||||
import {fetchRolesIfNeeded} from './role';
|
||||
@@ -122,7 +122,7 @@ export async function retryInitialTeamAndChannel(serverUrl: string) {
|
||||
const channelsToFetchProfiles = new Set<Channel>(directChannels);
|
||||
if (channelsToFetchProfiles.size) {
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(prefData.preferences || [], clData.config, clData.license);
|
||||
fetchMissingSidebarInfo(serverUrl, Array.from(channelsToFetchProfiles), user.locale, teammateDisplayNameSetting, user.id);
|
||||
fetchMissingDirectChannelsInfo(serverUrl, Array.from(channelsToFetchProfiles), user.locale, teammateDisplayNameSetting, user.id);
|
||||
}
|
||||
|
||||
fetchPostsForChannel(serverUrl, initialChannel.id);
|
||||
@@ -201,7 +201,7 @@ export async function retryInitialChannel(serverUrl: string, teamId: string) {
|
||||
const channelsToFetchProfiles = new Set<Channel>(directChannels);
|
||||
if (channelsToFetchProfiles.size) {
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], config, license);
|
||||
fetchMissingSidebarInfo(serverUrl, Array.from(channelsToFetchProfiles), user.locale, teammateDisplayNameSetting, user.id);
|
||||
fetchMissingDirectChannelsInfo(serverUrl, Array.from(channelsToFetchProfiles), user.locale, teammateDisplayNameSetting, user.id);
|
||||
}
|
||||
|
||||
fetchPostsForChannel(serverUrl, initialChannel.id);
|
||||
|
||||
@@ -110,6 +110,79 @@ export async function fetchProfilesInChannel(serverUrl: string, channelId: strin
|
||||
}
|
||||
}
|
||||
|
||||
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};
|
||||
}
|
||||
|
||||
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);
|
||||
if (!channelsToFetch.length) {
|
||||
return {data: []};
|
||||
}
|
||||
|
||||
// Batch fetching profiles per channel by chunks of 50
|
||||
const gms = chunk(channelsToFetch, 50);
|
||||
const data: ProfilesInChannelRequest[] = [];
|
||||
|
||||
const requests = gms.map((cIds) => client.getProfilesInGroupChannels(cIds));
|
||||
const response = await Promise.all(requests);
|
||||
for (const r of response) {
|
||||
for (const id in r) {
|
||||
if (r[id]) {
|
||||
data.push({
|
||||
channelId: id,
|
||||
users: r[id],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fetchOnly) {
|
||||
const modelPromises: Array<Promise<Model[]>> = [];
|
||||
const users = new Set<UserProfile>();
|
||||
const memberships: Array<{channel_id: string; user_id: string}> = [];
|
||||
for (const item of data) {
|
||||
if (item.users?.length) {
|
||||
for (const u of item.users) {
|
||||
users.add(u);
|
||||
memberships.push({channel_id: item.channelId, user_id: u.id});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (memberships.length) {
|
||||
modelPromises.push(operator.handleChannelMembership({
|
||||
channelMemberships: memberships,
|
||||
prepareRecordsOnly: true,
|
||||
}));
|
||||
}
|
||||
if (users.size) {
|
||||
const prepare = prepareUsers(operator, Array.from(users));
|
||||
modelPromises.push(prepare);
|
||||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
|
||||
return {data};
|
||||
} catch (error) {
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchProfilesPerChannels(serverUrl: string, channelIds: string[], excludeUserId?: string, fetchOnly = false): Promise<ProfilesPerChannelRequest> {
|
||||
try {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
@@ -121,10 +194,13 @@ export async function fetchProfilesPerChannels(serverUrl: string, channelIds: st
|
||||
|
||||
// let's filter those channels that we already have the users
|
||||
const membersCount = await getMembersCountByChannelsId(database, channelIds);
|
||||
const channelsToFetch = channelIds.filter((c) => membersCount[c] <= 1 || membersCount[c] > 2);
|
||||
const channelsToFetch = channelIds.filter((c) => membersCount[c] <= 1);
|
||||
if (!channelsToFetch.length) {
|
||||
return {data: []};
|
||||
}
|
||||
|
||||
// Batch fetching profiles per channel by chunks of 50
|
||||
const channels = chunk(channelsToFetch, 50);
|
||||
// Batch fetching profiles per channel by chunks of 300
|
||||
const channels = chunk(channelsToFetch, 300);
|
||||
const data: ProfilesInChannelRequest[] = [];
|
||||
|
||||
for await (const cIds of channels) {
|
||||
@@ -139,10 +215,12 @@ export async function fetchProfilesPerChannels(serverUrl: string, channelIds: st
|
||||
const memberships: Array<{channel_id: string; user_id: string}> = [];
|
||||
for (const item of data) {
|
||||
if (item.users?.length) {
|
||||
item.users.forEach((u) => {
|
||||
users.add(u);
|
||||
memberships.push({channel_id: item.channelId, user_id: u.id});
|
||||
});
|
||||
for (const u of item.users) {
|
||||
if (u.id !== excludeUserId) {
|
||||
users.add(u);
|
||||
memberships.push({channel_id: item.channelId, user_id: u.id});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (memberships.length) {
|
||||
@@ -152,7 +230,7 @@ export async function fetchProfilesPerChannels(serverUrl: string, channelIds: st
|
||||
}));
|
||||
}
|
||||
if (users.size) {
|
||||
const prepare = prepareUsers(operator, Array.from(users).filter((u) => u.id !== excludeUserId));
|
||||
const prepare = prepareUsers(operator, Array.from(users));
|
||||
modelPromises.push(prepare);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
storeMyChannelsForTeam, updateChannelInfoFromChannel, updateMyChannelFromWebsocket,
|
||||
} from '@actions/local/channel';
|
||||
import {switchToGlobalThreads} from '@actions/local/thread';
|
||||
import {fetchMissingSidebarInfo, fetchMyChannel, fetchChannelStats, fetchChannelById, switchToChannelById} from '@actions/remote/channel';
|
||||
import {fetchMissingDirectChannelsInfo, fetchMyChannel, fetchChannelStats, fetchChannelById, switchToChannelById} from '@actions/remote/channel';
|
||||
import {fetchPostsForChannel} from '@actions/remote/post';
|
||||
import {fetchRolesIfNeeded} from '@actions/remote/role';
|
||||
import {fetchUsersByIds, updateUsersNoLongerVisible} from '@actions/remote/user';
|
||||
@@ -205,7 +205,7 @@ export async function handleDirectAddedEvent(serverUrl: string, msg: WebSocketMe
|
||||
|
||||
const teammateDisplayNameSetting = await getTeammateNameDisplay(database);
|
||||
|
||||
const {directChannels, users} = await fetchMissingSidebarInfo(serverUrl, channels, user.locale, teammateDisplayNameSetting, user.id, true);
|
||||
const {directChannels, users} = await fetchMissingDirectChannelsInfo(serverUrl, channels, user.locale, teammateDisplayNameSetting, user.id, true);
|
||||
if (!directChannels?.[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
import React from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Text, TextProps} from 'react-native';
|
||||
|
||||
type FormattedDateProps = TextProps & {
|
||||
@@ -12,6 +13,8 @@ type FormattedDateProps = TextProps & {
|
||||
}
|
||||
|
||||
const FormattedDate = ({format = 'MMM DD, YYYY', timezone, value, ...props}: FormattedDateProps) => {
|
||||
const {locale} = useIntl();
|
||||
moment.locale(locale);
|
||||
let formattedDate = moment(value).format(format);
|
||||
if (timezone) {
|
||||
let zone: string;
|
||||
|
||||
@@ -18,7 +18,7 @@ export const useTeamSwitch = () => {
|
||||
setLoading(true);
|
||||
} else {
|
||||
// eslint-disable-next-line max-nested-callbacks
|
||||
time = setTimeout(() => setLoading(false), 200);
|
||||
time = setTimeout(() => setLoading(false), 0);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
|
||||
@@ -29,6 +29,7 @@ const globalScreen: SelectedView = {id: Screens.GLOBAL_THREADS, Component: Globa
|
||||
|
||||
const AdditionalTabletView = ({onTeam, currentChannelId, isCRTEnabled}: Props) => {
|
||||
const [selected, setSelected] = useState<SelectedView>(isCRTEnabled && !currentChannelId ? globalScreen : channelScreen);
|
||||
const [initiaLoad, setInitialLoad] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = DeviceEventEmitter.addListener(Navigation.NAVIGATION_HOME, (id: string) => {
|
||||
@@ -44,7 +45,15 @@ const AdditionalTabletView = ({onTeam, currentChannelId, isCRTEnabled}: Props) =
|
||||
return () => listener.remove();
|
||||
}, []);
|
||||
|
||||
if (!selected || !onTeam) {
|
||||
useEffect(() => {
|
||||
const t = setTimeout(() => {
|
||||
setInitialLoad(false);
|
||||
}, 0);
|
||||
|
||||
return () => clearTimeout(t);
|
||||
}, []);
|
||||
|
||||
if (!selected || !onTeam || initiaLoad) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,14 @@ import React, {useCallback, useEffect, useMemo} from 'react';
|
||||
import {FlatList} from 'react-native';
|
||||
import Animated, {Easing, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||
|
||||
import {fetchDirectChannelsInfo} from '@actions/remote/channel';
|
||||
import {useServerUrl} from '@app/context/server';
|
||||
import ChannelItem from '@components/channel_item';
|
||||
import {DMS_CATEGORY} from '@constants/categories';
|
||||
import ChannelModel from '@typings/database/models/servers/channel';
|
||||
import {isDMorGM} from '@utils/channel';
|
||||
|
||||
import type CategoryModel from '@typings/database/models/servers/category';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
|
||||
type Props = {
|
||||
sortedChannels: ChannelModel[];
|
||||
@@ -23,6 +26,7 @@ type Props = {
|
||||
const extractKey = (item: ChannelModel) => item.id;
|
||||
|
||||
const CategoryBody = ({sortedChannels, category, hiddenChannelIds, limit, onChannelSwitch, unreadChannels}: Props) => {
|
||||
const serverUrl = useServerUrl();
|
||||
const ids = useMemo(() => {
|
||||
let filteredChannels = sortedChannels;
|
||||
|
||||
@@ -35,7 +39,11 @@ const CategoryBody = ({sortedChannels, category, hiddenChannelIds, limit, onChan
|
||||
return filteredChannels.slice(0, limit);
|
||||
}
|
||||
return filteredChannels;
|
||||
}, [category.type, limit, hiddenChannelIds, sortedChannels]);
|
||||
}, [category.type, limit, hiddenChannelIds, sortedChannels.length]);
|
||||
|
||||
const directChannels = useMemo(() => {
|
||||
return ids.concat(unreadChannels).filter(isDMorGM);
|
||||
}, [ids.length, unreadChannels.length]);
|
||||
|
||||
const renderItem = useCallback(({item}: {item: ChannelModel}) => {
|
||||
return (
|
||||
@@ -54,6 +62,11 @@ const CategoryBody = ({sortedChannels, category, hiddenChannelIds, limit, onChan
|
||||
sharedValue.value = category.collapsed;
|
||||
}, [category.collapsed]);
|
||||
|
||||
useEffect(() => {
|
||||
const direct = directChannels.filter(isDMorGM);
|
||||
fetchDirectChannelsInfo(serverUrl, direct);
|
||||
}, [directChannels.length]);
|
||||
|
||||
const height = ids.length ? ids.length * 40 : 0;
|
||||
const unreadHeight = unreadChannels.length ? unreadChannels.length * 40 : 0;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback, useMemo, useRef} from 'react';
|
||||
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {FlatList, StyleSheet, View} from 'react-native';
|
||||
|
||||
@@ -46,6 +46,7 @@ const Categories = ({categories, onlyUnreads, unreadsOnTop}: Props) => {
|
||||
const isTablet = useIsTablet();
|
||||
const switchingTeam = useTeamSwitch();
|
||||
const teamId = categories[0]?.teamId;
|
||||
const [initiaLoad, setInitialLoad] = useState(true);
|
||||
|
||||
const onChannelSwitch = useCallback(async (channelId: string) => {
|
||||
switchToChannelById(serverUrl, channelId);
|
||||
@@ -87,13 +88,21 @@ const Categories = ({categories, onlyUnreads, unreadsOnTop}: Props) => {
|
||||
return orderedCategories;
|
||||
}, [categories, onlyUnreads, unreadsOnTop]);
|
||||
|
||||
useEffect(() => {
|
||||
const t = setTimeout(() => {
|
||||
setInitialLoad(false);
|
||||
}, 0);
|
||||
|
||||
return () => clearTimeout(t);
|
||||
}, []);
|
||||
|
||||
if (!categories.length) {
|
||||
return <LoadCategoriesError/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{!switchingTeam && (
|
||||
{!switchingTeam && !initiaLoad && (
|
||||
<FlatList
|
||||
data={categoriesToShow}
|
||||
ref={listRef}
|
||||
@@ -108,7 +117,7 @@ const Categories = ({categories, onlyUnreads, unreadsOnTop}: Props) => {
|
||||
strictMode={true}
|
||||
/>
|
||||
)}
|
||||
{switchingTeam && (
|
||||
{(switchingTeam || initiaLoad) && (
|
||||
<View style={styles.loadingView}>
|
||||
<Loading
|
||||
size='large'
|
||||
|
||||
1
package-lock.json
generated
1
package-lock.json
generated
@@ -5,6 +5,7 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mattermost-mobile",
|
||||
"version": "2.0.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache 2.0",
|
||||
|
||||
2
types/database/models/servers/channel.d.ts
vendored
2
types/database/models/servers/channel.d.ts
vendored
@@ -81,5 +81,5 @@ export default class ChannelModel extends Model {
|
||||
/** categoryChannel: category of this channel */
|
||||
categoryChannel: Relation<CategoryChannelModel>;
|
||||
|
||||
toApi = () => Channel;
|
||||
toApi(): Channel;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user