Merge pull request #7682 from mattermost/gm_conversion_on_reconnect_handler

[MM-55463] GM conversion reconnect handler
This commit is contained in:
Harshil Sharma 2023-11-29 09:25:02 +05:30 committed by GitHub
commit 9ff24a8551
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 14 deletions

View file

@ -19,7 +19,7 @@ import {selectDefaultTeam} from '@helpers/api/team';
import {DEFAULT_LOCALE} from '@i18n';
import NetworkManager from '@managers/network_manager';
import {getDeviceToken} from '@queries/app/global';
import {queryAllChannelsForTeam, queryChannelsById} from '@queries/servers/channel';
import {getChannelById, queryAllChannelsForTeam, queryChannelsById} from '@queries/servers/channel';
import {prepareModels, truncateCrtRelatedTables} from '@queries/servers/entry';
import {getHasCRTChanged} from '@queries/servers/preference';
import {getConfig, getCurrentChannelId, getCurrentTeamId, getIsDataRetentionEnabled, getPushVerificationStatus, getWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system';
@ -42,6 +42,8 @@ export type AppEntryData = {
removeTeamIds?: string[];
removeChannelIds?: string[];
isCRTEnabled: boolean;
initialChannelId?: string;
gmConverted: boolean;
}
export type AppEntryError = {
@ -56,6 +58,7 @@ export type EntryResponse = {
teamData: MyTeamsRequest;
chData?: MyChannelsRequest;
meData?: MyUserRequest;
gmConverted: boolean;
} | {
error: unknown;
}
@ -123,12 +126,12 @@ const entryRest = async (serverUrl: string, teamId?: string, channelId?: string,
const lastDisconnectedAt = since || await getWebSocketLastDisconnected(database);
const fetchedData = await fetchAppEntryData(serverUrl, lastDisconnectedAt, teamId);
const fetchedData = await fetchAppEntryData(serverUrl, lastDisconnectedAt, teamId, channelId);
if ('error' in fetchedData) {
return {error: fetchedData.error};
}
const {initialTeamId, teamData, chData, prefData, meData, removeTeamIds, removeChannelIds, isCRTEnabled} = fetchedData;
const {initialTeamId, initialChannelId: fetchedChannelId, teamData, chData, prefData, meData, removeTeamIds, removeChannelIds, isCRTEnabled, gmConverted} = fetchedData;
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,
@ -142,7 +145,7 @@ const entryRest = async (serverUrl: string, teamId?: string, channelId?: string,
const rolesData = await fetchRoles(serverUrl, teamData.memberships, chData?.memberships, meData.user, true);
const initialChannelId = await entryInitialChannelId(database, channelId, teamId, initialTeamId, meData?.user?.locale || '', chData?.channels, chData?.memberships);
const initialChannelId = await entryInitialChannelId(database, fetchedChannelId, teamId, initialTeamId, meData?.user?.locale || '', chData?.channels, chData?.memberships);
const removeTeams = await teamsToRemove(serverUrl, removeTeamIds);
@ -158,10 +161,10 @@ const entryRest = async (serverUrl: string, teamId?: string, channelId?: string,
const models = await Promise.all(modelPromises);
return {models: models.flat(), initialChannelId, initialTeamId, prefData, teamData, chData, meData};
return {models: models.flat(), initialChannelId, initialTeamId, prefData, teamData, chData, meData, gmConverted};
};
const fetchAppEntryData = async (serverUrl: string, sinceArg: number, initialTeamId = ''): Promise<AppEntryData | AppEntryError> => {
const fetchAppEntryData = async (serverUrl: string, sinceArg: number, onLoadTeamId = '', channelId?: string): Promise<AppEntryData | AppEntryError> => {
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
if (!database) {
return {error: `${serverUrl} database not found`};
@ -188,16 +191,45 @@ const fetchAppEntryData = async (serverUrl: string, sinceArg: number, initialTea
}
}
// Fetch in parallel teams / team membership / channels for current team / user preferences / user
const promises: [Promise<MyTeamsRequest>, Promise<MyChannelsRequest | undefined>, Promise<MyUserRequest>] = [
// Fetch in parallel teams / team membership / user preferences / user
const promises: [Promise<MyTeamsRequest>, Promise<MyUserRequest>] = [
fetchMyTeams(serverUrl, fetchOnly),
initialTeamId ? fetchMyChannelsForTeam(serverUrl, initialTeamId, includeDeletedChannels, since, fetchOnly, false, isCRTEnabled) : Promise.resolve(undefined),
fetchMe(serverUrl, fetchOnly),
];
const resolution = await Promise.all(promises);
const [teamData, , meData] = resolution;
let [, chData] = resolution;
const [teamData, meData] = resolution;
let chData;
let initialTeamId = onLoadTeamId;
let initialChannelId = channelId;
let gmConverted = false;
if (channelId) {
const existingChannel = await getChannelById(database, channelId);
if (existingChannel && existingChannel.type === General.GM_CHANNEL) {
// Okay, so now we know the channel existsin in mobile app's database as a GM.
// We now need to also check if channel on server is actually a private channel,
// and if so, which team does it belong to now. That team will become the
// active team on mobile app after this point.
const client = NetworkManager.getClient(serverUrl);
const serverChannel = await client.getChannel(channelId);
// Although yon can convert GM only to a pirvate channel, a private channel can furthur be converted to a public channel.
// So between the mobile app being on the GM and reconnecting,
// it may have become either a public or a private channel. So we need to check for both.
if (serverChannel.type === General.PRIVATE_CHANNEL || serverChannel.type === General.OPEN_CHANNEL) {
initialTeamId = serverChannel.team_id;
initialChannelId = channelId;
gmConverted = true;
}
}
}
if (initialTeamId) {
chData = await fetchMyChannelsForTeam(serverUrl, initialTeamId, includeDeletedChannels, since, fetchOnly, false, isCRTEnabled);
}
if (!initialTeamId && teamData.teams?.length && teamData.memberships?.length) {
// If no initial team was set in the database but got teams in the response
@ -221,6 +253,8 @@ const fetchAppEntryData = async (serverUrl: string, sinceArg: number, initialTea
meData,
removeTeamIds,
isCRTEnabled,
initialChannelId,
gmConverted,
};
if (teamData.teams?.length === 0 && !teamData.error) {
@ -438,6 +472,7 @@ export async function handleEntryAfterLoadNavigation(
currentChannelId: string,
initialTeamId: string,
initialChannelId: string,
gmConverted: boolean,
) {
try {
const {operator, database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
@ -462,7 +497,11 @@ export async function handleEntryAfterLoadNavigation(
await handleKickFromTeam(serverUrl, currentTeamIdAfterLoad);
}
} else if (currentTeamIdAfterLoad !== initialTeamId) {
await handleKickFromTeam(serverUrl, currentTeamIdAfterLoad);
if (gmConverted) {
await setCurrentTeamAndChannelId(operator, initialTeamId, currentChannelId);
} else {
await handleKickFromTeam(serverUrl, currentTeamIdAfterLoad);
}
} else if (currentChannelIdAfterLoad !== currentChannelId) {
// Switched channels while loading
if (!channelMembers.find((m) => m.channel_id === currentChannelIdAfterLoad)) {

View file

@ -159,9 +159,9 @@ async function doReconnect(serverUrl: string) {
setTeamLoading(serverUrl, false);
return entryData.error;
}
const {models, initialTeamId, initialChannelId, prefData, teamData, chData} = entryData;
const {models, initialTeamId, initialChannelId, prefData, teamData, chData, gmConverted} = entryData;
await handleEntryAfterLoadNavigation(serverUrl, teamData.memberships || [], chData?.memberships || [], currentTeamId || '', currentChannelId || '', initialTeamId, initialChannelId);
await handleEntryAfterLoadNavigation(serverUrl, teamData.memberships || [], chData?.memberships || [], currentTeamId || '', currentChannelId || '', initialTeamId, initialChannelId, gmConverted);
const dt = Date.now();
if (models?.length) {