From 844f2c03933b8ec0a10e48361d1e95de460a7fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Espino=20Garc=C3=ADa?= Date: Wed, 22 Jun 2022 23:51:28 +0200 Subject: [PATCH] Let ServerDatabases type hold undefined values (#6423) * Let ServerDatabases type hold undefined values * Avoid model.database --- app/actions/local/channel.test.ts | 2 +- app/actions/remote/channel.ts | 10 +++-- app/actions/remote/role.ts | 12 +++++- app/actions/remote/user.ts | 29 ++++++++++--- app/actions/websocket/preferences.ts | 11 +++-- app/actions/websocket/threads.ts | 43 ++++++------------- .../autocomplete/at_mention/index.ts | 4 +- .../app_command_parser/app_command_parser.ts | 5 ++- app/components/post_draft/index.ts | 2 +- .../post_draft/send_handler/index.ts | 2 +- .../post_list/combined_user_activity/index.ts | 2 +- .../button_binding/index.tsx | 8 ++-- .../embedded_bindings/menu_binding/index.tsx | 8 ++-- .../post_list/post/body/reactions/index.ts | 4 +- app/components/post_list/post/index.ts | 13 +++--- app/database/manager/__mocks__/index.ts | 2 +- app/database/manager/test.ts | 4 +- .../handlers/category.test.ts | 2 +- .../handlers/channel.test.ts | 2 +- .../handlers/group.test.ts | 2 +- .../handlers/index.test.ts | 2 +- .../handlers/post.test.ts | 2 +- .../handlers/reaction.test.ts | 2 +- .../handlers/team.test.ts | 2 +- .../handlers/thread.test.ts | 2 +- .../handlers/user.test.ts | 2 +- .../draft_upload_manager/index.test.ts | 2 +- app/queries/servers/role.ts | 16 +++---- .../destructive_options/archive/index.ts | 6 +-- .../convert_private/index.ts | 2 +- .../find_channels/quick_options/index.ts | 6 +-- .../categories_list/header/index.ts | 6 +-- .../servers_list/server_item/server_item.tsx | 2 +- app/screens/in_app_notification/index.tsx | 6 ++- app/screens/post_options/index.ts | 15 ++++--- test/test_helper.ts | 2 +- types/database/database.d.ts | 2 +- 37 files changed, 136 insertions(+), 108 deletions(-) diff --git a/app/actions/local/channel.test.ts b/app/actions/local/channel.test.ts index 11b86caa1c..f739337790 100644 --- a/app/actions/local/channel.test.ts +++ b/app/actions/local/channel.test.ts @@ -64,7 +64,7 @@ describe('switchToChannel', () => { } as ChannelMembership; beforeEach(async () => { await DatabaseManager.init([serverUrl]); - operator = DatabaseManager.serverDatabases[serverUrl].operator; + operator = DatabaseManager.serverDatabases[serverUrl]!.operator; spyNow = jest.spyOn(Date, 'now').mockImplementation(() => now); }); diff --git a/app/actions/remote/channel.ts b/app/actions/remote/channel.ts index 517378761e..428bb4c12d 100644 --- a/app/actions/remote/channel.ts +++ b/app/actions/remote/channel.ts @@ -631,8 +631,13 @@ export async function markChannelAsRead(serverUrl: string, channelId: string) { } export async function switchToChannelByName(serverUrl: string, channelName: string, teamName: string, errorHandler: (intl: IntlShape) => void, intl: IntlShape) { - const database = DatabaseManager.serverDatabases[serverUrl]?.database; - if (!database) { + let database; + let operator; + try { + const result = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + database = result.database; + operator = result.operator; + } catch (e) { return {error: `${serverUrl} database not found`}; } @@ -737,7 +742,6 @@ export async function switchToChannelByName(serverUrl: string, channelName: stri } const modelPromises: Array> = []; - const {operator} = DatabaseManager.serverDatabases[serverUrl]; if (!(team instanceof Model)) { modelPromises.push(...prepareMyTeams(operator, [team], [(myTeam as TeamMembership)])); } else if (!(myTeam instanceof Model)) { diff --git a/app/actions/remote/role.ts b/app/actions/remote/role.ts index 2856fdf11b..b166d40aaa 100644 --- a/app/actions/remote/role.ts +++ b/app/actions/remote/role.ts @@ -24,8 +24,16 @@ export const fetchRolesIfNeeded = async (serverUrl: string, updatedRoles: string return {error}; } - const database = DatabaseManager.serverDatabases[serverUrl].database; - const operator = DatabaseManager.serverDatabases[serverUrl].operator; + let database; + let operator; + try { + const result = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + database = result.database; + operator = result.operator; + } catch (e) { + return {error: `${serverUrl} database not found`}; + } + let newRoles; if (force) { newRoles = updatedRoles; diff --git a/app/actions/remote/user.ts b/app/actions/remote/user.ts index 837e2383d1..528ef3dfc4 100644 --- a/app/actions/remote/user.ts +++ b/app/actions/remote/user.ts @@ -288,11 +288,20 @@ export async function fetchStatusByIds(serverUrl: string, userIds: string[], fet return {statuses: []}; } + let database; + let operator; + try { + const result = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + database = result.database; + operator = result.operator; + } catch (e) { + return {error: `${serverUrl} database not found`}; + } + try { const statuses = await client.getStatusesByIds(userIds); if (!fetchOnly && DatabaseManager.serverDatabases[serverUrl]) { - const {database, operator} = DatabaseManager.serverDatabases[serverUrl]; if (operator) { const users = await queryUsersById(database, userIds).fetch(); const userStatuses = statuses.reduce((result: Record, s) => { @@ -792,9 +801,12 @@ export const buildProfileImageUrl = (serverUrl: string, userId: string, timestam }; export const autoUpdateTimezone = async (serverUrl: string, {deviceTimezone, userId}: {deviceTimezone: string; userId: string}) => { - const database = DatabaseManager.serverDatabases[serverUrl].database; - if (!database) { - return {error: `No database present for ${serverUrl}`}; + let database; + try { + const result = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + database = result.database; + } catch (e) { + return {error: `${serverUrl} database not found`}; } const currentUser = await getUserById(database, userId); @@ -814,9 +826,12 @@ export const autoUpdateTimezone = async (serverUrl: string, {deviceTimezone, use }; export const fetchTeamAndChannelMembership = async (serverUrl: string, userId: string, teamId: string, channelId?: string) => { - const operator = DatabaseManager.serverDatabases[serverUrl].operator; - if (!operator) { - return {error: `No database present for ${serverUrl}`}; + let operator; + try { + const result = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + operator = result.operator; + } catch (e) { + return {error: `${serverUrl} database not found`}; } let client: Client; diff --git a/app/actions/websocket/preferences.ts b/app/actions/websocket/preferences.ts index 4f93594a44..b14edb2f43 100644 --- a/app/actions/websocket/preferences.ts +++ b/app/actions/websocket/preferences.ts @@ -9,8 +9,13 @@ import {getPostById} from '@queries/servers/post'; import {deletePreferences, differsFromLocalNameFormat} from '@queries/servers/preference'; export async function handlePreferenceChangedEvent(serverUrl: string, msg: WebSocketMessage): Promise { - const operator = DatabaseManager.serverDatabases[serverUrl].operator; - if (!operator) { + let database; + let operator; + try { + const result = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + database = result.database; + operator = result.operator; + } catch (e) { return; } @@ -18,7 +23,7 @@ export async function handlePreferenceChangedEvent(serverUrl: string, msg: WebSo const preference: PreferenceType = JSON.parse(msg.data.preference); handleSavePostAdded(serverUrl, [preference]); - const hasDiffNameFormatPref = await differsFromLocalNameFormat(operator.database, [preference]); + const hasDiffNameFormatPref = await differsFromLocalNameFormat(database, [preference]); if (operator) { await operator.handlePreferences({ diff --git a/app/actions/websocket/threads.ts b/app/actions/websocket/threads.ts index c2c153a96a..f7c1075122 100644 --- a/app/actions/websocket/threads.ts +++ b/app/actions/websocket/threads.ts @@ -2,7 +2,6 @@ // See LICENSE.txt for license information. import {markTeamThreadsAsRead, processReceivedThreads, updateThread} from '@actions/local/thread'; -import DatabaseManager from '@database/manager'; export async function handleThreadUpdatedEvent(serverUrl: string, msg: WebSocketMessage): Promise { try { @@ -18,23 +17,16 @@ export async function handleThreadUpdatedEvent(serverUrl: string, msg: WebSocket } export async function handleThreadReadChangedEvent(serverUrl: string, msg: WebSocketMessage): Promise { - const operator = DatabaseManager.serverDatabases[serverUrl].operator; - if (!operator) { - return; - } - try { - if (operator) { - const {thread_id, timestamp, unread_mentions, unread_replies} = msg.data; - if (thread_id) { - await updateThread(serverUrl, thread_id, { - last_viewed_at: timestamp, - unread_mentions, - unread_replies, - }); - } else { - await markTeamThreadsAsRead(serverUrl, msg.broadcast.team_id); - } + const {thread_id, timestamp, unread_mentions, unread_replies} = msg.data; + if (thread_id) { + await updateThread(serverUrl, thread_id, { + last_viewed_at: timestamp, + unread_mentions, + unread_replies, + }); + } else { + await markTeamThreadsAsRead(serverUrl, msg.broadcast.team_id); } } catch (error) { // Do nothing @@ -42,23 +34,16 @@ export async function handleThreadReadChangedEvent(serverUrl: string, msg: WebSo } export async function handleThreadFollowChangedEvent(serverUrl: string, msg: WebSocketMessage): Promise { - const operator = DatabaseManager.serverDatabases[serverUrl].operator; - if (!operator) { - return; - } - try { - if (operator) { - const {reply_count, state, thread_id} = msg.data as { + const {reply_count, state, thread_id} = msg.data as { reply_count: number; state: boolean; thread_id: string; }; - await updateThread(serverUrl, thread_id, { - is_following: state, - reply_count, - }); - } + await updateThread(serverUrl, thread_id, { + is_following: state, + reply_count, + }); } catch (error) { // Do nothing } diff --git a/app/components/autocomplete/at_mention/index.ts b/app/components/autocomplete/at_mention/index.ts index 5e12e8bad3..187c46aa52 100644 --- a/app/components/autocomplete/at_mention/index.ts +++ b/app/components/autocomplete/at_mention/index.ts @@ -42,9 +42,9 @@ const enhanced = withObservables([], ({database, channelId}: WithDatabaseArgs & switchMap((c) => of$(Boolean(c?.isGroupConstrained))), ); - useChannelMentions = combineLatest([currentUser, currentChannel]).pipe(switchMap(([u, c]) => (u && c ? observePermissionForChannel(c, u, Permissions.USE_CHANNEL_MENTIONS, false) : of$(false)))); + useChannelMentions = combineLatest([currentUser, currentChannel]).pipe(switchMap(([u, c]) => (u && c ? observePermissionForChannel(database, c, u, Permissions.USE_CHANNEL_MENTIONS, false) : of$(false)))); useGroupMentions = combineLatest([currentUser, currentChannel, hasLicense]).pipe( - switchMap(([u, c, lcs]) => (lcs && u && c ? observePermissionForChannel(c, u, Permissions.USE_GROUP_MENTIONS, false) : of$(false))), + switchMap(([u, c, lcs]) => (lcs && u && c ? observePermissionForChannel(database, c, u, Permissions.USE_GROUP_MENTIONS, false) : of$(false))), ); } else { useChannelMentions = of$(false); diff --git a/app/components/autocomplete/slash_suggestion/app_command_parser/app_command_parser.ts b/app/components/autocomplete/slash_suggestion/app_command_parser/app_command_parser.ts index 0fd9b26d6e..264d7feec0 100644 --- a/app/components/autocomplete/slash_suggestion/app_command_parser/app_command_parser.ts +++ b/app/components/autocomplete/slash_suggestion/app_command_parser/app_command_parser.ts @@ -860,12 +860,15 @@ export class AppCommandParser { constructor(serverUrl: string, intl: IntlShape, channelID: string, teamID = '', rootPostID = '', theme: Theme) { this.serverUrl = serverUrl; - this.database = DatabaseManager.serverDatabases[serverUrl]?.database; this.channelID = channelID; this.rootPostID = rootPostID; this.teamID = teamID; this.intl = intl; this.theme = theme; + + // We are making the assumption the database is always present at this level. + // This assumption may not be correct. Please review. + this.database = DatabaseManager.serverDatabases[serverUrl]!.database; } // composeCommandSubmitCall creates the form submission call diff --git a/app/components/post_draft/index.ts b/app/components/post_draft/index.ts index 35bffd143a..bea3d30d6f 100644 --- a/app/components/post_draft/index.ts +++ b/app/components/post_draft/index.ts @@ -50,7 +50,7 @@ const enhanced = withObservables(['channelId', 'rootId', 'channelIsArchived'], ( switchMap((id) => observeChannel(database, id!)), ); - const canPost = combineLatest([channel, currentUser]).pipe(switchMap(([c, u]) => (c && u ? observePermissionForChannel(c, u, Permissions.CREATE_POST, true) : of$(true)))); + const canPost = combineLatest([channel, currentUser]).pipe(switchMap(([c, u]) => (c && u ? observePermissionForChannel(database, c, u, Permissions.CREATE_POST, true) : of$(true)))); const channelIsArchived = channel.pipe(switchMap((c) => (ownProps.channelIsArchived ? of$(true) : of$(c?.deleteAt !== 0)))); const experimentalTownSquareIsReadOnly = observeConfigBooleanValue(database, 'ExperimentalTownSquareIsReadOnly'); diff --git a/app/components/post_draft/send_handler/index.ts b/app/components/post_draft/send_handler/index.ts index fbdea5fe64..bbb23e536b 100644 --- a/app/components/post_draft/send_handler/index.ts +++ b/app/components/post_draft/send_handler/index.ts @@ -59,7 +59,7 @@ const enhanced = withObservables([], (ownProps: WithDatabaseArgs & OwnProps) => return of$(true); } - return u ? observePermissionForChannel(c, u, Permissions.USE_CHANNEL_MENTIONS, false) : of$(false); + return u ? observePermissionForChannel(database, c, u, Permissions.USE_CHANNEL_MENTIONS, false) : of$(false); }), ); diff --git a/app/components/post_list/combined_user_activity/index.ts b/app/components/post_list/combined_user_activity/index.ts index 11a00b8501..0fad263e8a 100644 --- a/app/components/post_list/combined_user_activity/index.ts +++ b/app/components/post_list/combined_user_activity/index.ts @@ -28,7 +28,7 @@ const withCombinedPosts = withObservables(['postId'], ({database, postId}: WithD const posts = queryPostsById(database, postIds).observe(); const post = posts.pipe(map((ps) => generateCombinedPost(postId, ps))); const canDelete = combineLatest([posts, currentUser]).pipe( - switchMap(([ps, u]) => (u ? observePermissionForPost(ps[0], u, Permissions.DELETE_OTHERS_POSTS, false) : of$(false))), + switchMap(([ps, u]) => (u && ps.length ? observePermissionForPost(database, ps[0], u, Permissions.DELETE_OTHERS_POSTS, false) : of$(false))), ); const usernamesById = post.pipe( diff --git a/app/components/post_list/post/body/content/embedded_bindings/button_binding/index.tsx b/app/components/post_list/post/body/content/embedded_bindings/button_binding/index.tsx index 68323d607d..a15a37d247 100644 --- a/app/components/post_list/post/body/content/embedded_bindings/button_binding/index.tsx +++ b/app/components/post_list/post/body/content/embedded_bindings/button_binding/index.tsx @@ -1,6 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import React, {useCallback, useRef} from 'react'; import {useIntl} from 'react-intl'; @@ -18,6 +19,7 @@ import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme'; import ButtonBindingText from './button_binding_text'; +import type {WithDatabaseArgs} from '@typings/database/database'; import type ChannelModel from '@typings/database/models/servers/channel'; import type PostModel from '@typings/database/models/servers/post'; @@ -125,9 +127,9 @@ const ButtonBinding = ({currentTeamId, binding, post, teamID, theme}: Props) => return null; }; -const withTeamId = withObservables(['post'], ({post}: {post: PostModel}) => ({ +const withTeamId = withObservables(['post'], ({post, database}: {post: PostModel} & WithDatabaseArgs) => ({ teamID: post.channel.observe().pipe(map((channel: ChannelModel) => channel.teamId)), - currentTeamId: observeCurrentTeamId(post.database), + currentTeamId: observeCurrentTeamId(database), })); -export default withTeamId(ButtonBinding); +export default withDatabase(withTeamId(ButtonBinding)); diff --git a/app/components/post_list/post/body/content/embedded_bindings/menu_binding/index.tsx b/app/components/post_list/post/body/content/embedded_bindings/menu_binding/index.tsx index a22cb4193b..a6f988f1cf 100644 --- a/app/components/post_list/post/body/content/embedded_bindings/menu_binding/index.tsx +++ b/app/components/post_list/post/body/content/embedded_bindings/menu_binding/index.tsx @@ -1,6 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import React, {useCallback, useState} from 'react'; import {useIntl} from 'react-intl'; @@ -11,6 +12,7 @@ import AutocompleteSelector from '@components/autocomplete_selector'; import {AppBindingLocations, AppCallResponseTypes} from '@constants/apps'; import {useServerUrl} from '@context/server'; import {observeCurrentTeamId} from '@queries/servers/system'; +import {WithDatabaseArgs} from '@typings/database/database'; import {createCallContext} from '@utils/apps'; import type ChannelModel from '@typings/database/models/servers/channel'; @@ -94,9 +96,9 @@ const MenuBinding = ({binding, currentTeamId, post, teamID}: Props) => { ); }; -const withTeamId = withObservables(['post'], ({post}: {post: PostModel}) => ({ +const withTeamId = withObservables(['post'], ({post, database}: {post: PostModel} & WithDatabaseArgs) => ({ teamID: post.channel.observe().pipe(map((channel: ChannelModel) => channel.teamId)), - currentTeamId: observeCurrentTeamId(post.database), + currentTeamId: observeCurrentTeamId(database), })); -export default withTeamId(MenuBinding); +export default withDatabase(withTeamId(MenuBinding)); diff --git a/app/components/post_list/post/body/reactions/index.ts b/app/components/post_list/post/body/reactions/index.ts index 6e0deb54a7..dbb0e12069 100644 --- a/app/components/post_list/post/body/reactions/index.ts +++ b/app/components/post_list/post/body/reactions/index.ts @@ -33,8 +33,8 @@ const withReactions = withObservables(['post'], ({database, post}: WithReactions map(([u, c, readOnly]) => ((c && c.deleteAt > 0) || (c?.name === General.DEFAULT_CHANNEL && !isSystemAdmin(u?.roles || '') && readOnly))), ); - const canAddReaction = currentUser.pipe(switchMap((u) => (u ? observePermissionForPost(post, u, Permissions.ADD_REACTION, true) : of$(true)))); - const canRemoveReaction = currentUser.pipe(switchMap((u) => (u ? observePermissionForPost(post, u, Permissions.REMOVE_REACTION, true) : of$(true)))); + const canAddReaction = currentUser.pipe(switchMap((u) => (u ? observePermissionForPost(database, post, u, Permissions.ADD_REACTION, true) : of$(true)))); + const canRemoveReaction = currentUser.pipe(switchMap((u) => (u ? observePermissionForPost(database, post, u, Permissions.REMOVE_REACTION, true) : of$(true)))); return { canAddReaction, diff --git a/app/components/post_list/post/index.ts b/app/components/post_list/post/index.ts index ca24d18d97..3bddd92132 100644 --- a/app/components/post_list/post/index.ts +++ b/app/components/post_list/post/index.ts @@ -20,6 +20,7 @@ import {areConsecutivePosts, isPostEphemeral} from '@utils/post'; import Post from './post'; +import type {Database} from '@nozbe/watermelondb'; import type {WithDatabaseArgs} from '@typings/database/database'; import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji'; import type PostModel from '@typings/database/models/servers/post'; @@ -35,8 +36,8 @@ type PropsInput = WithDatabaseArgs & { previousPost: PostModel | undefined; } -function observeShouldHighlightReplyBar(currentUser: UserModel, post: PostModel, postsInThread: PostsInThreadModel) { - const myPostsCount = queryPostsBetween(postsInThread.database, postsInThread.earliest, postsInThread.latest, null, currentUser.id, '', post.rootId || post.id).observeCount(); +function observeShouldHighlightReplyBar(database: Database, currentUser: UserModel, post: PostModel, postsInThread: PostsInThreadModel) { + const myPostsCount = queryPostsBetween(database, postsInThread.earliest, postsInThread.latest, null, currentUser.id, '', post.rootId || post.id).observeCount(); const root = post.root.observe().pipe(switchMap((rl) => (rl.length ? rl[0].observe() : of$(undefined)))); return combineLatest([myPostsCount, root]).pipe( @@ -101,7 +102,7 @@ const withPost = withObservables( let isPostAddChannelMember = of$(false); const isOwner = currentUser.id === post.userId; const author = post.userId ? post.author.observe() : of$(null); - const canDelete = observePermissionForPost(post, currentUser, isOwner ? Permissions.DELETE_POST : Permissions.DELETE_OTHERS_POSTS, false); + const canDelete = observePermissionForPost(database, post, currentUser, isOwner ? Permissions.DELETE_POST : Permissions.DELETE_OTHERS_POSTS, false); const isEphemeral = of$(isPostEphemeral(post)); const isSaved = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SAVED_POST, post.id). observeWithColumns(['value']).pipe( @@ -109,13 +110,13 @@ const withPost = withObservables( ); if (post.props?.add_channel_member && isPostEphemeral(post)) { - isPostAddChannelMember = observeCanManageChannelMembers(post, currentUser); + isPostAddChannelMember = observeCanManageChannelMembers(database, post, currentUser); } const highlightReplyBar = post.postsInThread.observe().pipe( switchMap((postsInThreads: PostsInThreadModel[]) => { if (postsInThreads.length) { - return observeShouldHighlightReplyBar(currentUser, post, postsInThreads[0]); + return observeShouldHighlightReplyBar(database, currentUser, post, postsInThreads[0]); } return of$(false); })); @@ -127,7 +128,7 @@ const withPost = withObservables( } if (post.message.length && !(/^\s{4}/).test(post.message)) { - isJumboEmoji = queryAllCustomEmojis(post.database).observe().pipe( + isJumboEmoji = queryAllCustomEmojis(database).observe().pipe( // eslint-disable-next-line max-nested-callbacks switchMap((customEmojis: CustomEmojiModel[]) => of$(hasJumboEmojiOnly(post.message, customEmojis.map((c) => c.name))), ), diff --git a/app/database/manager/__mocks__/index.ts b/app/database/manager/__mocks__/index.ts index 2e84d37a74..5dfb85569d 100644 --- a/app/database/manager/__mocks__/index.ts +++ b/app/database/manager/__mocks__/index.ts @@ -252,7 +252,7 @@ class DatabaseManager { if (database) { const server = await queryActiveServer(database); if (server?.url) { - return this.serverDatabases[server.url].database; + return this.serverDatabases[server.url]!.database; } } diff --git a/app/database/manager/test.ts b/app/database/manager/test.ts index b5b27766b4..6b019cbba8 100644 --- a/app/database/manager/test.ts +++ b/app/database/manager/test.ts @@ -58,7 +58,7 @@ describe('*** Database Manager tests ***', () => { // as we haven't set an active server yet, so the first registered server should be the active one expect(activeServerUrl).toBe(serverUrls[0]); - expect(serverA).toEqual(DatabaseManager.serverDatabases[serverUrls[0]].database); + expect(serverA).toEqual(DatabaseManager.serverDatabases[serverUrls[0]]!.database); await DatabaseManager.setActiveServerDatabase('https://appv2.mattermost.com'); @@ -66,7 +66,7 @@ describe('*** Database Manager tests ***', () => { activeServerUrl = await DatabaseManager.getActiveServerUrl(); const serverB = await DatabaseManager.getActiveServerDatabase(); expect(activeServerUrl).toBe(serverUrls[1]); - expect(serverB).toEqual(DatabaseManager.serverDatabases[serverUrls[1]].database); + expect(serverB).toEqual(DatabaseManager.serverDatabases[serverUrls[1]]!.database); }); it('=> should delete appv1 server from the servers table of App database', async () => { diff --git a/app/database/operator/server_data_operator/handlers/category.test.ts b/app/database/operator/server_data_operator/handlers/category.test.ts index 28e0d481d4..c7773a6800 100644 --- a/app/database/operator/server_data_operator/handlers/category.test.ts +++ b/app/database/operator/server_data_operator/handlers/category.test.ts @@ -14,7 +14,7 @@ describe('*** Operator: Category Handlers tests ***', () => { let operator: ServerDataOperator; beforeAll(async () => { await DatabaseManager.init(['baseHandler.test.com']); - operator = DatabaseManager.serverDatabases['baseHandler.test.com'].operator; + operator = DatabaseManager.serverDatabases['baseHandler.test.com']!.operator; }); it('=> handleCategories: should write to the CATEGORY table', async () => { diff --git a/app/database/operator/server_data_operator/handlers/channel.test.ts b/app/database/operator/server_data_operator/handlers/channel.test.ts index 6bf5d89152..ef60330f3c 100644 --- a/app/database/operator/server_data_operator/handlers/channel.test.ts +++ b/app/database/operator/server_data_operator/handlers/channel.test.ts @@ -20,7 +20,7 @@ describe('*** Operator: Channel Handlers tests ***', () => { let operator: ServerDataOperator; beforeAll(async () => { await DatabaseManager.init(['baseHandler.test.com']); - operator = DatabaseManager.serverDatabases['baseHandler.test.com'].operator; + operator = DatabaseManager.serverDatabases['baseHandler.test.com']!.operator; }); it('=> HandleChannel: should write to the CHANNEL table', async () => { diff --git a/app/database/operator/server_data_operator/handlers/group.test.ts b/app/database/operator/server_data_operator/handlers/group.test.ts index 05faef215b..f967c0d887 100644 --- a/app/database/operator/server_data_operator/handlers/group.test.ts +++ b/app/database/operator/server_data_operator/handlers/group.test.ts @@ -14,7 +14,7 @@ describe('*** Operator: Group Handlers tests ***', () => { let operator: ServerDataOperator; beforeAll(async () => { await DatabaseManager.init(['baseHandler.test.com']); - operator = DatabaseManager.serverDatabases['baseHandler.test.com'].operator; + operator = DatabaseManager.serverDatabases['baseHandler.test.com']!.operator; }); it('=> handleGroups: should write to the GROUP table', async () => { diff --git a/app/database/operator/server_data_operator/handlers/index.test.ts b/app/database/operator/server_data_operator/handlers/index.test.ts index 1f8ab18c49..9a3f3bc637 100644 --- a/app/database/operator/server_data_operator/handlers/index.test.ts +++ b/app/database/operator/server_data_operator/handlers/index.test.ts @@ -15,7 +15,7 @@ describe('*** DataOperator: Base Handlers tests ***', () => { let operator: ServerDataOperator; beforeAll(async () => { await DatabaseManager.init(['baseHandler.test.com']); - operator = DatabaseManager.serverDatabases['baseHandler.test.com'].operator; + operator = DatabaseManager.serverDatabases['baseHandler.test.com']!.operator; }); it('=> HandleRole: should write to the ROLE table', async () => { diff --git a/app/database/operator/server_data_operator/handlers/post.test.ts b/app/database/operator/server_data_operator/handlers/post.test.ts index 73a8c9fabd..69abea6a45 100644 --- a/app/database/operator/server_data_operator/handlers/post.test.ts +++ b/app/database/operator/server_data_operator/handlers/post.test.ts @@ -19,7 +19,7 @@ describe('*** Operator: Post Handlers tests ***', () => { beforeAll(async () => { await DatabaseManager.init(['baseHandler.test.com']); - operator = DatabaseManager.serverDatabases['baseHandler.test.com'].operator; + operator = DatabaseManager.serverDatabases['baseHandler.test.com']!.operator; }); it('=> HandleDraft: should write to the the Draft table', async () => { diff --git a/app/database/operator/server_data_operator/handlers/reaction.test.ts b/app/database/operator/server_data_operator/handlers/reaction.test.ts index c24e474b2b..9cda5389c4 100644 --- a/app/database/operator/server_data_operator/handlers/reaction.test.ts +++ b/app/database/operator/server_data_operator/handlers/reaction.test.ts @@ -9,7 +9,7 @@ describe('*** Operator: User Handlers tests ***', () => { beforeAll(async () => { await DatabaseManager.init(['baseHandler.test.com']); - operator = DatabaseManager.serverDatabases['baseHandler.test.com'].operator; + operator = DatabaseManager.serverDatabases['baseHandler.test.com']!.operator; }); it('=> HandleReactions: should write to Reactions table', async () => { diff --git a/app/database/operator/server_data_operator/handlers/team.test.ts b/app/database/operator/server_data_operator/handlers/team.test.ts index 5b4716cdef..721414903f 100644 --- a/app/database/operator/server_data_operator/handlers/team.test.ts +++ b/app/database/operator/server_data_operator/handlers/team.test.ts @@ -20,7 +20,7 @@ describe('*** Operator: Team Handlers tests ***', () => { let operator: ServerDataOperator; beforeAll(async () => { await DatabaseManager.init(['baseHandler.test.com']); - operator = DatabaseManager.serverDatabases['baseHandler.test.com'].operator; + operator = DatabaseManager.serverDatabases['baseHandler.test.com']!.operator; }); it('=> HandleTeam: should write to the TEAM table', async () => { diff --git a/app/database/operator/server_data_operator/handlers/thread.test.ts b/app/database/operator/server_data_operator/handlers/thread.test.ts index ac526a7b3a..a01f47ef2b 100644 --- a/app/database/operator/server_data_operator/handlers/thread.test.ts +++ b/app/database/operator/server_data_operator/handlers/thread.test.ts @@ -23,7 +23,7 @@ describe('*** Operator: Thread Handlers tests ***', () => { beforeAll(async () => { await DatabaseManager.init(['baseHandler.test.com']); - operator = DatabaseManager.serverDatabases['baseHandler.test.com'].operator; + operator = DatabaseManager.serverDatabases['baseHandler.test.com']!.operator; }); it('=> HandleThreads: should write to the the Thread & ThreadParticipant & ThreadsInTeam tables', async () => { diff --git a/app/database/operator/server_data_operator/handlers/user.test.ts b/app/database/operator/server_data_operator/handlers/user.test.ts index 3782ef75fc..238329f624 100644 --- a/app/database/operator/server_data_operator/handlers/user.test.ts +++ b/app/database/operator/server_data_operator/handlers/user.test.ts @@ -14,7 +14,7 @@ describe('*** Operator: User Handlers tests ***', () => { beforeAll(async () => { await DatabaseManager.init(['baseHandler.test.com']); - operator = DatabaseManager.serverDatabases['baseHandler.test.com'].operator; + operator = DatabaseManager.serverDatabases['baseHandler.test.com']!.operator; }); it('=> HandleReactions: should write to Reactions table', async () => { diff --git a/app/managers/draft_upload_manager/index.test.ts b/app/managers/draft_upload_manager/index.test.ts index 686ba1de14..f13f2ae845 100644 --- a/app/managers/draft_upload_manager/index.test.ts +++ b/app/managers/draft_upload_manager/index.test.ts @@ -69,7 +69,7 @@ describe('draft upload manager', () => { beforeEach(async () => { await DatabaseManager.init([url]); - operator = DatabaseManager.serverDatabases[url].operator; + operator = DatabaseManager.serverDatabases[url]!.operator; AppState.currentState = 'active'; }); diff --git a/app/queries/servers/role.ts b/app/queries/servers/role.ts index d57b631cbc..6726e63dae 100644 --- a/app/queries/servers/role.ts +++ b/app/queries/servers/role.ts @@ -37,8 +37,7 @@ export const queryRolesByNames = (database: Database, names: string[]) => { return database.get(ROLE).query(Q.where('name', Q.oneOf(names))); }; -export function observePermissionForChannel(channel: ChannelModel, user: UserModel, permission: string, defaultValue: boolean) { - const database = channel.database; +export function observePermissionForChannel(database: Database, channel: ChannelModel, user: UserModel, permission: string, defaultValue: boolean) { const myChannel = observeMyChannel(database, channel.id); const myTeam = channel.teamId ? observeMyTeam(database, channel.teamId) : of$(undefined); @@ -56,8 +55,7 @@ export function observePermissionForChannel(channel: ChannelModel, user: UserMod })); } -export function observePermissionForTeam(team: TeamModel, user: UserModel, permission: string, defaultValue: boolean) { - const database = team.database; +export function observePermissionForTeam(database: Database, team: TeamModel, user: UserModel, permission: string, defaultValue: boolean) { return observeMyTeam(database, team.id).pipe( switchMap((myTeam) => { const rolesArray = [...user.roles.split(' ')]; @@ -73,17 +71,17 @@ export function observePermissionForTeam(team: TeamModel, user: UserModel, permi ); } -export function observePermissionForPost(post: PostModel, user: UserModel, permission: string, defaultValue: boolean) { - return observeChannel(post.database, post.channelId).pipe(switchMap((c) => (c ? observePermissionForChannel(c, user, permission, defaultValue) : of$(defaultValue)))); +export function observePermissionForPost(database: Database, post: PostModel, user: UserModel, permission: string, defaultValue: boolean) { + return observeChannel(database, post.channelId).pipe(switchMap((c) => (c ? observePermissionForChannel(database, c, user, permission, defaultValue) : of$(defaultValue)))); } -export function observeCanManageChannelMembers(post: PostModel, user: UserModel) { - return observeChannel(post.database, post.channelId).pipe((switchMap((c) => { +export function observeCanManageChannelMembers(database: Database, post: PostModel, user: UserModel) { + return observeChannel(database, post.channelId).pipe((switchMap((c) => { if (!c || c.deleteAt !== 0 || isDMorGM(c) || c.name === General.DEFAULT_CHANNEL) { return of$(false); } const permission = c.type === General.OPEN_CHANNEL ? Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS : Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS; - return observePermissionForChannel(c, user, permission, true); + return observePermissionForChannel(database, c, user, permission, true); }))); } diff --git a/app/screens/channel_info/destructive_options/archive/index.ts b/app/screens/channel_info/destructive_options/archive/index.ts index aa3079fec6..4defc6f2c3 100644 --- a/app/screens/channel_info/destructive_options/archive/index.ts +++ b/app/screens/channel_info/destructive_options/archive/index.ts @@ -47,10 +47,10 @@ const enhanced = withObservables(['channelId', 'type'], ({channelId, database, t } if (type === General.OPEN_CHANNEL) { - return observePermissionForChannel(ch, u, Permissions.DELETE_PUBLIC_CHANNEL, true); + return observePermissionForChannel(database, ch, u, Permissions.DELETE_PUBLIC_CHANNEL, true); } - return observePermissionForChannel(ch, u, Permissions.DELETE_PRIVATE_CHANNEL, true); + return observePermissionForChannel(database, ch, u, Permissions.DELETE_PRIVATE_CHANNEL, true); }), ); @@ -64,7 +64,7 @@ const enhanced = withObservables(['channelId', 'type'], ({channelId, database, t return of$(false); } - return observePermissionForTeam(t, u, Permissions.MANAGE_TEAM, false); + return observePermissionForTeam(database, t, u, Permissions.MANAGE_TEAM, false); }), ); diff --git a/app/screens/channel_info/destructive_options/convert_private/index.ts b/app/screens/channel_info/destructive_options/convert_private/index.ts index 1a58b54e57..df15a697d0 100644 --- a/app/screens/channel_info/destructive_options/convert_private/index.ts +++ b/app/screens/channel_info/destructive_options/convert_private/index.ts @@ -29,7 +29,7 @@ const enhanced = withObservables(['channelId'], ({channelId, database}: Props) = return of$(false); } - return observePermissionForChannel(ch, u, Permissions.CONVERT_PUBLIC_CHANNEL_TO_PRIVATE, false); + return observePermissionForChannel(database, ch, u, Permissions.CONVERT_PUBLIC_CHANNEL_TO_PRIVATE, false); }), ); diff --git a/app/screens/find_channels/quick_options/index.ts b/app/screens/find_channels/quick_options/index.ts index 08ef67ae54..ccc7956bc2 100644 --- a/app/screens/find_channels/quick_options/index.ts +++ b/app/screens/find_channels/quick_options/index.ts @@ -21,15 +21,15 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { const currentUser = observeCurrentUser(database); const canJoinChannels = combineLatest([currentUser, team]).pipe( - switchMap(([u, t]) => (t && u ? observePermissionForTeam(t, u, Permissions.JOIN_PUBLIC_CHANNELS, true) : of$(false))), + switchMap(([u, t]) => (t && u ? observePermissionForTeam(database, t, u, Permissions.JOIN_PUBLIC_CHANNELS, true) : of$(false))), ); const canCreatePublicChannels = combineLatest([currentUser, team]).pipe( - switchMap(([u, t]) => (t && u ? observePermissionForTeam(t, u, Permissions.CREATE_PUBLIC_CHANNEL, true) : of$(false))), + switchMap(([u, t]) => (t && u ? observePermissionForTeam(database, t, u, Permissions.CREATE_PUBLIC_CHANNEL, true) : of$(false))), ); const canCreatePrivateChannels = combineLatest([currentUser, team]).pipe( - switchMap(([u, t]) => (t && u ? observePermissionForTeam(t, u, Permissions.CREATE_PRIVATE_CHANNEL, false) : of$(false))), + switchMap(([u, t]) => (t && u ? observePermissionForTeam(database, t, u, Permissions.CREATE_PRIVATE_CHANNEL, false) : of$(false))), ); const canCreateChannels = combineLatest([canCreatePublicChannels, canCreatePrivateChannels]).pipe( diff --git a/app/screens/home/channel_list/categories_list/header/index.ts b/app/screens/home/channel_list/categories_list/header/index.ts index 4e7ad2e6df..797e8105f1 100644 --- a/app/screens/home/channel_list/categories_list/header/index.ts +++ b/app/screens/home/channel_list/categories_list/header/index.ts @@ -22,17 +22,17 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { const currentUser = observeCurrentUser(database); const canJoinChannels = combineLatest([currentUser, team]).pipe( - switchMap(([u, t]) => (t && u ? observePermissionForTeam(t, u, Permissions.JOIN_PUBLIC_CHANNELS, true) : of$(false))), + switchMap(([u, t]) => (t && u ? observePermissionForTeam(database, t, u, Permissions.JOIN_PUBLIC_CHANNELS, true) : of$(false))), distinctUntilChanged(), ); const canCreatePublicChannels = combineLatest([currentUser, team]).pipe( - switchMap(([u, t]) => (t && u ? observePermissionForTeam(t, u, Permissions.CREATE_PUBLIC_CHANNEL, true) : of$(false))), + switchMap(([u, t]) => (t && u ? observePermissionForTeam(database, t, u, Permissions.CREATE_PUBLIC_CHANNEL, true) : of$(false))), distinctUntilChanged(), ); const canCreatePrivateChannels = combineLatest([currentUser, team]).pipe( - switchMap(([u, t]) => (t && u ? observePermissionForTeam(t, u, Permissions.CREATE_PRIVATE_CHANNEL, false) : of$(false))), + switchMap(([u, t]) => (t && u ? observePermissionForTeam(database, t, u, Permissions.CREATE_PRIVATE_CHANNEL, false) : of$(false))), distinctUntilChanged(), ); diff --git a/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx b/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx index 340c9e51a8..28f20350d5 100644 --- a/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx +++ b/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx @@ -466,7 +466,7 @@ const ServerItem = ({ {Boolean(database) && server.lastActiveAt > 0 && } {showTutorial && diff --git a/app/screens/in_app_notification/index.tsx b/app/screens/in_app_notification/index.tsx index 403bc7f7d8..2e9f718d12 100644 --- a/app/screens/in_app_notification/index.tsx +++ b/app/screens/in_app_notification/index.tsx @@ -143,6 +143,8 @@ const InAppNotification = ({componentId, serverName, serverUrl, notification}: I // eslint-disable-next-line new-cap const gesture = Gesture.Pan().activeOffsetY(-20).onStart(() => runOnJS(animateDismissOverlay)()); + const database = DatabaseManager.serverDatabases[serverUrl]?.database; + return ( @@ -156,14 +158,16 @@ const InAppNotification = ({componentId, serverName, serverUrl, notification}: I onPress={notificationTapped} activeOpacity={1} > + {Boolean(database) && + } <View style={styles.flex}> diff --git a/app/screens/post_options/index.ts b/app/screens/post_options/index.ts index 2a9d44e8a2..438b597cee 100644 --- a/app/screens/post_options/index.ts +++ b/app/screens/post_options/index.ts @@ -20,6 +20,7 @@ import {isSystemAdmin} from '@utils/user'; import PostOptions from './post_options'; +import type {Database} from '@nozbe/watermelondb'; import type {WithDatabaseArgs} from '@typings/database/database'; import type ChannelModel from '@typings/database/models/servers/channel'; import type PostModel from '@typings/database/models/servers/post'; @@ -33,7 +34,7 @@ type EnhancedProps = WithDatabaseArgs & { location: string; } -const observeCanEditPost = (isOwner: boolean, post: PostModel, postEditTimeLimit: number, isLicensed: boolean, channel: ChannelModel, user: UserModel) => { +const observeCanEditPost = (database: Database, isOwner: boolean, post: PostModel, postEditTimeLimit: number, isLicensed: boolean, channel: ChannelModel, user: UserModel) => { if (!post || isSystemMessage(post)) { return of$(false); } @@ -45,11 +46,11 @@ const observeCanEditPost = (isOwner: boolean, post: PostModel, postEditTimeLimit } } - return observePermissionForChannel(channel, user, Permissions.EDIT_POST, false).pipe(switchMap((v) => { + return observePermissionForChannel(database, channel, user, Permissions.EDIT_POST, false).pipe(switchMap((v) => { if (!v || isOwner) { return of$(v); } - return observePermissionForChannel(channel, user, Permissions.EDIT_OTHERS_POSTS, false); + return observePermissionForChannel(database, channel, user, Permissions.EDIT_OTHERS_POSTS, false); })); }; @@ -78,11 +79,11 @@ const enhanced = withObservables([], ({combinedPost, post, showAddReaction, loca const serverVersion = config.pipe(switchMap((cfg) => of$(cfg?.Version || ''))); const postEditTimeLimit = config.pipe(switchMap((cfg) => of$(parseInt(cfg?.PostEditTimeLimit || '-1', 10)))); - const canPostPermission = combineLatest([channel, currentUser]).pipe(switchMap(([c, u]) => ((c && u) ? observePermissionForChannel(c, u, Permissions.CREATE_POST, false) : of$(false)))); - const hasAddReactionPermission = currentUser.pipe(switchMap((u) => (u ? observePermissionForPost(post, u, Permissions.ADD_REACTION, true) : of$(false)))); + const canPostPermission = combineLatest([channel, currentUser]).pipe(switchMap(([c, u]) => ((c && u) ? observePermissionForChannel(database, c, u, Permissions.CREATE_POST, false) : of$(false)))); + const hasAddReactionPermission = currentUser.pipe(switchMap((u) => (u ? observePermissionForPost(database, post, u, Permissions.ADD_REACTION, true) : of$(false)))); const canDeletePostPermission = currentUser.pipe(switchMap((u) => { const isOwner = post.userId === u?.id; - return u ? observePermissionForPost(post, u, isOwner ? Permissions.DELETE_POST : Permissions.DELETE_OTHERS_POSTS, false) : of$(false); + return u ? observePermissionForPost(database, post, u, isOwner ? Permissions.DELETE_POST : Permissions.DELETE_OTHERS_POSTS, false) : of$(false); })); const experimentalTownSquareIsReadOnly = config.pipe(switchMap((value) => of$(value?.ExperimentalTownSquareIsReadOnly === 'true'))); @@ -117,7 +118,7 @@ const enhanced = withObservables([], ({combinedPost, post, showAddReaction, loca const canEdit = combineLatest([postEditTimeLimit, isLicensed, channel, currentUser, channelIsArchived, channelIsReadOnly, canEditUntil, canPostPermission]).pipe( switchMap(([lt, ls, c, u, isArchived, isReadOnly, until, canPost]) => { const isOwner = u?.id === post.userId; - const canEditPostPermission = (c && u) ? observeCanEditPost(isOwner, post, lt, ls, c, u) : of$(false); + const canEditPostPermission = (c && u) ? observeCanEditPost(database, isOwner, post, lt, ls, c, u) : of$(false); const timeNotReached = (until === -1) || (until > Date.now()); return canEditPostPermission.pipe( // eslint-disable-next-line max-nested-callbacks diff --git a/test/test_helper.ts b/test/test_helper.ts index 42ca3b4aa6..2e1dff9527 100644 --- a/test/test_helper.ts +++ b/test/test_helper.ts @@ -54,7 +54,7 @@ class TestHelper { setupServerDatabase = async () => { const serverUrl = 'https://appv1.mattermost.com'; await DatabaseManager.init([serverUrl]); - const {database, operator} = DatabaseManager.serverDatabases[serverUrl]; + const {database, operator} = DatabaseManager.serverDatabases[serverUrl]!; this.initMockEntities(); diff --git a/types/database/database.d.ts b/types/database/database.d.ts index 90973aabe1..5b4c406820 100644 --- a/types/database/database.d.ts +++ b/types/database/database.d.ts @@ -42,7 +42,7 @@ export type ServerDatabase = { } export type ServerDatabases = { - [x: string]: ServerDatabase; + [x: string]: ServerDatabase | undefined; }; export type TransformerArgs = {