forked from Ivasoft/mattermost-mobile
Fixes a race condition and deleting only teams that you no longer belong to (#6028)
* Fixes a race condition and deleting only teams that you no longer belong to * common action teamsToRemove
This commit is contained in:
@@ -8,12 +8,11 @@ import DatabaseManager from '@database/manager';
|
||||
import {queryChannelsById, queryDefaultChannelForTeam} from '@queries/servers/channel';
|
||||
import {prepareModels} from '@queries/servers/entry';
|
||||
import {prepareCommonSystemValues, queryCommonSystemValues, queryCurrentChannelId, queryCurrentTeamId, queryWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system';
|
||||
import {deleteMyTeams, queryTeamsById} from '@queries/servers/team';
|
||||
import {queryCurrentUser} from '@queries/servers/user';
|
||||
import {deleteV1Data} from '@utils/file';
|
||||
import {isTablet} from '@utils/helpers';
|
||||
|
||||
import {AppEntryData, AppEntryError, deferredAppEntryActions, fetchAppEntryData, syncOtherServers} from './common';
|
||||
import {AppEntryData, AppEntryError, deferredAppEntryActions, fetchAppEntryData, syncOtherServers, teamsToRemove} from './common';
|
||||
|
||||
export const appEntry = async (serverUrl: string, since = 0) => {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
@@ -62,12 +61,7 @@ export const appEntry = async (serverUrl: string, since = 0) => {
|
||||
}
|
||||
}
|
||||
|
||||
let removeTeams;
|
||||
if (removeTeamIds?.length) {
|
||||
// Immediately delete myTeams so that the UI renders only teams the user is a member of.
|
||||
removeTeams = await queryTeamsById(database, removeTeamIds);
|
||||
await deleteMyTeams(operator, removeTeams!);
|
||||
}
|
||||
const removeTeams = await teamsToRemove(serverUrl, removeTeamIds);
|
||||
|
||||
let removeChannels;
|
||||
if (removeChannelIds?.length) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import {fetchChannelStats, fetchMissingSidebarInfo, fetchMyChannelsForTeam, mark
|
||||
import {fetchPostsForChannel, fetchPostsForUnreadChannels} from '@actions/remote/post';
|
||||
import {MyPreferencesRequest, fetchMyPreferences} from '@actions/remote/preference';
|
||||
import {fetchConfigAndLicense} from '@actions/remote/systems';
|
||||
import {fetchMyTeams, fetchTeamsChannelsAndUnreadPosts, MyTeamsRequest} from '@actions/remote/team';
|
||||
import {fetchAllTeams, fetchMyTeams, fetchTeamsChannelsAndUnreadPosts, MyTeamsRequest} from '@actions/remote/team';
|
||||
import {fetchMe, MyUserRequest, updateAllUsersSince} from '@actions/remote/user';
|
||||
import {General, Preferences} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
@@ -16,7 +16,7 @@ import NetworkManager from '@init/network_manager';
|
||||
import {queryAllServers} from '@queries/app/servers';
|
||||
import {queryAllChannelsForTeam} from '@queries/servers/channel';
|
||||
import {queryConfig} from '@queries/servers/system';
|
||||
import {queryAvailableTeamIds, queryMyTeams} from '@queries/servers/team';
|
||||
import {deleteMyTeams, queryAvailableTeamIds, queryMyTeams, queryMyTeamsById, queryTeamsById} from '@queries/servers/team';
|
||||
|
||||
import type ClientError from '@client/rest/error';
|
||||
|
||||
@@ -34,6 +34,27 @@ export type AppEntryError = {
|
||||
error?: Error | ClientError | string;
|
||||
}
|
||||
|
||||
export const teamsToRemove = async (serverUrl: string, removeTeamIds?: string[]) => {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
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 queryMyTeamsById(database, removeTeamIds);
|
||||
if (removeMyTeams?.length) {
|
||||
await deleteMyTeams(operator, removeMyTeams);
|
||||
const ids = removeMyTeams.map((m) => m.id);
|
||||
const removeTeams = await queryTeamsById(database, ids);
|
||||
return removeTeams;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const fetchAppEntryData = async (serverUrl: string, since: number, initialTeamId: string): Promise<AppEntryData | AppEntryError> => {
|
||||
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
|
||||
if (!database) {
|
||||
@@ -187,6 +208,7 @@ export const deferredAppEntryActions = async (
|
||||
await fetchTeamsChannelsAndUnreadPosts(serverUrl, since, teamData.teams, teamData.memberships, initialTeamId);
|
||||
}
|
||||
|
||||
fetchAllTeams(serverUrl);
|
||||
updateAllUsersSince(serverUrl, since);
|
||||
};
|
||||
|
||||
|
||||
@@ -11,13 +11,13 @@ import DatabaseManager from '@database/manager';
|
||||
import {queryChannelsById, queryDefaultChannelForTeam, queryMyChannel} from '@queries/servers/channel';
|
||||
import {prepareModels} from '@queries/servers/entry';
|
||||
import {queryCommonSystemValues, queryCurrentTeamId, queryWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system';
|
||||
import {deleteMyTeams, queryMyTeamById, queryTeamsById} from '@queries/servers/team';
|
||||
import {queryMyTeamById} from '@queries/servers/team';
|
||||
import {queryCurrentUser} from '@queries/servers/user';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import {isTablet} from '@utils/helpers';
|
||||
import {emitNotificationError} from '@utils/notification';
|
||||
|
||||
import {AppEntryData, AppEntryError, deferredAppEntryActions, fetchAppEntryData, syncOtherServers} from './common';
|
||||
import {AppEntryData, AppEntryError, deferredAppEntryActions, fetchAppEntryData, syncOtherServers, teamsToRemove} from './common';
|
||||
|
||||
export const pushNotificationEntry = async (serverUrl: string, notification: NotificationWithData) => {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
@@ -117,12 +117,7 @@ export const pushNotificationEntry = async (serverUrl: string, notification: Not
|
||||
emitNotificationError('Channel');
|
||||
}
|
||||
|
||||
let removeTeams;
|
||||
if (removeTeamIds?.length) {
|
||||
// Immediately delete myTeams so that the UI renders only teams the user is a member of.
|
||||
removeTeams = await queryTeamsById(operator.database, removeTeamIds);
|
||||
await deleteMyTeams(operator, removeTeams!);
|
||||
}
|
||||
const removeTeams = await teamsToRemove(serverUrl, removeTeamIds);
|
||||
|
||||
let removeChannels;
|
||||
if (removeChannelIds?.length) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {fetchMissingSidebarInfo, switchToChannelById} from '@actions/remote/channel';
|
||||
import {AppEntryData, AppEntryError, fetchAppEntryData} from '@actions/remote/entry/common';
|
||||
import {AppEntryData, AppEntryError, fetchAppEntryData, teamsToRemove} from '@actions/remote/entry/common';
|
||||
import {fetchPostsForUnreadChannels, fetchPostsSince} from '@actions/remote/post';
|
||||
import {fetchRoles} from '@actions/remote/role';
|
||||
import {fetchConfigAndLicense} from '@actions/remote/systems';
|
||||
@@ -15,7 +15,6 @@ import {getTeammateNameDisplaySetting} from '@helpers/api/preference';
|
||||
import {queryChannelsById, queryDefaultChannelForTeam} from '@queries/servers/channel';
|
||||
import {prepareModels} from '@queries/servers/entry';
|
||||
import {queryCommonSystemValues, queryConfig, queryCurrentChannelId, queryWebSocketLastDisconnected, resetWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system';
|
||||
import {deleteMyTeams, queryTeamsById} from '@queries/servers/team';
|
||||
import {isTablet} from '@utils/helpers';
|
||||
|
||||
import {handleCategoryCreatedEvent, handleCategoryDeletedEvent, handleCategoryOrderUpdatedEvent, handleCategoryUpdatedEvent} from './category';
|
||||
@@ -137,12 +136,7 @@ async function doReconnect(serverUrl: string) {
|
||||
}
|
||||
}
|
||||
|
||||
let removeTeams;
|
||||
if (removeTeamIds?.length) {
|
||||
// Immediately delete myTeams so that the UI renders only teams the user is a member of.
|
||||
removeTeams = await queryTeamsById(database, removeTeamIds);
|
||||
await deleteMyTeams(operator, removeTeams!);
|
||||
}
|
||||
const removeTeams = await teamsToRemove(serverUrl, removeTeamIds);
|
||||
|
||||
let removeChannels;
|
||||
if (removeChannelIds?.length) {
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||
|
||||
import {fetchAllTeams} from '@actions/remote/team';
|
||||
import {TEAM_SIDEBAR_WIDTH} from '@constants/view';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
@@ -47,7 +45,6 @@ export default function TeamSidebar({canCreateTeams, iconPad, otherTeams, teamsC
|
||||
const width = useSharedValue(initialWidth);
|
||||
const marginTop = useSharedValue(iconPad ? 44 : 0);
|
||||
const theme = useTheme();
|
||||
const serverUrl = useServerUrl();
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
const transform = useAnimatedStyle(() => {
|
||||
@@ -64,10 +61,6 @@ export default function TeamSidebar({canCreateTeams, iconPad, otherTeams, teamsC
|
||||
marginTop.value = iconPad ? 44 : 0;
|
||||
}, [iconPad]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAllTeams(serverUrl);
|
||||
}, [serverUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
width.value = teamsCount > 1 ? TEAM_SIDEBAR_WIDTH : 0;
|
||||
}, [teamsCount]);
|
||||
|
||||
@@ -188,16 +188,11 @@ export const prepareMyTeams = (operator: ServerDataOperator, teams: Team[], memb
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteMyTeams = async (operator: ServerDataOperator, teams: TeamModel[]) => {
|
||||
export const deleteMyTeams = async (operator: ServerDataOperator, myTeams: MyTeamModel[]) => {
|
||||
try {
|
||||
const preparedModels: Model[] = [];
|
||||
for await (const team of teams) {
|
||||
try {
|
||||
const myTeam = await team.myTeam.fetch() as MyTeamModel;
|
||||
preparedModels.push(myTeam.prepareDestroyPermanently());
|
||||
} catch {
|
||||
// Record not found, do nothing
|
||||
}
|
||||
for (const myTeam of myTeams) {
|
||||
preparedModels.push(myTeam.prepareDestroyPermanently());
|
||||
}
|
||||
|
||||
if (preparedModels.length) {
|
||||
@@ -296,6 +291,15 @@ export const queryTeamsById = async (database: Database, teamIds: string[]): Pro
|
||||
}
|
||||
};
|
||||
|
||||
export const queryMyTeamsById = async (database: Database, teamIds: string[]): Promise<MyTeamModel[]|undefined> => {
|
||||
try {
|
||||
const teams = (await database.get<MyTeamModel>(MY_TEAM).query(Q.where('id', Q.oneOf(teamIds))).fetch());
|
||||
return teams;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const queryTeamByName = async (database: Database, teamName: string): Promise<TeamModel|undefined> => {
|
||||
try {
|
||||
const team = (await database.get(TEAM).query(Q.where('name', teamName)).fetch()) as TeamModel[];
|
||||
|
||||
Reference in New Issue
Block a user