From cfb09ce7d71a9f8945eab042ddc1c1c00a17180a Mon Sep 17 00:00:00 2001 From: Mattermost Build Date: Fri, 27 Jan 2023 18:10:45 +0200 Subject: [PATCH] Do not access record children directly to avoid crashes if the child is not present in the db (#7028) (#7036) (cherry picked from commit 50b845452e67ccb40b932d18269de61cd054f4a9) Co-authored-by: Elias Nahum --- app/components/channel_item/index.ts | 4 ++-- .../copy_permalink_option/index.tsx | 3 ++- .../follow_thread_option/index.ts | 8 +++++--- app/components/files/index.ts | 3 ++- app/components/post_draft/post_input/index.ts | 4 ++-- .../post_draft/send_handler/index.ts | 4 ++-- .../post_list/post/body/add_members/index.ts | 6 +++--- .../button_binding/index.tsx | 4 ++-- .../embedded_bindings/menu_binding/index.tsx | 4 ++-- .../post_list/post/body/reactions/index.ts | 3 ++- app/components/post_list/post/footer/index.ts | 2 +- app/components/post_list/post/header/index.ts | 10 +++++----- app/components/post_list/post/index.ts | 20 ++++++++++--------- .../post_list/post/system_message/index.tsx | 10 +++++++--- .../channel_info/index.ts | 11 ++++++---- app/queries/servers/channel.ts | 12 +++++++++++ app/queries/servers/file.ts | 13 +++++++++++- app/queries/servers/reaction.ts | 12 +++++++++++ app/queries/servers/thread.ts | 8 +++++--- .../intro/direct_channel/index.ts | 3 ++- .../intro/direct_channel/member/index.ts | 7 +++++-- .../channel_info/title/group_message/index.ts | 4 ++-- app/screens/edit_post/index.tsx | 3 ++- app/screens/gallery/footer/index.ts | 3 ++- app/screens/permalink/index.ts | 3 ++- app/screens/post_options/index.ts | 6 ++++-- app/screens/reactions/index.ts | 3 ++- app/screens/thread_options/index.ts | 4 ++-- .../components/channel_item/index.ts | 4 ++-- 29 files changed, 121 insertions(+), 60 deletions(-) diff --git a/app/components/channel_item/index.ts b/app/components/channel_item/index.ts index 1568ce814..0e881b2f4 100644 --- a/app/components/channel_item/index.ts +++ b/app/components/channel_item/index.ts @@ -10,7 +10,7 @@ import {switchMap, distinctUntilChanged} from 'rxjs/operators'; import {observeChannelsWithCalls} from '@calls/state'; import {General} from '@constants'; import {withServerUrl} from '@context/server'; -import {observeChannelSettings, observeMyChannel} from '@queries/servers/channel'; +import {observeChannelSettings, observeMyChannel, queryChannelMembers} from '@queries/servers/channel'; import {queryDraft} from '@queries/servers/drafts'; import {observeCurrentChannelId, observeCurrentUserId} from '@queries/servers/system'; import {observeTeam} from '@queries/servers/team'; @@ -67,7 +67,7 @@ const enhance = withObservables(['channel', 'showTeamName'], ({ let membersCount = of$(0); if (channel.type === General.GM_CHANNEL) { - membersCount = channel.members.observeCount(false); + membersCount = queryChannelMembers(database, channel.id).observeCount(false); } const isUnread = myChannel.pipe( diff --git a/app/components/common_post_options/copy_permalink_option/index.tsx b/app/components/common_post_options/copy_permalink_option/index.tsx index afffeb18a..e598e6903 100644 --- a/app/components/common_post_options/copy_permalink_option/index.tsx +++ b/app/components/common_post_options/copy_permalink_option/index.tsx @@ -6,6 +6,7 @@ import withObservables from '@nozbe/with-observables'; import {combineLatest, of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; +import {observeChannel} from '@queries/servers/channel'; import {observeCurrentTeamId} from '@queries/servers/system'; import {observeTeam} from '@queries/servers/team'; @@ -17,7 +18,7 @@ import type TeamModel from '@typings/database/models/servers/team'; const enhanced = withObservables(['post'], ({post, database}: WithDatabaseArgs & { post: PostModel }) => { const currentTeamId = observeCurrentTeamId(database); - const channel = post.channel.observe(); + const channel = observeChannel(database, post.id); const teamName = combineLatest([channel, currentTeamId]).pipe( switchMap(([c, tid]) => { diff --git a/app/components/common_post_options/follow_thread_option/index.ts b/app/components/common_post_options/follow_thread_option/index.ts index 0b27c98cb..6439db5f3 100644 --- a/app/components/common_post_options/follow_thread_option/index.ts +++ b/app/components/common_post_options/follow_thread_option/index.ts @@ -1,18 +1,20 @@ // 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 {observeTeamIdByThread} from '@queries/servers/thread'; import FollowThreadOption from './follow_thread_option'; +import type {WithDatabaseArgs} from '@typings/database/database'; import type ThreadModel from '@typings/database/models/servers/thread'; -const enhanced = withObservables(['thread'], ({thread}: { thread: ThreadModel }) => { +const enhanced = withObservables(['thread'], ({thread, database}: {thread: ThreadModel} & WithDatabaseArgs) => { return { - teamId: observeTeamIdByThread(thread), + teamId: observeTeamIdByThread(database, thread), }; }); -export default enhanced(FollowThreadOption); +export default withDatabase(enhanced(FollowThreadOption)); diff --git a/app/components/files/index.ts b/app/components/files/index.ts index 7a49560ca..2f034ab4b 100644 --- a/app/components/files/index.ts +++ b/app/components/files/index.ts @@ -6,6 +6,7 @@ import withObservables from '@nozbe/with-observables'; import {combineLatest, of as of$, from as from$} from 'rxjs'; import {map, switchMap} from 'rxjs/operators'; +import {queryFilesForPost} from '@queries/servers/file'; import {observeConfigBooleanValue, observeLicense} from '@queries/servers/system'; import {fileExists} from '@utils/file'; @@ -47,7 +48,7 @@ const enhance = withObservables(['post'], ({database, post}: EnhanceProps) => { map(([download, compliance]) => compliance || download), ); - const filesInfo = post.files.observeWithColumns(['local_path']).pipe( + const filesInfo = queryFilesForPost(database, post.id).observeWithColumns(['local_path']).pipe( switchMap((fs) => from$(filesLocalPathValidation(fs, post.userId))), ); diff --git a/app/components/post_draft/post_input/index.ts b/app/components/post_draft/post_input/index.ts index e78482e6f..48b162d38 100644 --- a/app/components/post_draft/post_input/index.ts +++ b/app/components/post_draft/post_input/index.ts @@ -7,7 +7,7 @@ import React from 'react'; import {of as of$} from 'rxjs'; import {switchMap, distinctUntilChanged} from 'rxjs/operators'; -import {observeChannel} from '@queries/servers/channel'; +import {observeChannel, observeChannelInfo} from '@queries/servers/channel'; import {observeConfigBooleanValue, observeConfigIntValue} from '@queries/servers/system'; import PostInput from './post_input'; @@ -31,7 +31,7 @@ const enhanced = withObservables(['channelId'], ({database, channelId}: WithData ); const membersInChannel = channel.pipe( - switchMap((c) => (c ? c.info.observe() : of$({memberCount: 0}))), + switchMap((c) => (c ? observeChannelInfo(database, c.id) : of$({memberCount: 0}))), switchMap((i: ChannelInfoModel) => of$(i.memberCount)), distinctUntilChanged(), ); diff --git a/app/components/post_draft/send_handler/index.ts b/app/components/post_draft/send_handler/index.ts index 590ac7f7d..c80f1b592 100644 --- a/app/components/post_draft/send_handler/index.ts +++ b/app/components/post_draft/send_handler/index.ts @@ -8,7 +8,7 @@ import {switchMap} from 'rxjs/operators'; import {General, Permissions} from '@constants'; import {MAX_MESSAGE_LENGTH_FALLBACK} from '@constants/post_draft'; -import {observeChannel, observeCurrentChannel} from '@queries/servers/channel'; +import {observeChannel, observeChannelInfo, observeCurrentChannel} from '@queries/servers/channel'; import {queryAllCustomEmojis} from '@queries/servers/custom_emoji'; import {observePermissionForChannel} from '@queries/servers/role'; import {observeConfigBooleanValue, observeConfigIntValue, observeCurrentUserId} from '@queries/servers/system'; @@ -56,7 +56,7 @@ const enhanced = withObservables([], (ownProps: WithDatabaseArgs & OwnProps) => }), ); - const channelInfo = channel.pipe(switchMap((c) => (c ? c.info.observe() : of$(undefined)))); + const channelInfo = channel.pipe(switchMap((c) => (c ? observeChannelInfo(database, c.id) : of$(undefined)))); const membersCount = channelInfo.pipe( switchMap((i) => (i ? of$(i.memberCount) : of$(0))), ); diff --git a/app/components/post_list/post/body/add_members/index.ts b/app/components/post_list/post/body/add_members/index.ts index 9073a69f1..159fa04ba 100644 --- a/app/components/post_list/post/body/add_members/index.ts +++ b/app/components/post_list/post/body/add_members/index.ts @@ -6,19 +6,19 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; +import {observeChannel} from '@queries/servers/channel'; import {observeCurrentUser} from '@queries/servers/user'; import AddMembers from './add_members'; 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'; const enhance = withObservables(['post'], ({database, post}: WithDatabaseArgs & {post: PostModel}) => ({ currentUser: observeCurrentUser(database), - channelType: post.channel.observe().pipe( + channelType: observeChannel(database, post.channelId).pipe( switchMap( - (channel: ChannelModel) => (channel ? of$(channel.type) : of$(null)), + (channel) => (channel ? of$(channel.type) : of$(null)), ), ), })); 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 117098c75..9d6277a5c 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 @@ -12,6 +12,7 @@ import {handleBindingClick, postEphemeralCallResponseForPost} from '@actions/rem import {handleGotoLocation} from '@actions/remote/command'; import {AppBindingLocations, AppCallResponseTypes} from '@constants/apps'; import {useServerUrl} from '@context/server'; +import {observeChannel} from '@queries/servers/channel'; import {observeCurrentTeamId} from '@queries/servers/system'; import {showAppForm} from '@screens/navigation'; import {createCallContext} from '@utils/apps'; @@ -22,7 +23,6 @@ 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'; type Props = { @@ -137,7 +137,7 @@ const ButtonBinding = ({currentTeamId, binding, post, teamID, theme}: Props) => }; const withTeamId = withObservables(['post'], ({post, database}: {post: PostModel} & WithDatabaseArgs) => ({ - teamID: post.channel.observe().pipe(map((channel: ChannelModel) => channel.teamId)), + teamID: observeChannel(database, post.channelId).pipe(map((channel) => channel?.teamId)), currentTeamId: observeCurrentTeamId(database), })); 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 db9d89a99..1502be76a 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 @@ -10,11 +10,11 @@ import {postEphemeralCallResponseForPost} from '@actions/remote/apps'; import AutocompleteSelector from '@components/autocomplete_selector'; import {useServerUrl} from '@context/server'; import {useAppBinding} from '@hooks/apps'; +import {observeChannel} from '@queries/servers/channel'; import {observeCurrentTeamId} from '@queries/servers/system'; import {logDebug} from '@utils/log'; 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'; type Props = { @@ -79,7 +79,7 @@ const MenuBinding = ({binding, currentTeamId, post, teamID}: Props) => { }; const withTeamId = withObservables(['post'], ({post, database}: {post: PostModel} & WithDatabaseArgs) => ({ - teamID: post.channel.observe().pipe(map((channel: ChannelModel) => channel.teamId)), + teamID: observeChannel(database, post.channelId).pipe(map((channel) => channel?.teamId)), currentTeamId: observeCurrentTeamId(database), })); diff --git a/app/components/post_list/post/body/reactions/index.ts b/app/components/post_list/post/body/reactions/index.ts index 9c616e060..f8f1f3236 100644 --- a/app/components/post_list/post/body/reactions/index.ts +++ b/app/components/post_list/post/body/reactions/index.ts @@ -8,6 +8,7 @@ import {map, switchMap} from 'rxjs/operators'; import {General, Permissions} from '@constants'; import {observeChannel} from '@queries/servers/channel'; +import {observeReactionsForPost} from '@queries/servers/reaction'; import {observePermissionForPost} from '@queries/servers/role'; import {observeConfigBooleanValue, observeCurrentUserId} from '@queries/servers/system'; import {observeUser} from '@queries/servers/user'; @@ -42,7 +43,7 @@ const withReactions = withObservables(['post'], ({database, post}: WithReactions currentUserId, disabled, postId: of$(post.id), - reactions: post.reactions.observe(), + reactions: observeReactionsForPost(database, post.id), }; }); diff --git a/app/components/post_list/post/footer/index.ts b/app/components/post_list/post/footer/index.ts index 80fdf7551..6aec4e0df 100644 --- a/app/components/post_list/post/footer/index.ts +++ b/app/components/post_list/post/footer/index.ts @@ -16,7 +16,7 @@ const enhanced = withObservables( ({database, thread}: WithDatabaseArgs & {thread: ThreadModel}) => { return { participants: queryThreadParticipants(database, thread.id).observe(), - teamId: observeTeamIdByThread(thread), + teamId: observeTeamIdByThread(database, thread), }; }, ); diff --git a/app/components/post_list/post/header/index.ts b/app/components/post_list/post/header/index.ts index dc1fade7e..d56e8ce49 100644 --- a/app/components/post_list/post/header/index.ts +++ b/app/components/post_list/post/header/index.ts @@ -8,10 +8,10 @@ import {map, switchMap} from 'rxjs/operators'; import {Preferences} from '@constants'; import {getPreferenceAsBool} from '@helpers/api/preference'; -import {observePostAuthor, queryPostReplies} from '@queries/servers/post'; +import {observePost, observePostAuthor, queryPostReplies} from '@queries/servers/post'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import {observeConfigBooleanValue} from '@queries/servers/system'; -import {observeTeammateNameDisplay} from '@queries/servers/user'; +import {observeTeammateNameDisplay, observeUser} from '@queries/servers/user'; import Header from './header'; @@ -35,9 +35,9 @@ const withHeaderProps = withObservables( const teammateNameDisplay = observeTeammateNameDisplay(database); const commentCount = queryPostReplies(database, post.rootId || post.id).observeCount(); const isCustomStatusEnabled = observeConfigBooleanValue(database, 'EnableCustomUserStatuses'); - const rootPostAuthor = differentThreadSequence ? post.root.observe().pipe(switchMap((root) => { - if (root.length) { - return root[0].author.observe(); + const rootPostAuthor = differentThreadSequence ? observePost(database, post.rootId).pipe(switchMap((root) => { + if (root) { + return observeUser(database, root.userId); } return of$(null); diff --git a/app/components/post_list/post/index.ts b/app/components/post_list/post/index.ts index 5c47d4ab3..9863abade 100644 --- a/app/components/post_list/post/index.ts +++ b/app/components/post_list/post/index.ts @@ -8,7 +8,9 @@ import {of as of$, combineLatest} from 'rxjs'; import {switchMap, distinctUntilChanged} from 'rxjs/operators'; import {Permissions, Preferences, Screens} from '@constants'; -import {observePostAuthor, queryPostsBetween} from '@queries/servers/post'; +import {queryFilesForPost} from '@queries/servers/file'; +import {observePost, observePostAuthor, queryPostsBetween} from '@queries/servers/post'; +import {queryReactionsForPost} from '@queries/servers/reaction'; import {observeCanManageChannelMembers, observePermissionForPost} from '@queries/servers/role'; import {observeIsPostPriorityEnabled} from '@queries/servers/system'; import {observeThreadById} from '@queries/servers/thread'; @@ -34,7 +36,7 @@ type PropsInput = WithDatabaseArgs & { 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)))); + const root = observePost(database, post.rootId); return combineLatest([myPostsCount, root]).pipe( switchMap(([mpc, r]) => { @@ -62,14 +64,14 @@ function observeShouldHighlightReplyBar(database: Database, currentUser: UserMod ); } -function observeHasReplies(post: PostModel) { +function observeHasReplies(database: Database, post: PostModel) { if (!post.rootId) { return post.postsInThread.observe().pipe(switchMap((c) => of$(c.length > 0))); } - return post.root.observe().pipe(switchMap((rl) => { - if (rl.length) { - return rl[0].postsInThread.observe().pipe(switchMap((c) => of$(c.length > 0))); + return observePost(database, post.rootId).pipe(switchMap((r) => { + if (r) { + return r.postsInThread.observe().pipe(switchMap((c) => of$(c.length > 0))); } return of$(false); })); @@ -122,19 +124,19 @@ const withPost = withObservables( isLastReply = of$(!(nextPost?.rootId === post.rootId)); } - const hasReplies = observeHasReplies(post);//Need to review and understand + const hasReplies = observeHasReplies(database, post);//Need to review and understand const isConsecutivePost = author.pipe( switchMap((user) => of$(Boolean(post && previousPost && !user?.isBot && areConsecutivePosts(post, previousPost)))), distinctUntilChanged(), ); - const hasFiles = post.files.observeCount().pipe( + const hasFiles = queryFilesForPost(database, post.id).observeCount().pipe( switchMap((c) => of$(c > 0)), distinctUntilChanged(), ); - const hasReactions = post.reactions.observeCount().pipe( + const hasReactions = queryReactionsForPost(database, post.id).observeCount().pipe( switchMap((c) => of$(c > 0)), distinctUntilChanged(), ); diff --git a/app/components/post_list/post/system_message/index.tsx b/app/components/post_list/post/system_message/index.tsx index 293f7f042..ebf840e0d 100644 --- a/app/components/post_list/post/system_message/index.tsx +++ b/app/components/post_list/post/system_message/index.tsx @@ -1,14 +1,18 @@ // 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 {observeUser} from '@queries/servers/user'; + import SystemMessage from './system_message'; +import type {WithDatabaseArgs} from '@typings/database/database'; import type PostModel from '@typings/database/models/servers/post'; -const withPost = withObservables(['post'], ({post}: {post: PostModel}) => ({ - author: post.author.observe(), +const enhance = withObservables(['post'], ({post, database}: {post: PostModel} & WithDatabaseArgs) => ({ + author: observeUser(database, post.userId), })); -export default withPost(SystemMessage); +export default withDatabase(enhance(SystemMessage)); diff --git a/app/components/post_with_channel_info/channel_info/index.ts b/app/components/post_with_channel_info/channel_info/index.ts index af0c1675d..131cbd71b 100644 --- a/app/components/post_with_channel_info/channel_info/index.ts +++ b/app/components/post_with_channel_info/channel_info/index.ts @@ -1,27 +1,30 @@ // 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 {switchMap, of as of$} from 'rxjs'; +import {observeChannel} from '@queries/servers/channel'; import {observeTeam} from '@queries/servers/team'; import ChannelInfo from './channel_info'; +import type {WithDatabaseArgs} from '@typings/database/database'; import type PostModel from '@typings/database/models/servers/post'; -const enhance = withObservables(['post'], ({post}: {post: PostModel}) => { - const channel = post.channel.observe(); +const enhance = withObservables(['post'], ({post, database}: {post: PostModel} & WithDatabaseArgs) => { + const channel = observeChannel(database, post.channelId); return { channelName: channel.pipe( switchMap((chan) => (chan ? of$(chan.displayName) : '')), ), teamName: channel.pipe( - switchMap((chan) => (chan && chan.teamId ? observeTeam(post.database, chan.teamId) : of$(null))), + switchMap((chan) => (chan && chan.teamId ? observeTeam(database, chan.teamId) : of$(null))), switchMap((team) => of$(team?.displayName || null)), ), }; }); -export default enhance(ChannelInfo); +export default withDatabase(enhance(ChannelInfo)); diff --git a/app/queries/servers/channel.ts b/app/queries/servers/channel.ts index 12e332dc6..23da2a112 100644 --- a/app/queries/servers/channel.ts +++ b/app/queries/servers/channel.ts @@ -1,6 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +/* eslint-disable max-lines */ + import {Database, Model, Q, Query, Relation} from '@nozbe/watermelondb'; import {of as of$, Observable} from 'rxjs'; import {map as map$, switchMap, distinctUntilChanged} from 'rxjs/operators'; @@ -664,3 +666,13 @@ export const queryChannelsForAutocomplete = (database: Database, matchTerm: stri return database.get(CHANNEL).query(...clauses); }; + +export const queryChannelMembers = (database: Database, channelId: string) => { + return database.get(CHANNEL_MEMBERSHIP).query( + Q.where('channel_id', channelId), + ); +}; + +export const observeChannelMembers = (database: Database, channelId: string) => { + return queryChannelMembers(database, channelId).observe(); +}; diff --git a/app/queries/servers/file.ts b/app/queries/servers/file.ts index 035a78471..7226058d2 100644 --- a/app/queries/servers/file.ts +++ b/app/queries/servers/file.ts @@ -1,9 +1,10 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {Database, Q} from '@nozbe/watermelondb'; + import {MM_TABLES} from '@constants/database'; -import type {Database} from '@nozbe/watermelondb'; import type FileModel from '@typings/database/models/servers/file'; const {SERVER: {FILE}} = MM_TABLES; @@ -16,3 +17,13 @@ export const getFileById = async (database: Database, fileId: string) => { return undefined; } }; + +export const queryFilesForPost = (database: Database, postId: string) => { + return database.get(FILE).query( + Q.where('post_id', postId), + ); +}; + +export const observeFilesForPost = (database: Database, postId: string) => { + return queryFilesForPost(database, postId).observe(); +}; diff --git a/app/queries/servers/reaction.ts b/app/queries/servers/reaction.ts index f1e4edd31..2b6e26280 100644 --- a/app/queries/servers/reaction.ts +++ b/app/queries/servers/reaction.ts @@ -6,6 +6,7 @@ import {Database, Q} from '@nozbe/watermelondb'; import {MM_TABLES} from '@constants/database'; import type ReactionModel from '@typings/database/models/servers/reaction'; + const {SERVER: {REACTION}} = MM_TABLES; export const queryReaction = (database: Database, emojiName: string, postId: string, userId: string) => { @@ -15,3 +16,14 @@ export const queryReaction = (database: Database, emojiName: string, postId: str Q.where('user_id', userId), ); }; + +export const queryReactionsForPost = (database: Database, postId: string) => { + return database.get(REACTION).query( + Q.where('post_id', postId), + ); +}; + +export const observeReactionsForPost = (database: Database, postId: string) => { + return queryReactionsForPost(database, postId).observe(); +}; + diff --git a/app/queries/servers/thread.ts b/app/queries/servers/thread.ts index 4a200ad8e..beba32047 100644 --- a/app/queries/servers/thread.ts +++ b/app/queries/servers/thread.ts @@ -9,6 +9,8 @@ import {Preferences} from '@constants'; import {MM_TABLES} from '@constants/database'; import {processIsCRTEnabled} from '@utils/thread'; +import {observeChannel} from './channel'; +import {observePost} from './post'; import {queryPreferencesByCategoryAndName} from './preference'; import {getConfig, observeConfigValue} from './system'; @@ -61,13 +63,13 @@ export const observeThreadById = (database: Database, threadId: string) => { ); }; -export const observeTeamIdByThread = (thread: ThreadModel) => { - return thread.post.observe().pipe( +export const observeTeamIdByThread = (database: Database, thread: ThreadModel) => { + return observePost(database, thread.id).pipe( switchMap((post) => { if (!post) { return of$(undefined); } - return post.channel.observe().pipe( + return observeChannel(database, post.channelId).pipe( switchMap((channel) => of$(channel?.teamId)), ); }), diff --git a/app/screens/channel/channel_post_list/intro/direct_channel/index.ts b/app/screens/channel/channel_post_list/intro/direct_channel/index.ts index 8a7e8402c..23a561cfb 100644 --- a/app/screens/channel/channel_post_list/intro/direct_channel/index.ts +++ b/app/screens/channel/channel_post_list/intro/direct_channel/index.ts @@ -7,6 +7,7 @@ import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {General} from '@constants'; +import {observeChannelMembers} from '@queries/servers/channel'; import {observeCurrentUserId} from '@queries/servers/system'; import {observeUser} from '@queries/servers/user'; import {getUserIdFromChannelName} from '@utils/user'; @@ -21,7 +22,7 @@ const observeIsBot = (user: UserModel | undefined) => of$(Boolean(user?.isBot)); const enhanced = withObservables([], ({channel, database}: {channel: ChannelModel} & WithDatabaseArgs) => { const currentUserId = observeCurrentUserId(database); - const members = channel.members.observe(); + const members = observeChannelMembers(database, channel.id); let isBot = of$(false); if (channel.type === General.DM_CHANNEL) { diff --git a/app/screens/channel/channel_post_list/intro/direct_channel/member/index.ts b/app/screens/channel/channel_post_list/intro/direct_channel/member/index.ts index 2e1577dcb..9927c73b0 100644 --- a/app/screens/channel/channel_post_list/intro/direct_channel/member/index.ts +++ b/app/screens/channel/channel_post_list/intro/direct_channel/member/index.ts @@ -4,12 +4,15 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; +import {observeUser} from '@queries/servers/user'; + import Member from './member'; +import type {WithDatabaseArgs} from '@typings/database/database'; import type ChannelMembershipModel from '@typings/database/models/servers/channel_membership'; -const enhanced = withObservables([], ({member}: {member: ChannelMembershipModel}) => ({ - user: member.memberUser.observe(), +const enhanced = withObservables([], ({member, database}: {member: ChannelMembershipModel} & WithDatabaseArgs) => ({ + user: observeUser(database, member.userId), })); export default withDatabase(enhanced(Member)); diff --git a/app/screens/channel_info/title/group_message/index.ts b/app/screens/channel_info/title/group_message/index.ts index 0a7db4332..bf5083235 100644 --- a/app/screens/channel_info/title/group_message/index.ts +++ b/app/screens/channel_info/title/group_message/index.ts @@ -6,7 +6,7 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {observeChannel} from '@queries/servers/channel'; +import {observeChannel, observeChannelMembers} from '@queries/servers/channel'; import {observeCurrentUserId} from '@queries/servers/system'; import GroupMessage from './group_message'; @@ -20,7 +20,7 @@ type Props = WithDatabaseArgs & { const enhanced = withObservables(['channelId'], ({channelId, database}: Props) => { const currentUserId = observeCurrentUserId(database); const channel = observeChannel(database, channelId); - const members = channel.pipe(switchMap((c) => (c ? c.members.observe() : of$([])))); + const members = channel.pipe(switchMap((c) => (c ? observeChannelMembers(database, channelId) : of$([])))); return { currentUserId, diff --git a/app/screens/edit_post/index.tsx b/app/screens/edit_post/index.tsx index a740d0451..d18b8b972 100644 --- a/app/screens/edit_post/index.tsx +++ b/app/screens/edit_post/index.tsx @@ -7,6 +7,7 @@ import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {MAX_MESSAGE_LENGTH_FALLBACK} from '@constants/post_draft'; +import {observeFilesForPost} from '@queries/servers/file'; import {observeConfigIntValue} from '@queries/servers/system'; import EditPost from './edit_post'; @@ -17,7 +18,7 @@ import type PostModel from '@typings/database/models/servers/post'; const enhance = withObservables([], ({database, post}: WithDatabaseArgs & { post: PostModel}) => { const maxPostSize = observeConfigIntValue(database, 'MaxPostSize', MAX_MESSAGE_LENGTH_FALLBACK); - const hasFilesAttached = post.files.observe().pipe(switchMap((files) => of$(files?.length > 0))); + const hasFilesAttached = observeFilesForPost(database, post.id).pipe(switchMap((files) => of$(files?.length > 0))); return { maxPostSize, diff --git a/app/screens/gallery/footer/index.ts b/app/screens/gallery/footer/index.ts index 4b64fdab2..a7561289a 100644 --- a/app/screens/gallery/footer/index.ts +++ b/app/screens/gallery/footer/index.ts @@ -43,7 +43,8 @@ const enhanced = withObservables(['item'], ({database, item}: FooterProps) => { const channel = combineLatest([currentChannelId, post]).pipe( switchMap(([cId, p]) => { - return p?.channel.observe() || observeChannel(database, cId); + const id = p?.channelId || cId; + return observeChannel(database, id); }), ); const enablePostUsernameOverride = observeConfigBooleanValue(database, 'EnablePostUsernameOverride'); diff --git a/app/screens/permalink/index.ts b/app/screens/permalink/index.ts index af8c9e8e5..0887e8cd2 100644 --- a/app/screens/permalink/index.ts +++ b/app/screens/permalink/index.ts @@ -6,6 +6,7 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; +import {observeChannel} from '@queries/servers/channel'; import {observePost} from '@queries/servers/post'; import {observeCurrentTeamId} from '@queries/servers/system'; import {queryMyTeamsByIds, queryTeamByName} from '@queries/servers/team'; @@ -32,7 +33,7 @@ const enhance = withObservables([], ({database, postId, teamName}: OwnProps) => return { channel: post.pipe( - switchMap((p) => (p ? p.channel.observe() : of$(undefined))), + switchMap((p) => (p ? observeChannel(database, p.channelId) : of$(undefined))), ), rootId: post.pipe( switchMap((p) => of$(p?.rootId)), diff --git a/app/screens/post_options/index.ts b/app/screens/post_options/index.ts index 3f573e892..bfcbfbff0 100644 --- a/app/screens/post_options/index.ts +++ b/app/screens/post_options/index.ts @@ -10,7 +10,9 @@ import {General, Permissions, Post, Screens} from '@constants'; import {AppBindingLocations} from '@constants/apps'; import {MAX_ALLOWED_REACTIONS} from '@constants/emoji'; import AppsManager from '@managers/apps_manager'; +import {observeChannel} from '@queries/servers/channel'; import {observePost, observePostSaved} from '@queries/servers/post'; +import {observeReactionsForPost} from '@queries/servers/reaction'; import {observePermissionForChannel, observePermissionForPost} from '@queries/servers/role'; import {observeConfigBooleanValue, observeConfigIntValue, observeConfigValue, observeLicense} from '@queries/servers/system'; import {observeIsCRTEnabled, observeThreadById} from '@queries/servers/thread'; @@ -74,7 +76,7 @@ const withPost = withObservables([], ({post, database}: {post: Post | PostModel} }); const enhanced = withObservables([], ({combinedPost, post, showAddReaction, location, database, serverUrl}: EnhancedProps) => { - const channel = post.channel.observe(); + const channel = observeChannel(database, post.channelId); const channelIsArchived = channel.pipe(switchMap((ch: ChannelModel) => of$(ch.deleteAt !== 0))); const currentUser = observeCurrentUser(database); const isLicensed = observeLicense(database).pipe(switchMap((lcs) => of$(lcs?.IsLicensed === 'true'))); @@ -95,7 +97,7 @@ const enhanced = withObservables([], ({combinedPost, post, showAddReaction, loca return of$(c?.name === General.DEFAULT_CHANNEL && (u && !isSystemAdmin(u.roles)) && readOnly); })); - const isUnderMaxAllowedReactions = post.reactions.observe().pipe( + const isUnderMaxAllowedReactions = observeReactionsForPost(database, post.id).pipe( // eslint-disable-next-line max-nested-callbacks switchMap((reactions: ReactionModel[]) => of$(new Set(reactions.map((r) => r.emojiName)).size < MAX_ALLOWED_REACTIONS)), ); diff --git a/app/screens/reactions/index.ts b/app/screens/reactions/index.ts index 0bc13f15c..cac1d9405 100644 --- a/app/screens/reactions/index.ts +++ b/app/screens/reactions/index.ts @@ -7,6 +7,7 @@ import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {observePost} from '@queries/servers/post'; +import {observeReactionsForPost} from '@queries/servers/reaction'; import Reactions from './reactions'; @@ -21,7 +22,7 @@ const enhanced = withObservables([], ({postId, database}: EnhancedProps) => { return { reactions: post.pipe( - switchMap((p) => (p ? p.reactions.observe() : of$(undefined))), + switchMap((p) => (p ? observeReactionsForPost(database, postId) : of$(undefined))), ), }; }); diff --git a/app/screens/thread_options/index.ts b/app/screens/thread_options/index.ts index d92e18629..2720d743d 100644 --- a/app/screens/thread_options/index.ts +++ b/app/screens/thread_options/index.ts @@ -4,7 +4,7 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {observePostSaved} from '@queries/servers/post'; +import {observePost, observePostSaved} from '@queries/servers/post'; import {observeCurrentTeam} from '@queries/servers/team'; import ThreadOptions from './thread_options'; @@ -19,7 +19,7 @@ type Props = WithDatabaseArgs & { const enhanced = withObservables(['thread'], ({database, thread}: Props) => { return { isSaved: observePostSaved(database, thread.id), - post: thread.post.observe(), + post: observePost(database, thread.id), team: observeCurrentTeam(database), }; }); diff --git a/share_extension/components/channel_item/index.ts b/share_extension/components/channel_item/index.ts index 0f5fe4569..7f75eb99c 100644 --- a/share_extension/components/channel_item/index.ts +++ b/share_extension/components/channel_item/index.ts @@ -8,7 +8,7 @@ import {switchMap, distinctUntilChanged} from 'rxjs/operators'; import {General} from '@constants'; import {withServerUrl} from '@context/server'; -import {observeMyChannel} from '@queries/servers/channel'; +import {observeMyChannel, queryChannelMembers} from '@queries/servers/channel'; import {observeCurrentUserId} from '@queries/servers/system'; import {observeTeam} from '@queries/servers/team'; @@ -37,7 +37,7 @@ const enhance = withObservables(['channel', 'showTeamName'], ({channel, showTeam let membersCount = of$(0); if (channel.type === General.GM_CHANNEL) { - membersCount = channel.members.observeCount(false); + membersCount = queryChannelMembers(database, channel.id).observeCount(false); } const hasMember = myChannel.pipe(