diff --git a/app/actions/remote/entry.ts b/app/actions/remote/entry.ts index 88569cc847..a6cdf190ca 100644 --- a/app/actions/remote/entry.ts +++ b/app/actions/remote/entry.ts @@ -279,6 +279,7 @@ const fetchAppEntryData = async (serverUrl: string, initialTeamId: string): Prom fetchMe(serverUrl, fetchOnly), ]; + const removeTeamIds: string[] = []; const resolution = await Promise.all(promises); const [teamData, , prefData, meData] = resolution; let [, chData] = resolution; @@ -287,7 +288,7 @@ const fetchAppEntryData = async (serverUrl: string, initialTeamId: string): Prom // If no initial team was set in the database but got teams in the response const config = await queryConfig(database); const teamOrderPreference = getPreferenceValue(prefData.preferences || [], Preferences.TEAMS_ORDER, '', '') as string; - const teamMembers = teamData.memberships.map((m) => m.team_id); + const teamMembers = teamData.memberships.filter((m) => m.delete_at === 0).map((m) => m.team_id); const myTeams = teamData.teams!.filter((t) => teamMembers?.includes(t.id)); const defaultTeam = selectDefaultTeam(myTeams, meData.user?.locale || DEFAULT_LOCALE, teamOrderPreference, config.ExperimentalPrimaryTeam); if (defaultTeam?.id) { @@ -295,18 +296,24 @@ const fetchAppEntryData = async (serverUrl: string, initialTeamId: string): Prom } } + const removedFromTeam = teamData.memberships?.filter((m) => m.delete_at > 0); + if (removedFromTeam?.length) { + removeTeamIds.push(...removedFromTeam.map((m) => m.team_id)); + } + let data: AppEntryData = { initialTeamId, teamData, chData, prefData, meData, + removeTeamIds, }; if (teamData.teams?.length === 0) { // User is no longer a member of any team const myTeams = await queryMyTeams(database); - const removeTeamIds: string[] = myTeams?.map((myTeam) => myTeam.id) || []; + removeTeamIds.push(...(myTeams?.map((myTeam) => myTeam.id) || [])); return { ...data, @@ -319,7 +326,9 @@ const fetchAppEntryData = async (serverUrl: string, initialTeamId: string): Prom const chError = chData?.error as ClientError | undefined; if (!inTeam || chError?.status_code === 403) { // User is no longer a member of the current team - const removeTeamIds = [initialTeamId]; + if (!removeTeamIds.includes(initialTeamId)) { + removeTeamIds.push(initialTeamId); + } const availableTeamIds = await queryAvailableTeamIds(database, initialTeamId, teamData.teams, prefData.preferences, meData.user?.locale); const alternateTeamData = await fetchAlternateTeamData(serverUrl, availableTeamIds, removeTeamIds, includeDeletedChannels, lastDisconnected, fetchOnly); diff --git a/app/actions/remote/team.ts b/app/actions/remote/team.ts index dcdfa3e5e3..07bfbc6c92 100644 --- a/app/actions/remote/team.ts +++ b/app/actions/remote/team.ts @@ -8,7 +8,7 @@ import DatabaseManager from '@database/manager'; import NetworkManager from '@init/network_manager'; import {prepareMyChannelsForTeam, queryDefaultChannelForTeam} from '@queries/servers/channel'; import {queryWebSocketLastDisconnected} from '@queries/servers/system'; -import {prepareMyTeams, syncTeamTable} from '@queries/servers/team'; +import {prepareDeleteTeam, prepareMyTeams, queryTeamsById, syncTeamTable} from '@queries/servers/team'; import {isTablet} from '@utils/helpers'; import {fetchMyChannelsForTeam} from './channel'; @@ -92,10 +92,23 @@ export const fetchMyTeams = async (serverUrl: string, fetchOnly = false): Promis const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; const modelPromises: Array> = []; if (operator) { - const prepare = prepareMyTeams(operator, teams, memberships); + const removeTeamIds = memberships.filter((m) => m.delete_at > 0).map((m) => m.team_id); + const remainingTeams = teams.filter((t) => !removeTeamIds.includes(t.id)); + const prepare = prepareMyTeams(operator, remainingTeams, memberships); if (prepare) { modelPromises.push(...prepare); } + + if (removeTeamIds.length) { + if (removeTeamIds?.length) { + // Immediately delete myTeams so that the UI renders only teams the user is a member of. + const removeTeams = await queryTeamsById(operator.database, removeTeamIds); + removeTeams?.forEach((team) => { + modelPromises.push(prepareDeleteTeam(team)); + }); + } + } + if (modelPromises.length) { const models = await Promise.all(modelPromises); const flattenedModels = models.flat() as Model[]; diff --git a/app/components/team_sidebar/index.ts b/app/components/team_sidebar/index.ts index da295fa971..d29c2ec796 100644 --- a/app/components/team_sidebar/index.ts +++ b/app/components/team_sidebar/index.ts @@ -22,18 +22,17 @@ import type UserModel from '@typings/database/models/servers/user'; const {SERVER: {SYSTEM, MY_TEAM, TEAM, USER, ROLE}} = MM_TABLES; -type PropsInput = WithDatabaseArgs & { - currentUser: UserModel; -} +const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { + const currentUser = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( + switchMap(({value}) => database.get(USER).findAndObserve(value)), + ); + const rolesArray = currentUser.pipe( + switchMap((u) => of$(u.roles.split(' '))), + ); + const roles = rolesArray.pipe( + switchMap((values) => database.get(ROLE).query(Q.where('name', Q.oneOf(values))).observe()), + ); -const withSystem = withObservables([], ({database}: WithDatabaseArgs) => ({ - currentUser: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap((currentUserId: SystemModel) => database.get(USER).findAndObserve(currentUserId.value))), -})); - -const withTeams = withObservables([], ({currentUser, database}: PropsInput) => { - const rolesArray = [...currentUser.roles.split(' ')]; - const roles = database.get(ROLE).query(Q.where('name', Q.oneOf(rolesArray))).observe(); const canCreateTeams = roles.pipe(switchMap((r) => of$(hasPermission(r, Permissions.CREATE_TEAM, false)))); const otherTeams = database.get(MY_TEAM).query().observe().pipe( @@ -50,4 +49,4 @@ const withTeams = withObservables([], ({currentUser, database}: PropsInput) => { }; }); -export default withDatabase(withSystem(withTeams(TeamSidebar))); +export default withDatabase(enhanced(TeamSidebar)); diff --git a/app/queries/servers/team.ts b/app/queries/servers/team.ts index e514183644..0f2e24baba 100644 --- a/app/queries/servers/team.ts +++ b/app/queries/servers/team.ts @@ -130,13 +130,19 @@ export const queryLastTeam = async (database: Database) => { export const syncTeamTable = async (operator: ServerDataOperator, teams: Team[]) => { try { - const notAvailable = await operator.database.get(TEAM).query(Q.where('id', Q.notIn(teams.map((t) => t.id)))).fetch(); + const deletedTeams = teams.filter((t) => t.delete_at > 0).map((t) => t.id); + const availableTeams = teams.filter((a) => !deletedTeams.includes(a.id)); const models = []; - const deletions = await Promise.all(notAvailable.map((t) => prepareDeleteTeam(t))); - for (const d of deletions) { - models.push(...d); + + if (deletedTeams.length) { + const notAvailable = await operator.database.get(TEAM).query(Q.where('id', Q.oneOf(deletedTeams))).fetch(); + const deletions = await Promise.all(notAvailable.map((t) => prepareDeleteTeam(t))); + for (const d of deletions) { + models.push(...d); + } } - models.push(...await operator.handleTeam({teams, prepareRecordsOnly: true})); + + models.push(...await operator.handleTeam({teams: availableTeams, prepareRecordsOnly: true})); await operator.batchRecords(models); return {}; } catch (error) { @@ -163,7 +169,7 @@ export const queryDefaultTeam = async (database: Database) => { export const prepareMyTeams = (operator: ServerDataOperator, teams: Team[], memberships: TeamMembership[]) => { try { const teamRecords = operator.handleTeam({prepareRecordsOnly: true, teams}); - const teamMemberships = memberships.filter((m) => teams.find((t) => t.id === m.team_id)); + const teamMemberships = memberships.filter((m) => teams.find((t) => t.id === m.team_id) && m.delete_at === 0); const teamMembershipRecords = operator.handleTeamMemberships({prepareRecordsOnly: true, teamMemberships}); const myTeams: MyTeam[] = teamMemberships.map((tm) => { return {id: tm.team_id, roles: tm.roles ?? ''};