From e6af1e116bc510d4f3a044089782eb6c2e3bac80 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Thu, 1 Dec 2022 16:28:51 +0200 Subject: [PATCH] Fix categories where a previously deleted category was not removed (#6808) --- app/actions/local/category.ts | 45 +++++-------------------------- app/actions/remote/retry.ts | 8 +++--- app/actions/remote/team.ts | 5 ++-- app/actions/websocket/teams.ts | 5 ++-- app/queries/servers/categories.ts | 41 ++++++++++++++++++++++++++++ app/queries/servers/entry.ts | 5 ++-- 6 files changed, 57 insertions(+), 52 deletions(-) diff --git a/app/actions/local/category.ts b/app/actions/local/category.ts index a587b027a..26e8abb05 100644 --- a/app/actions/local/category.ts +++ b/app/actions/local/category.ts @@ -5,11 +5,10 @@ import {Model} from '@nozbe/watermelondb'; import {CHANNELS_CATEGORY, DMS_CATEGORY} from '@constants/categories'; import DatabaseManager from '@database/manager'; -import {prepareCategories, prepareCategoryChannels, queryCategoriesByTeamIds, getCategoryById} from '@queries/servers/categories'; +import {prepareCategoryChannels, queryCategoriesByTeamIds, getCategoryById, prepareCategoriesAndCategoriesChannels} from '@queries/servers/categories'; import {getCurrentUserId} from '@queries/servers/system'; import {queryMyTeams} from '@queries/servers/team'; import {isDMorGM} from '@utils/channel'; -import {pluckUnique} from '@utils/helpers'; import {logError} from '@utils/log'; import type ChannelModel from '@typings/database/models/servers/channel'; @@ -34,48 +33,18 @@ export const deleteCategory = async (serverUrl: string, categoryId: string) => { export async function storeCategories(serverUrl: string, categories: CategoryWithChannels[], prune = false, prepareRecordsOnly = false) { try { - const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - - const modelPromises: Array> = []; - const preparedCategories = prepareCategories(operator, categories); - if (preparedCategories) { - modelPromises.push(preparedCategories); - } - - const preparedCategoryChannels = prepareCategoryChannels(operator, categories); - if (preparedCategoryChannels) { - modelPromises.push(preparedCategoryChannels); - } - - const models = await Promise.all(modelPromises); - const flattenedModels = models.flat(); - - if (prune && categories.length) { - const remoteCategoryIds = new Set(categories.map((cat) => cat.id)); - - // 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(database, teamIds).fetch(); - - localCategories. - filter((category) => category.type === 'custom'). - forEach((localCategory) => { - if (!remoteCategoryIds.has(localCategory.id)) { - localCategory.prepareDestroyPermanently(); - flattenedModels.push(localCategory); - } - }); - } + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const models = await prepareCategoriesAndCategoriesChannels(operator, categories, prune); if (prepareRecordsOnly) { - return {models: flattenedModels}; + return {models}; } - if (flattenedModels?.length > 0) { - await operator.batchRecords(flattenedModels); + if (models.length > 0) { + await operator.batchRecords(models); } - return {models: flattenedModels}; + return {models}; } catch (error) { logError('FAILED TO STORE CATEGORIES', error); return {error}; diff --git a/app/actions/remote/retry.ts b/app/actions/remote/retry.ts index 497f691f0..0c078c2a7 100644 --- a/app/actions/remote/retry.ts +++ b/app/actions/remote/retry.ts @@ -7,7 +7,7 @@ import DatabaseManager from '@database/manager'; import {getPreferenceValue, getTeammateNameDisplaySetting} from '@helpers/api/preference'; import {selectDefaultTeam} from '@helpers/api/team'; import NetworkManager from '@managers/network_manager'; -import {prepareCategories, prepareCategoryChannels} from '@queries/servers/categories'; +import {prepareCategoriesAndCategoriesChannels} from '@queries/servers/categories'; import {prepareMyChannelsForTeam} from '@queries/servers/channel'; import {prepareMyPreferences, queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import {prepareCommonSystemValues, getConfig, getLicense} from '@queries/servers/system'; @@ -105,8 +105,7 @@ export async function retryInitialTeamAndChannel(serverUrl: string) { storeConfig(serverUrl, clData.config, true), ...prepareMyTeams(operator, teamData.teams!, teamData.memberships!), ...await prepareMyChannelsForTeam(operator, initialTeam.id, chData!.channels!, chData!.memberships!), - prepareCategories(operator, chData!.categories!), - prepareCategoryChannels(operator, chData!.categories!), + prepareCategoriesAndCategoriesChannels(operator, chData!.categories!, true), prepareCommonSystemValues( operator, @@ -193,8 +192,7 @@ export async function retryInitialChannel(serverUrl: string, teamId: string) { const models: Model[] = (await Promise.all([ ...await prepareMyChannelsForTeam(operator, teamId, chData!.channels!, chData!.memberships!), - prepareCategories(operator, chData!.categories!), - prepareCategoryChannels(operator, chData!.categories!), + prepareCategoriesAndCategoriesChannels(operator, chData!.categories!, true), prepareCommonSystemValues(operator, {currentChannelId: initialChannel?.id}), ])).flat(); diff --git a/app/actions/remote/team.ts b/app/actions/remote/team.ts index 83624fb3f..7bd9616e2 100644 --- a/app/actions/remote/team.ts +++ b/app/actions/remote/team.ts @@ -8,7 +8,7 @@ import {removeUserFromTeam as localRemoveUserFromTeam} from '@actions/local/team import {Events} from '@constants'; import DatabaseManager from '@database/manager'; import NetworkManager from '@managers/network_manager'; -import {prepareCategories, prepareCategoryChannels} from '@queries/servers/categories'; +import {prepareCategoriesAndCategoriesChannels} from '@queries/servers/categories'; import {prepareMyChannelsForTeam, getDefaultChannelForTeam} from '@queries/servers/channel'; import {prepareCommonSystemValues, getCurrentTeamId, getCurrentUserId} from '@queries/servers/system'; import {addTeamToTeamHistory, prepareDeleteTeam, prepareMyTeams, getNthLastChannelFromTeam, queryTeamsById, syncTeamTable} from '@queries/servers/team'; @@ -73,8 +73,7 @@ export async function addUserToTeam(serverUrl: string, teamId: string, userId: s operator.handleMyTeam({myTeams, prepareRecordsOnly: true}), operator.handleTeamMemberships({teamMemberships: [member], prepareRecordsOnly: true}), ...await prepareMyChannelsForTeam(operator, teamId, channels || [], channelMembers || []), - prepareCategories(operator, categories || []), - prepareCategoryChannels(operator, categories || []), + prepareCategoriesAndCategoriesChannels(operator, categories || [], true), ])).flat(); await operator.batchRecords(models); diff --git a/app/actions/websocket/teams.ts b/app/actions/websocket/teams.ts index b8cace9ce..c469b8b24 100644 --- a/app/actions/websocket/teams.ts +++ b/app/actions/websocket/teams.ts @@ -12,7 +12,7 @@ import {updateUsersNoLongerVisible} from '@actions/remote/user'; import Events from '@constants/events'; import DatabaseManager from '@database/manager'; import {getActiveServerUrl} from '@queries/app/servers'; -import {prepareCategories, prepareCategoryChannels} from '@queries/servers/categories'; +import {prepareCategoriesAndCategoriesChannels} from '@queries/servers/categories'; import {prepareMyChannelsForTeam} from '@queries/servers/channel'; import {getCurrentTeam, getLastTeam, prepareMyTeams} from '@queries/servers/team'; import {getCurrentUser} from '@queries/servers/user'; @@ -94,8 +94,7 @@ export async function handleUserAddedToTeamEvent(serverUrl: string, msg: WebSock const modelPromises: Array> = []; if (teams?.length && teamMemberships?.length) { const {channels, memberships, categories} = await fetchMyChannelsForTeam(serverUrl, teamId, false, 0, true); - modelPromises.push(prepareCategories(operator, categories)); - modelPromises.push(prepareCategoryChannels(operator, categories)); + modelPromises.push(prepareCategoriesAndCategoriesChannels(operator, categories || [], true)); modelPromises.push(...await prepareMyChannelsForTeam(operator, teamId, channels || [], memberships || [])); const {roles} = await fetchRoles(serverUrl, teamMemberships, memberships, undefined, true); diff --git a/app/queries/servers/categories.ts b/app/queries/servers/categories.ts index f79debb01..2210143ba 100644 --- a/app/queries/servers/categories.ts +++ b/app/queries/servers/categories.ts @@ -8,6 +8,7 @@ import {switchMap, distinctUntilChanged} from 'rxjs/operators'; import {FAVORITES_CATEGORY} from '@constants/categories'; import {MM_TABLES} from '@constants/database'; import {makeCategoryChannelId} from '@utils/categories'; +import {pluckUnique} from '@utils/helpers'; import {observeChannelsByLastPostAt} from './channel'; @@ -35,6 +36,46 @@ export const queryCategoriesByTeamIds = (database: Database, teamIds: string[]) return database.get(CATEGORY).query(Q.where('team_id', Q.oneOf(teamIds))); }; +export async function prepareCategoriesAndCategoriesChannels(operator: ServerDataOperator, categories: CategoryWithChannels[], prune = false) { + try { + const modelPromises: Array> = []; + const preparedCategories = prepareCategories(operator, categories); + if (preparedCategories) { + modelPromises.push(preparedCategories); + } + + const preparedCategoryChannels = prepareCategoryChannels(operator, categories); + if (preparedCategoryChannels) { + modelPromises.push(preparedCategoryChannels); + } + + const models = await Promise.all(modelPromises); + const flattenedModels = models.flat(); + + if (prune && categories.length) { + const remoteCategoryIds = new Set(categories.map((cat) => cat.id)); + + // 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 customCategories = localCategories.filter((c) => c.type === 'custom'); + for await (const custom of customCategories) { + if (!remoteCategoryIds.has(custom.id)) { + const categoryChannels = await custom.categoryChannels.fetch(); + for (const cc of categoryChannels) { + flattenedModels.push(cc.prepareDestroyPermanently()); + } + flattenedModels.push(custom.prepareDestroyPermanently()); + } + } + } + + return flattenedModels; + } catch { + return []; + } +} + export const prepareCategories = (operator: ServerDataOperator, categories?: CategoryWithChannels[]) => { return operator.handleCategories({categories, prepareRecordsOnly: true}); }; diff --git a/app/queries/servers/entry.ts b/app/queries/servers/entry.ts index 3f9ed8b81..c2774517f 100644 --- a/app/queries/servers/entry.ts +++ b/app/queries/servers/entry.ts @@ -5,7 +5,7 @@ import {MM_TABLES} from '@constants/database'; import DatabaseManager from '@database/manager'; import ServerDataOperator from '@database/operator/server_data_operator'; -import {prepareCategories, prepareCategoryChannels} from './categories'; +import {prepareCategoriesAndCategoriesChannels} from './categories'; import {prepareDeleteChannel, prepareMyChannelsForTeam} from './channel'; import {prepareMyPreferences} from './preference'; import {resetWebSocketLastDisconnected} from './system'; @@ -62,8 +62,7 @@ export async function prepareModels({operator, initialTeamId, removeTeams, remov } if (chData?.categories?.length) { - modelPromises.push(prepareCategories(operator, chData.categories)); - modelPromises.push(prepareCategoryChannels(operator, chData.categories)); + modelPromises.push(prepareCategoriesAndCategoriesChannels(operator, chData.categories, true)); } if (chData?.channels?.length && chData.memberships?.length) {