diff --git a/app/actions/local/category.ts b/app/actions/local/category.ts index ab06a5a24a..50b52ce3e2 100644 --- a/app/actions/local/category.ts +++ b/app/actions/local/category.ts @@ -4,7 +4,7 @@ import {Model} from '@nozbe/watermelondb'; import DatabaseManager from '@database/manager'; -import {prepareCategories, prepareCategoryChannels, queryCategoriesByTeamIds, queryCategoryById} from '@queries/servers/categories'; +import {prepareCategories, prepareCategoryChannels, queryCategoriesByTeamIds, getCategoryById} from '@queries/servers/categories'; import {pluckUnique} from '@utils/helpers'; export const deleteCategory = async (serverUrl: string, categoryId: string) => { @@ -14,7 +14,7 @@ export const deleteCategory = async (serverUrl: string, categoryId: string) => { } try { - const category = await queryCategoryById(database, categoryId); + const category = await getCategoryById(database, categoryId); if (category) { await database.write(async () => { @@ -56,7 +56,7 @@ export const storeCategories = async (serverUrl: string, categories: CategoryWit // 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); + const localCategories = await queryCategoriesByTeamIds(database, teamIds).fetch(); localCategories. filter((category) => category.type === 'custom'). @@ -92,7 +92,7 @@ export const toggleCollapseCategory = async (serverUrl: string, categoryId: stri } try { - const category = await queryCategoryById(database, categoryId); + const category = await getCategoryById(database, categoryId); if (category) { await database.write(async () => { diff --git a/app/actions/local/channel.test.ts b/app/actions/local/channel.test.ts index 9449a6465d..c74c791021 100644 --- a/app/actions/local/channel.test.ts +++ b/app/actions/local/channel.test.ts @@ -8,9 +8,9 @@ import {Navigation} from '@constants'; import {SYSTEM_IDENTIFIERS} from '@constants/database'; import DatabaseManager from '@database/manager'; import ServerDataOperator from '@database/operator/server_data_operator'; -import {queryMyChannel} from '@queries/servers/channel'; -import {queryCommonSystemValues, queryTeamHistory} from '@queries/servers/system'; -import {queryChannelHistory} from '@queries/servers/team'; +import {getMyChannel} from '@queries/servers/channel'; +import {getCommonSystemValues, getTeamHistory} from '@queries/servers/system'; +import {getTeamChannelHistory} from '@queries/servers/team'; import {dismissAllModalsAndPopToRoot, dismissAllModalsAndPopToScreen} from '@screens/navigation'; import {switchToChannel} from './channel'; @@ -37,10 +37,10 @@ jest.mock('@utils/helpers', () => { }); const queryDatabaseValues = async (database: Database, teamId: string, channelId: string) => ({ - systemValues: await queryCommonSystemValues(database), - teamHistory: await queryTeamHistory(database), - channelHistory: await queryChannelHistory(database, teamId), - member: await queryMyChannel(database, channelId), + systemValues: await getCommonSystemValues(database), + teamHistory: await getTeamHistory(database), + channelHistory: await getTeamChannelHistory(database, teamId), + member: await getMyChannel(database, channelId), }); describe('switchToChannel', () => { diff --git a/app/actions/local/channel.ts b/app/actions/local/channel.ts index ec500bc805..acb7a40960 100644 --- a/app/actions/local/channel.ts +++ b/app/actions/local/channel.ts @@ -1,18 +1,17 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Model, Q} from '@nozbe/watermelondb'; +import {Model} from '@nozbe/watermelondb'; import {DeviceEventEmitter} from 'react-native'; import {General, Navigation as NavigationConstants, Preferences, Screens} from '@constants'; -import {MM_TABLES} from '@constants/database'; import DatabaseManager from '@database/manager'; import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; -import {prepareDeleteChannel, prepareMyChannelsForTeam, queryAllMyChannelIds, queryChannelsById, queryMyChannel} from '@queries/servers/channel'; +import {prepareDeleteChannel, prepareMyChannelsForTeam, queryAllMyChannel, getMyChannel, getChannelById, queryUsersOnChannel} from '@queries/servers/channel'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; -import {prepareCommonSystemValues, PrepareCommonSystemValuesArgs, queryCommonSystemValues, queryCurrentTeamId, setCurrentChannelId} from '@queries/servers/system'; -import {addChannelToTeamHistory, addTeamToTeamHistory, queryTeamById, removeChannelFromTeamHistory} from '@queries/servers/team'; -import {queryCurrentUser} from '@queries/servers/user'; +import {prepareCommonSystemValues, PrepareCommonSystemValuesArgs, getCommonSystemValues, getCurrentTeamId, setCurrentChannelId} from '@queries/servers/system'; +import {addChannelToTeamHistory, addTeamToTeamHistory, getTeamById, removeChannelFromTeamHistory} from '@queries/servers/team'; +import {getCurrentUser} from '@queries/servers/user'; import {dismissAllModalsAndPopToRoot, dismissAllModalsAndPopToScreen} from '@screens/navigation'; import {isTablet} from '@utils/helpers'; import {displayGroupMessageName, displayUsername, getUserIdFromChannelName} from '@utils/user'; @@ -20,8 +19,6 @@ import {displayGroupMessageName, displayUsername, getUserIdFromChannelName} from import type ChannelModel from '@typings/database/models/servers/channel'; import type UserModel from '@typings/database/models/servers/user'; -const {SERVER: {CHANNEL_MEMBERSHIP, USER}} = MM_TABLES; - export const switchToChannel = async (serverUrl: string, channelId: string, teamId?: string, prepareRecordsOnly = false) => { const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; if (!operator) { @@ -33,63 +30,65 @@ export const switchToChannel = async (serverUrl: string, channelId: string, team try { const dt = Date.now(); const isTabletDevice = await isTablet(); - const system = await queryCommonSystemValues(database); - const member = await queryMyChannel(database, channelId); + const system = await getCommonSystemValues(database); + const member = await getMyChannel(database, channelId); if (member) { - const channel: ChannelModel = await member.channel.fetch(); - if (!channel.teamId && teamId) { - const team = await queryTeamById(database, teamId); - if (!team) { - return {error: `team with id ${teamId} not found`}; + const channel = await member.channel.fetch(); + if (channel) { + if (!channel.teamId && teamId) { + const team = await getTeamById(database, teamId); + if (!team) { + return {error: `team with id ${teamId} not found`}; + } } - } - const toTeamId = channel.teamId || teamId || system.currentTeamId; + const toTeamId = channel.teamId || teamId || system.currentTeamId; - if (isTabletDevice && system.currentChannelId !== channelId) { - // On tablet, the channel is being rendered, by setting the channel to empty first we speed up - // the switch by ~3x - await setCurrentChannelId(operator, ''); - } - - if (system.currentTeamId !== toTeamId) { - const history = await addTeamToTeamHistory(operator, toTeamId, true); - models.push(...history); - } - - if ((system.currentTeamId !== toTeamId) || (system.currentChannelId !== channelId)) { - const commonValues: PrepareCommonSystemValuesArgs = { - currentChannelId: system.currentChannelId === channelId ? undefined : channelId, - currentTeamId: system.currentTeamId === toTeamId ? undefined : toTeamId, - }; - const common = await prepareCommonSystemValues(operator, commonValues); - if (common) { - models.push(...common); + if (isTabletDevice && system.currentChannelId !== channelId) { + // On tablet, the channel is being rendered, by setting the channel to empty first we speed up + // the switch by ~3x + await setCurrentChannelId(operator, ''); } - } - if (system.currentChannelId !== channelId || system.currentTeamId !== toTeamId) { - const history = await addChannelToTeamHistory(operator, toTeamId, channelId, true); - models.push(...history); - } + if (system.currentTeamId !== toTeamId) { + const history = await addTeamToTeamHistory(operator, toTeamId, true); + models.push(...history); + } - const {member: viewedAt} = await markChannelAsViewed(serverUrl, channelId, true); - if (viewedAt) { - models.push(viewedAt); - } + if ((system.currentTeamId !== toTeamId) || (system.currentChannelId !== channelId)) { + const commonValues: PrepareCommonSystemValuesArgs = { + currentChannelId: system.currentChannelId === channelId ? undefined : channelId, + currentTeamId: system.currentTeamId === toTeamId ? undefined : toTeamId, + }; + const common = await prepareCommonSystemValues(operator, commonValues); + if (common) { + models.push(...common); + } + } - if (models.length && !prepareRecordsOnly) { - await operator.batchRecords(models); - } + if (system.currentChannelId !== channelId || system.currentTeamId !== toTeamId) { + const history = await addChannelToTeamHistory(operator, toTeamId, channelId, true); + models.push(...history); + } - if (isTabletDevice) { - dismissAllModalsAndPopToRoot(); - DeviceEventEmitter.emit(NavigationConstants.NAVIGATION_HOME); - } else { - dismissAllModalsAndPopToScreen(Screens.CHANNEL, '', undefined, {topBar: {visible: false}}); - } + const {member: viewedAt} = await markChannelAsViewed(serverUrl, channelId, true); + if (viewedAt) { + models.push(viewedAt); + } - console.log('channel switch to', channel?.displayName, channelId, (Date.now() - dt), 'ms'); //eslint-disable-line + if (models.length && !prepareRecordsOnly) { + await operator.batchRecords(models); + } + + if (isTabletDevice) { + dismissAllModalsAndPopToRoot(); + DeviceEventEmitter.emit(NavigationConstants.NAVIGATION_HOME); + } else { + dismissAllModalsAndPopToScreen(Screens.CHANNEL, '', undefined, {topBar: {visible: false}}); + } + + console.log('channel switch to', channel?.displayName, channelId, (Date.now() - dt), 'ms'); //eslint-disable-line + } } } catch (error) { return {error}; @@ -107,13 +106,13 @@ export const removeCurrentUserFromChannel = async (serverUrl: string, channelId: const {operator, database} = serverDatabase; const models: Model[] = []; - const myChannel = await queryMyChannel(database, channelId); + const myChannel = await getMyChannel(database, channelId); if (myChannel) { const channel = await myChannel.channel.fetch() as ChannelModel; models.push(...await prepareDeleteChannel(channel)); let teamId = channel.teamId; if (teamId) { - teamId = await queryCurrentTeamId(database); + teamId = await getCurrentTeamId(database); } const system = await removeChannelFromTeamHistory(operator, teamId, channel.id, true); if (system) { @@ -139,12 +138,11 @@ export const setChannelDeleteAt = async (serverUrl: string, channelId: string, d const {operator, database} = serverDatabase; - const channels = await queryChannelsById(database, [channelId]); - if (!channels?.length) { + const channel = await getChannelById(database, channelId); + if (!channel) { return; } - const channel = channels[0]; const model = channel.prepareUpdate((c) => { c.deleteAt = deleteAt; }); @@ -163,7 +161,7 @@ export const selectAllMyChannelIds = async (serverUrl: string) => { return []; } - return queryAllMyChannelIds(database); + return queryAllMyChannel(database).fetchIds(); }; export const markChannelAsViewed = async (serverUrl: string, channelId: string, prepareRecordsOnly = false) => { @@ -172,7 +170,7 @@ export const markChannelAsViewed = async (serverUrl: string, channelId: string, return {error: `${serverUrl} database not found`}; } - const member = await queryMyChannel(operator.database, channelId); + const member = await getMyChannel(operator.database, channelId); if (!member) { return {error: 'not a member'}; } @@ -202,7 +200,7 @@ export const markChannelAsUnread = async (serverUrl: string, channelId: string, return {error: `${serverUrl} database not found`}; } - const member = await queryMyChannel(operator.database, channelId); + const member = await getMyChannel(operator.database, channelId); if (!member) { return {error: 'not a member'}; } @@ -233,7 +231,7 @@ export const resetMessageCount = async (serverUrl: string, channelId: string) => return {error: `${serverUrl} database not found`}; } - const member = await queryMyChannel(operator.database, channelId); + const member = await getMyChannel(operator.database, channelId); if (!member) { return {error: 'not a member'}; } @@ -287,7 +285,7 @@ export const updateLastPostAt = async (serverUrl: string, channelId: string, las return {error: `${serverUrl} database not found`}; } - const member = await queryMyChannel(operator.database, channelId); + const member = await getMyChannel(operator.database, channelId); if (!member) { return {error: 'not a member'}; } @@ -316,13 +314,13 @@ export async function updateChannelsDisplayName(serverUrl: string, channels: Cha } const {database} = operator; - const currentUser = await queryCurrentUser(database); + const currentUser = await getCurrentUser(database); if (!currentUser) { return {}; } - const {config, license} = await queryCommonSystemValues(database); - const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT); + const {config, license} = await getCommonSystemValues(database); + const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch(); const displaySettings = getTeammateNameDisplaySetting(preferences, config, license); const models: Model[] = []; for await (const channel of channels) { @@ -332,7 +330,7 @@ export async function updateChannelsDisplayName(serverUrl: string, channels: Cha const user = users.find((u) => u.id === otherUserId); newDisplayName = displayUsername(user, currentUser.locale, displaySettings); } else { - const dbProfiles = await database.get(USER).query(Q.on(CHANNEL_MEMBERSHIP, Q.where('channel_id', channel.id))).fetch(); + const dbProfiles = await queryUsersOnChannel(database, channel.id).fetch(); const profileIds = dbProfiles.map((p) => p.id); const gmUsers = users.filter((u) => profileIds.includes(u.id)); if (gmUsers.length) { diff --git a/app/actions/local/draft.ts b/app/actions/local/draft.ts index 83f810e1bf..b02d33f046 100644 --- a/app/actions/local/draft.ts +++ b/app/actions/local/draft.ts @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import DatabaseManager from '@database/manager'; -import {queryDraft} from '@queries/servers/drafts'; +import {getDraft} from '@queries/servers/drafts'; export const updateDraftFile = async (serverUrl: string, channelId: string, rootId: string, file: FileInfo, prepareRecordsOnly = false) => { const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; @@ -10,7 +10,7 @@ export const updateDraftFile = async (serverUrl: string, channelId: string, root return {error: `${serverUrl} database not found`}; } - const draft = await queryDraft(operator.database, channelId, rootId); + const draft = await getDraft(operator.database, channelId, rootId); if (!draft) { return {error: 'no draft'}; } @@ -44,7 +44,7 @@ export const removeDraftFile = async (serverUrl: string, channelId: string, root return {error: `${serverUrl} database not found`}; } - const draft = await queryDraft(operator.database, channelId, rootId); + const draft = await getDraft(operator.database, channelId, rootId); if (!draft) { return {error: 'no draft'}; } @@ -79,7 +79,7 @@ export const updateDraftMessage = async (serverUrl: string, channelId: string, r return {error: `${serverUrl} database not found`}; } - const draft = await queryDraft(operator.database, channelId, rootId); + const draft = await getDraft(operator.database, channelId, rootId); if (!draft) { if (!message) { return {}; @@ -123,7 +123,7 @@ export const addFilesToDraft = async (serverUrl: string, channelId: string, root return {error: `${serverUrl} database not found`}; } - const draft = await queryDraft(operator.database, channelId, rootId); + const draft = await getDraft(operator.database, channelId, rootId); if (!draft) { const newDraft: Draft = { channel_id: channelId, @@ -156,7 +156,7 @@ export const removeDraft = async (serverUrl: string, channelId: string, rootId = return {error: `${serverUrl} database not found`}; } - const draft = await queryDraft(database, channelId, rootId); + const draft = await getDraft(database, channelId, rootId); if (draft) { await database.write(async () => { await draft.destroyPermanently(); diff --git a/app/actions/local/notification.ts b/app/actions/local/notification.ts index f4a1922d79..66518ad22a 100644 --- a/app/actions/local/notification.ts +++ b/app/actions/local/notification.ts @@ -1,11 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {MM_TABLES} from '@constants/database'; import DatabaseManager from '@database/manager'; -import {queryPostsInChannel} from '@queries/servers/post'; - -import type PostModel from '@typings/database/models/servers/post'; +import {getPostById, queryPostsInChannel} from '@queries/servers/post'; export const updatePostSinceCache = async (serverUrl: string, notification: NotificationWithData) => { const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; @@ -16,15 +13,17 @@ export const updatePostSinceCache = async (serverUrl: string, notification: Noti try { if (notification.payload?.channel_id) { const {database} = operator; - const chunks = await queryPostsInChannel(database, notification.payload.channel_id); + const chunks = await queryPostsInChannel(database, notification.payload.channel_id).fetch(); if (chunks.length) { const recent = chunks[0]; - const lastPost = await database.get(MM_TABLES.SERVER.POST).find(notification.payload.post_id); - await operator.database.write(async () => { - await recent.update(() => { - recent.latest = lastPost.createAt; + const lastPost = await getPostById(database, notification.payload.post_id); + if (lastPost) { + await operator.database.write(async () => { + await recent.update(() => { + recent.latest = lastPost.createAt; + }); }); - }); + } } } return {}; diff --git a/app/actions/local/post.ts b/app/actions/local/post.ts index 6fa755e5ff..6c0f71b813 100644 --- a/app/actions/local/post.ts +++ b/app/actions/local/post.ts @@ -4,8 +4,8 @@ import {postActionWithCookie} from '@actions/remote/post'; import {ActionType, Post} from '@constants'; import DatabaseManager from '@database/manager'; -import {prepareDeletePost, queryPostById} from '@queries/servers/post'; -import {queryCurrentUserId} from '@queries/servers/system'; +import {getPostById, prepareDeletePost} from '@queries/servers/post'; +import {getCurrentUserId} from '@queries/servers/system'; import {generateId} from '@utils/general'; import {getPostIdsForCombinedUserActivityPost} from '@utils/post_list'; @@ -66,7 +66,7 @@ export const sendEphemeralPost = async (serverUrl: string, message: string, chan let authorId = userId; if (!authorId) { - authorId = await queryCurrentUserId(operator.database); + authorId = await getCurrentUserId(operator.database); } const timestamp = Date.now(); @@ -110,7 +110,7 @@ export const removePost = async (serverUrl: string, post: PostModel | Post) => { const systemPostIds = getPostIdsForCombinedUserActivityPost(post.id); const removeModels = []; for await (const id of systemPostIds) { - const postModel = await queryPostById(operator.database, id); + const postModel = await getPostById(operator.database, id); if (postModel) { const preparedPost = await prepareDeletePost(postModel); removeModels.push(...preparedPost); @@ -121,7 +121,7 @@ export const removePost = async (serverUrl: string, post: PostModel | Post) => { await operator.batchRecords(removeModels); } } else { - const postModel = await queryPostById(operator.database, post.id); + const postModel = await getPostById(operator.database, post.id); if (postModel) { const preparedPost = await prepareDeletePost(postModel); if (preparedPost.length) { @@ -143,7 +143,7 @@ export const markPostAsDeleted = async (serverUrl: string, post: Post) => { return {error: `${serverUrl} database not found`}; } - const dbPost = await queryPostById(operator.database, post.id); + const dbPost = await getPostById(operator.database, post.id); if (!dbPost) { return {}; } diff --git a/app/actions/local/reactions.ts b/app/actions/local/reactions.ts index 846db4cdba..7cd144bf58 100644 --- a/app/actions/local/reactions.ts +++ b/app/actions/local/reactions.ts @@ -1,11 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {SYSTEM_IDENTIFIERS} from '@constants/database'; import DatabaseManager from '@database/manager'; -import {safeParseJSON} from '@utils/helpers'; - -import type SystemModel from '@typings/database/models/servers/system'; +import {getRecentReactions} from '@queries/servers/system'; const MAXIMUM_RECENT_EMOJI = 27; @@ -14,18 +12,13 @@ export const addRecentReaction = async (serverUrl: string, emojiNames: string[], if (!operator) { return {error: `${serverUrl} database not found`}; } + const {database} = operator; if (!emojiNames.length) { return []; } - let recent: string[] = []; - try { - const emojis = await operator.database.get(MM_TABLES.SERVER.SYSTEM).find(SYSTEM_IDENTIFIERS.RECENT_REACTIONS); - recent.push(...(safeParseJSON(emojis.value) as string[] || [])); - } catch { - // no previous values.. continue - } + let recent = await getRecentReactions(database); try { const recentEmojis = new Set(recent); diff --git a/app/actions/local/team.ts b/app/actions/local/team.ts index ef46c3be81..7e711e4f06 100644 --- a/app/actions/local/team.ts +++ b/app/actions/local/team.ts @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import DatabaseManager from '@database/manager'; -import {prepareDeleteTeam, queryMyTeamById, removeTeamFromTeamHistory} from '@queries/servers/team'; +import {prepareDeleteTeam, getMyTeamById, removeTeamFromTeamHistory} from '@queries/servers/team'; import type TeamModel from '@typings/database/models/servers/team'; @@ -14,7 +14,7 @@ export const removeUserFromTeam = async (serverUrl: string, teamId: string) => { const {operator, database} = serverDatabase; - const myTeam = await queryMyTeamById(database, teamId); + const myTeam = await getMyTeamById(database, teamId); if (myTeam) { const team = await myTeam.team.fetch() as TeamModel; const models = await prepareDeleteTeam(team); diff --git a/app/actions/local/thread.ts b/app/actions/local/thread.ts index 641db6ae46..d4e720518e 100644 --- a/app/actions/local/thread.ts +++ b/app/actions/local/thread.ts @@ -4,9 +4,9 @@ import {General, Screens} from '@constants'; import DatabaseManager from '@database/manager'; import {getTranslations, t} from '@i18n'; -import {queryChannelById} from '@queries/servers/channel'; -import {queryPostById} from '@queries/servers/post'; -import {queryCurrentUser} from '@queries/servers/user'; +import {getChannelById} from '@queries/servers/channel'; +import {getPostById} from '@queries/servers/post'; +import {getCurrentUser} from '@queries/servers/user'; import {goToScreen} from '@screens/navigation'; import EphemeralStore from '@store/ephemeral_store'; import {changeOpacity} from '@utils/theme'; @@ -18,16 +18,16 @@ export const switchToThread = async (serverUrl: string, rootId: string) => { } try { - const user = await queryCurrentUser(database); + const user = await getCurrentUser(database); if (!user) { return {error: 'User not found'}; } - const post = await queryPostById(database, rootId); + const post = await getPostById(database, rootId); if (!post) { return {error: 'Post not found'}; } - const channel = await queryChannelById(database, post.channelId); + const channel = await getChannelById(database, post.channelId); if (!channel) { return {error: 'Channel not found'}; } diff --git a/app/actions/local/timezone.ts b/app/actions/local/timezone.ts index 1609d0c417..f9d4cc58d4 100644 --- a/app/actions/local/timezone.ts +++ b/app/actions/local/timezone.ts @@ -5,7 +5,7 @@ import {getTimeZone} from 'react-native-localize'; import {updateMe} from '@actions/remote/user'; import DatabaseManager from '@database/manager'; -import {queryUserById} from '@queries/servers/user'; +import {getUserById} from '@queries/servers/user'; import type UserModel from '@typings/database/models/servers/user'; @@ -23,7 +23,7 @@ export const autoUpdateTimezone = async (serverUrl: string, {deviceTimezone, use return {error: `No database present for ${serverUrl}`}; } - const currentUser = await queryUserById(database, userId) ?? null; + const currentUser = await getUserById(database, userId); if (!currentUser) { return null; diff --git a/app/actions/local/user.ts b/app/actions/local/user.ts index 9e32cdca9a..8746f72706 100644 --- a/app/actions/local/user.ts +++ b/app/actions/local/user.ts @@ -4,8 +4,8 @@ import {SYSTEM_IDENTIFIERS} from '@constants/database'; import General from '@constants/general'; import DatabaseManager from '@database/manager'; -import {queryRecentCustomStatuses} from '@queries/servers/system'; -import {queryCurrentUser} from '@queries/servers/user'; +import {getRecentCustomStatuses} from '@queries/servers/system'; +import {getCurrentUser} from '@queries/servers/user'; import {addRecentReaction} from './reactions'; @@ -20,7 +20,7 @@ export const setCurrentUserStatusOffline = async (serverUrl: string) => { const {database, operator} = serverDatabase; - const user = await queryCurrentUser(database); + const user = await getCurrentUser(database); if (!user) { return {error: `No current user for ${serverUrl}`}; } @@ -80,8 +80,7 @@ export const updateRecentCustomStatuses = async (serverUrl: string, customStatus return {error: `${serverUrl} database not found`}; } - const recent = await queryRecentCustomStatuses(operator.database); - const recentStatuses = (recent ? recent.value : []) as UserCustomStatus[]; + const recentStatuses = await getRecentCustomStatuses(operator.database); const index = recentStatuses.findIndex((cs) => ( cs.emoji === customStatus.emoji && cs.text === customStatus.text && @@ -115,7 +114,7 @@ export const updateLocalUser = async ( } try { - const user = await queryCurrentUser(database); + const user = await getCurrentUser(database); if (user) { await database.write(async () => { await user.update((userRecord: UserModel) => { diff --git a/app/actions/remote/channel.ts b/app/actions/remote/channel.ts index e7f8e34b4c..cbfa4ec541 100644 --- a/app/actions/remote/channel.ts +++ b/app/actions/remote/channel.ts @@ -12,11 +12,11 @@ import DatabaseManager from '@database/manager'; import {privateChannelJoinPrompt} from '@helpers/api/channel'; import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; import NetworkManager from '@init/network_manager'; -import {prepareMyChannelsForTeam, queryChannelById, queryChannelByName, queryMyChannel} from '@queries/servers/channel'; +import {prepareMyChannelsForTeam, getChannelById, getChannelByName, getMyChannel} from '@queries/servers/channel'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; -import {queryCommonSystemValues, queryCurrentTeamId, queryCurrentUserId} from '@queries/servers/system'; -import {prepareMyTeams, queryNthLastChannelFromTeam, queryMyTeamById, queryTeamById, queryTeamByName} from '@queries/servers/team'; -import {queryCurrentUser} from '@queries/servers/user'; +import {getCommonSystemValues, getCurrentTeamId, getCurrentUserId} from '@queries/servers/system'; +import {prepareMyTeams, getNthLastChannelFromTeam, getMyTeamById, getTeamById, getTeamByName} from '@queries/servers/team'; +import {getCurrentUser} from '@queries/servers/user'; import {getDirectChannelName} from '@utils/channel'; import {PERMALINK_GENERIC_TEAM_NAME_REDIRECT} from '@utils/url'; import {displayGroupMessageName, displayUsername} from '@utils/user'; @@ -121,8 +121,8 @@ export const fetchChannelCreator = async (serverUrl: string, channelId: string, } try { - const currentUserId = await queryCurrentUserId(operator.database); - const channel = await queryChannelById(operator.database, channelId); + const currentUserId = await getCurrentUserId(operator.database); + const channel = await getChannelById(operator.database, channelId); if (channel && channel.creatorId) { const user = await client.getUser(channel.creatorId); @@ -170,7 +170,7 @@ export const fetchChannelStats = async (serverUrl: string, channelId: string, fe try { const stats = await client.getChannelStats(channelId); if (!fetchOnly) { - const channel = await queryChannelById(operator.database, channelId); + const channel = await getChannelById(operator.database, channelId); if (channel) { const channelInfo = await channel.info.fetch() as ChannelInfoModel; const channelInfos: ChannelInfo[] = [{ @@ -303,7 +303,7 @@ export const fetchMissingSidebarInfo = async (serverUrl: string, directChannels: const ownDirectChannel = directChannels.find((dm) => dm.name === getDirectChannelName(currentUserId, currentUserId)); const database = DatabaseManager.serverDatabases[serverUrl]?.database; if (ownDirectChannel && database) { - const currentUser = await queryCurrentUser(database); + const currentUser = await getCurrentUser(database); ownDirectChannel.display_name = displayUsername(currentUser, locale, teammateDisplayNameSetting); } } @@ -339,7 +339,8 @@ export const joinChannel = async (serverUrl: string, userId: string, teamId: str channel = await client.getChannel(channelId); } else if (channelName) { channel = await client.getChannelByName(teamId, channelName, true); - if ([General.GM_CHANNEL, General.DM_CHANNEL].includes(channel.type)) { + const directTypes: string[] = [General.GM_CHANNEL, General.DM_CHANNEL]; + if (directTypes.includes(channel.type)) { member = await client.getChannelMember(channel.id, userId); } else { member = await client.addToChannel(userId, channel.id); @@ -402,13 +403,13 @@ export const switchToChannelByName = async (serverUrl: string, channelName: stri let myTeam: MyTeamModel | TeamMembership | undefined; let name = teamName; const roles: string [] = []; - const system = await queryCommonSystemValues(database); - const currentTeam = await queryTeamById(database, system.currentTeamId); + const system = await getCommonSystemValues(database); + const currentTeam = await getTeamById(database, system.currentTeamId); if (name === PERMALINK_GENERIC_TEAM_NAME_REDIRECT) { name = currentTeam!.name; } else { - team = await queryTeamByName(database, teamName); + team = await getTeamByName(database, teamName); } if (!team) { @@ -422,7 +423,7 @@ export const switchToChannelByName = async (serverUrl: string, channelName: stri } let joinedNewTeam = false; - myTeam = await queryMyTeamById(database, team.id); + myTeam = await getMyTeamById(database, team.id); if (!myTeam) { const added = await addUserToTeam(serverUrl, team.id, system.currentUserId, true); if (added.error) { @@ -457,7 +458,7 @@ export const switchToChannelByName = async (serverUrl: string, channelName: stri return {error: 'Channel is archived'}; } - myChannel = await queryMyChannel(database, channel.id); + myChannel = await getMyChannel(database, channel.id); if (!myChannel) { if (channel.type === General.PRIVATE_CHANNEL) { @@ -564,7 +565,7 @@ export const createDirectChannel = async (serverUrl: string, userId: string, dis } try { - const currentUser = await queryCurrentUser(operator.database); + const currentUser = await getCurrentUser(operator.database); if (!currentUser) { return {error: 'Cannot get the current user'}; } @@ -573,8 +574,8 @@ export const createDirectChannel = async (serverUrl: string, userId: string, dis if (displayName) { created.display_name = displayName; } else { - const preferences = await queryPreferencesByCategoryAndName(operator.database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT); - const system = await queryCommonSystemValues(operator.database); + const preferences = await queryPreferencesByCategoryAndName(operator.database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch(); + const system = await getCommonSystemValues(operator.database); const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], system.config, system.license); const {directChannels} = await fetchMissingSidebarInfo(serverUrl, [created], currentUser.locale, teammateDisplayNameSetting, currentUser.id, true); created.display_name = directChannels?.[0].display_name || created.display_name; @@ -638,9 +639,9 @@ export const makeDirectChannel = async (serverUrl: string, userId: string, displ } try { - const currentUserId = await queryCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(operator.database); const channelName = getDirectChannelName(userId, currentUserId); - let channel: Channel|ChannelModel|undefined = await queryChannelByName(operator.database, channelName); + let channel: Channel|ChannelModel|undefined = await getChannelByName(operator.database, channelName); let result: {data?: Channel|ChannelModel; error?: any}; if (channel) { result = {data: channel}; @@ -689,7 +690,7 @@ export const createGroupChannel = async (serverUrl: string, userIds: string[]) = return {error}; } try { - const currentUser = await queryCurrentUser(operator.database); + const currentUser = await getCurrentUser(operator.database); if (!currentUser) { return {error: 'Cannot get the current user'}; } @@ -701,8 +702,8 @@ export const createGroupChannel = async (serverUrl: string, userIds: string[]) = return {data: created}; } - const preferences = await queryPreferencesByCategoryAndName(operator.database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT); - const system = await queryCommonSystemValues(operator.database); + const preferences = await queryPreferencesByCategoryAndName(operator.database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch(); + const system = await getCommonSystemValues(operator.database); const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], system.config, system.license); const {directChannels} = await fetchMissingSidebarInfo(serverUrl, [created], currentUser.locale, teammateDisplayNameSetting, currentUser.id, true); @@ -764,7 +765,7 @@ export const makeGroupChannel = async (serverUrl: string, userIds: string[], sho } try { - const currentUserId = await queryCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(operator.database); const result = await createGroupChannel(serverUrl, [currentUserId, ...userIds]); const channel = result.data; @@ -822,10 +823,10 @@ export async function getOrCreateDirectChannel(serverUrl: string, otherUserId: s return {error}; } - const currentUserId = await queryCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(operator.database); const channelName = getDirectChannelName(currentUserId, otherUserId); - const channel = await queryChannelByName(operator.database, channelName); + const channel = await getChannelByName(operator.database, channelName); let result; if (channel) { result = {channel}; @@ -884,8 +885,8 @@ export const switchToPenultimateChannel = async (serverUrl: string) => { } try { - const currentTeam = await queryCurrentTeamId(database); - const channelId = await queryNthLastChannelFromTeam(database, currentTeam, 1); + const currentTeam = await getCurrentTeamId(database); + const channelId = await getNthLastChannelFromTeam(database, currentTeam, 1); return switchToChannelById(serverUrl, channelId); } catch (error) { return {error}; @@ -906,7 +907,7 @@ export const searchChannels = async (serverUrl: string, term: string) => { } try { - const currentTeamId = await queryCurrentTeamId(database); + const currentTeamId = await getCurrentTeamId(database); const channels = await client.autocompleteChannels(currentTeamId, term); return {channels}; } catch (error) { diff --git a/app/actions/remote/command.ts b/app/actions/remote/command.ts index 629bcf6aa6..d53011b689 100644 --- a/app/actions/remote/command.ts +++ b/app/actions/remote/command.ts @@ -10,8 +10,8 @@ import {SYSTEM_IDENTIFIERS} from '@constants/database'; import DeepLinkTypes from '@constants/deep_linking'; import DatabaseManager from '@database/manager'; import NetworkManager from '@init/network_manager'; -import {queryChannelsById} from '@queries/servers/channel'; -import {queryConfig, queryCurrentTeamId} from '@queries/servers/system'; +import {getChannelById} from '@queries/servers/channel'; +import {getConfig, getCurrentTeamId} from '@queries/servers/system'; import {queryUsersByUsername} from '@queries/servers/user'; import {showModal} from '@screens/navigation'; import * as DraftUtils from '@utils/draft'; @@ -43,8 +43,8 @@ export const executeCommand = async (serverUrl: string, intl: IntlShape, message // } // } - const channel = (await queryChannelsById(operator.database, [channelId]))?.[0]; - const teamId = channel?.teamId || (await queryCurrentTeamId(operator.database)); + const channel = await getChannelById(operator.database, channelId); + const teamId = channel?.teamId || (await getCurrentTeamId(operator.database)); const args: CommandArgs = { channel_id: channelId, @@ -129,8 +129,8 @@ export const handleGotoLocation = async (serverUrl: string, intl: IntlShape, loc return {error: `${serverUrl} database not found`}; } - const config = await queryConfig(operator.database); - const match = matchDeepLink(location, serverUrl, config.SiteURL); + const config = await getConfig(operator.database); + const match = matchDeepLink(location, serverUrl, config?.SiteURL); if (match) { switch (match.type) { @@ -158,7 +158,7 @@ export const handleGotoLocation = async (serverUrl: string, intl: IntlShape, loc return {error: `${serverUrl} database not found`}; } } - const user = (await queryUsersByUsername(serverDatabase, [data.userName]))?.[0]; + const user = (await queryUsersByUsername(serverDatabase, [data.userName]).fetch())[0]; if (!user) { DraftUtils.errorUnkownUser(intl); return {data: false}; diff --git a/app/actions/remote/custom_emoji.ts b/app/actions/remote/custom_emoji.ts index a139ae71d8..d5754143c8 100644 --- a/app/actions/remote/custom_emoji.ts +++ b/app/actions/remote/custom_emoji.ts @@ -54,7 +54,7 @@ export const searchCustomEmojis = async (serverUrl: string, term: string) => { const data = await client.searchCustomEmoji(term); if (data.length) { const names = data.map((c) => c.name); - const exist = await queryCustomEmojisByName(operator.database, names); + const exist = await queryCustomEmojisByName(operator.database, names).fetch(); const emojis = data.filter((d) => exist.findIndex((c) => c.name === d.name)); if (emojis.length) { await operator.handleCustomEmojis({ diff --git a/app/actions/remote/entry/app.ts b/app/actions/remote/entry/app.ts index 3f0c37b6da..c9236c5a0f 100644 --- a/app/actions/remote/entry/app.ts +++ b/app/actions/remote/entry/app.ts @@ -5,10 +5,10 @@ import {switchToChannelById} from '@actions/remote/channel'; import {fetchRoles} from '@actions/remote/role'; import {fetchConfigAndLicense} from '@actions/remote/systems'; import DatabaseManager from '@database/manager'; -import {queryChannelsById, queryDefaultChannelForTeam} from '@queries/servers/channel'; +import {queryChannelsById, getDefaultChannelForTeam} from '@queries/servers/channel'; import {prepareModels} from '@queries/servers/entry'; -import {prepareCommonSystemValues, queryCommonSystemValues, queryCurrentChannelId, queryCurrentTeamId, queryWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system'; -import {queryCurrentUser} from '@queries/servers/user'; +import {prepareCommonSystemValues, getCommonSystemValues, getCurrentChannelId, getCurrentTeamId, getWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system'; +import {getCurrentUser} from '@queries/servers/user'; import {deleteV1Data} from '@utils/file'; import {isTablet} from '@utils/helpers'; @@ -22,8 +22,8 @@ export const appEntry = async (serverUrl: string, since = 0) => { const {database} = operator; const tabletDevice = await isTablet(); - const currentTeamId = await queryCurrentTeamId(database); - const lastDisconnectedAt = (await queryWebSocketLastDisconnected(database)) || since; + const currentTeamId = await getCurrentTeamId(database); + const lastDisconnectedAt = (await getWebSocketLastDisconnected(database)) || since; const fetchedData = await fetchAppEntryData(serverUrl, lastDisconnectedAt, currentTeamId); const fetchedError = (fetchedData as AppEntryError).error; @@ -35,10 +35,10 @@ export const appEntry = async (serverUrl: string, since = 0) => { const rolesData = await fetchRoles(serverUrl, teamData?.memberships, chData?.memberships, meData?.user, true); if (initialTeamId === currentTeamId) { - let cId = await queryCurrentChannelId(database); + let cId = await getCurrentChannelId(database); if (tabletDevice) { if (!cId) { - const channel = await queryDefaultChannelForTeam(database, initialTeamId); + const channel = await getDefaultChannelForTeam(database, initialTeamId); if (channel) { cId = channel.id; } @@ -51,7 +51,7 @@ export const appEntry = async (serverUrl: string, since = 0) => { // renders the correct team. let channelId = ''; if (tabletDevice) { - const channel = await queryDefaultChannelForTeam(database, initialTeamId); + const channel = await getDefaultChannelForTeam(database, initialTeamId); channelId = channel?.id || ''; } if (channelId) { @@ -65,7 +65,7 @@ export const appEntry = async (serverUrl: string, since = 0) => { let removeChannels; if (removeChannelIds?.length) { - removeChannels = await queryChannelsById(database, removeChannelIds); + removeChannels = await queryChannelsById(database, removeChannelIds).fetch(); } const modelPromises = await prepareModels({operator, initialTeamId, removeTeams, removeChannels, teamData, chData, prefData, meData}); @@ -77,8 +77,8 @@ export const appEntry = async (serverUrl: string, since = 0) => { await operator.batchRecords(models.flat()); } - const {id: currentUserId, locale: currentUserLocale} = meData.user || (await queryCurrentUser(database))!; - const {config, license} = await queryCommonSystemValues(database); + const {id: currentUserId, locale: currentUserLocale} = meData.user || (await getCurrentUser(database))!; + const {config, license} = await getCommonSystemValues(database); deferredAppEntryActions(serverUrl, lastDisconnectedAt, currentUserId, currentUserLocale, prefData.preferences, config, license, teamData, chData, initialTeamId); if (!since) { diff --git a/app/actions/remote/entry/common.ts b/app/actions/remote/entry/common.ts index 44602c7526..3fab20a2f1 100644 --- a/app/actions/remote/entry/common.ts +++ b/app/actions/remote/entry/common.ts @@ -15,8 +15,8 @@ import {DEFAULT_LOCALE} from '@i18n'; import NetworkManager from '@init/network_manager'; import {queryAllServers} from '@queries/app/servers'; import {queryAllChannelsForTeam} from '@queries/servers/channel'; -import {queryConfig} from '@queries/servers/system'; -import {deleteMyTeams, queryAvailableTeamIds, queryMyTeams, queryMyTeamsById, queryTeamsById} from '@queries/servers/team'; +import {getConfig} from '@queries/servers/system'; +import {deleteMyTeams, getAvailableTeamIds, queryMyTeams, queryMyTeamsByIds, queryTeamsById} from '@queries/servers/team'; import type ClientError from '@client/rest/error'; @@ -43,11 +43,11 @@ export const teamsToRemove = async (serverUrl: string, removeTeamIds?: string[]) const {database} = operator; if (removeTeamIds?.length) { // Immediately delete myTeams so that the UI renders only teams the user is a member of. - const removeMyTeams = await queryMyTeamsById(database, removeTeamIds); + const removeMyTeams = await queryMyTeamsByIds(database, removeTeamIds).fetch(); if (removeMyTeams?.length) { await deleteMyTeams(operator, removeMyTeams); const ids = removeMyTeams.map((m) => m.id); - const removeTeams = await queryTeamsById(database, ids); + const removeTeams = await queryTeamsById(database, ids).fetch(); return removeTeams; } } @@ -81,11 +81,11 @@ export const fetchAppEntryData = async (serverUrl: string, since: number, initia if (!initialTeamId && teamData.teams?.length && teamData.memberships?.length) { // If no initial team was set in the database but got teams in the response - const config = await queryConfig(database); + const config = await getConfig(database); const teamOrderPreference = getPreferenceValue(prefData.preferences || [], Preferences.TEAMS_ORDER, '', '') as string; const teamMembers = teamData.memberships.filter((m) => m.delete_at === 0).map((m) => m.team_id); const myTeams = teamData.teams!.filter((t) => teamMembers?.includes(t.id)); - const defaultTeam = selectDefaultTeam(myTeams, meData.user?.locale || DEFAULT_LOCALE, teamOrderPreference, config.ExperimentalPrimaryTeam); + const defaultTeam = selectDefaultTeam(myTeams, meData.user?.locale || DEFAULT_LOCALE, teamOrderPreference, config?.ExperimentalPrimaryTeam); if (defaultTeam?.id) { chData = await fetchMyChannelsForTeam(serverUrl, defaultTeam.id, includeDeletedChannels, since, fetchOnly); } @@ -107,8 +107,8 @@ export const fetchAppEntryData = async (serverUrl: string, since: number, initia if (teamData.teams?.length === 0 && !teamData.error) { // User is no longer a member of any team - const myTeams = await queryMyTeams(database); - removeTeamIds.push(...(myTeams?.map((myTeam) => myTeam.id) || [])); + const myTeams = await queryMyTeams(database).fetch(); + removeTeamIds.push(...(myTeams.map((myTeam) => myTeam.id) || [])); return { ...data, @@ -125,7 +125,7 @@ export const fetchAppEntryData = async (serverUrl: string, since: number, initia removeTeamIds.push(initialTeamId); } - const availableTeamIds = await queryAvailableTeamIds(database, initialTeamId, teamData.teams, prefData.preferences, meData.user?.locale); + const availableTeamIds = await getAvailableTeamIds(database, initialTeamId, teamData.teams, prefData.preferences, meData.user?.locale); const alternateTeamData = await fetchAlternateTeamData(serverUrl, availableTeamIds, removeTeamIds, includeDeletedChannels, since, fetchOnly); data = { @@ -138,7 +138,7 @@ export const fetchAppEntryData = async (serverUrl: string, since: number, initia const removeChannelIds: string[] = []; const fetchedChannelIds = data.chData.channels.map((channel) => channel.id); - const channels = await queryAllChannelsForTeam(database, initialTeamId); + const channels = await queryAllChannelsForTeam(database, initialTeamId).fetch(); for (const channel of channels) { if (!fetchedChannelIds.includes(channel.id)) { removeChannelIds.push(channel.id); diff --git a/app/actions/remote/entry/notification.ts b/app/actions/remote/entry/notification.ts index 743b82280f..f8322d7596 100644 --- a/app/actions/remote/entry/notification.ts +++ b/app/actions/remote/entry/notification.ts @@ -8,11 +8,11 @@ import {markChannelAsRead} from '@actions/remote/channel'; import {fetchRoles} from '@actions/remote/role'; import {Screens} from '@constants'; import DatabaseManager from '@database/manager'; -import {queryChannelsById, queryDefaultChannelForTeam, queryMyChannel} from '@queries/servers/channel'; +import {queryChannelsById, getDefaultChannelForTeam, getMyChannel} from '@queries/servers/channel'; import {prepareModels} from '@queries/servers/entry'; -import {queryCommonSystemValues, queryCurrentTeamId, queryWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system'; -import {queryMyTeamById} from '@queries/servers/team'; -import {queryCurrentUser} from '@queries/servers/user'; +import {getCommonSystemValues, getCurrentTeamId, getWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system'; +import {getMyTeamById} from '@queries/servers/team'; +import {getCurrentUser} from '@queries/servers/user'; import EphemeralStore from '@store/ephemeral_store'; import {isTablet} from '@utils/helpers'; import {emitNotificationError} from '@utils/notification'; @@ -29,8 +29,8 @@ export const pushNotificationEntry = async (serverUrl: string, notification: Not const channelId = notification.payload!.channel_id!; const isTabletDevice = await isTablet(); const {database} = operator; - const currentTeamId = await queryCurrentTeamId(database); - const lastDisconnectedAt = await queryWebSocketLastDisconnected(database); + const currentTeamId = await getCurrentTeamId(database); + const lastDisconnectedAt = await getWebSocketLastDisconnected(database); const currentServerUrl = await DatabaseManager.getActiveServerUrl(); let isDirectChannel = false; @@ -46,8 +46,8 @@ export const pushNotificationEntry = async (serverUrl: string, notification: Not } // To make the switch faster we determine if we already have the team & channel - const myChannel = await queryMyChannel(database, channelId); - const myTeam = await queryMyTeamById(database, teamId); + const myChannel = await getMyChannel(database, channelId); + const myTeam = await getMyTeamById(database, teamId); let switchedToTeamAndChanel = false; if (myChannel && myTeam) { switchedToTeamAndChanel = true; @@ -79,7 +79,7 @@ export const pushNotificationEntry = async (serverUrl: string, notification: Not selectedTeamId = initialTeamId; if (!isDirectChannel) { if (isTabletDevice) { - const channel = await queryDefaultChannelForTeam(operator.database, selectedTeamId); + const channel = await getDefaultChannelForTeam(operator.database, selectedTeamId); selectedChannelId = channel?.id || ''; } else { selectedChannelId = ''; @@ -93,7 +93,7 @@ export const pushNotificationEntry = async (serverUrl: string, notification: Not // renders the correct channel. if (isTabletDevice) { - const channel = await queryDefaultChannelForTeam(operator.database, selectedTeamId); + const channel = await getDefaultChannelForTeam(operator.database, selectedTeamId); selectedChannelId = channel?.id || ''; } else { selectedChannelId = ''; @@ -121,7 +121,7 @@ export const pushNotificationEntry = async (serverUrl: string, notification: Not let removeChannels; if (removeChannelIds?.length) { - removeChannels = await queryChannelsById(operator.database, removeChannelIds); + removeChannels = await queryChannelsById(operator.database, removeChannelIds).fetch(); } const modelPromises = await prepareModels({operator, initialTeamId, removeTeams, removeChannels, teamData, chData, prefData, meData}); @@ -134,8 +134,8 @@ export const pushNotificationEntry = async (serverUrl: string, notification: Not await operator.batchRecords(models.flat() as Model[]); } - const {id: currentUserId, locale: currentUserLocale} = meData.user || (await queryCurrentUser(operator.database))!; - const {config, license} = await queryCommonSystemValues(operator.database); + const {id: currentUserId, locale: currentUserLocale} = meData.user || (await getCurrentUser(operator.database))!; + const {config, license} = await getCommonSystemValues(operator.database); deferredAppEntryActions(serverUrl, lastDisconnectedAt, currentUserId, currentUserLocale, prefData.preferences, config, license, teamData, chData, selectedTeamId, selectedChannelId); syncOtherServers(serverUrl); diff --git a/app/actions/remote/general.ts b/app/actions/remote/general.ts index c40798c189..ac969ed51b 100644 --- a/app/actions/remote/general.ts +++ b/app/actions/remote/general.ts @@ -5,7 +5,7 @@ import {SYSTEM_IDENTIFIERS} from '@constants/database'; import DatabaseManager from '@database/manager'; import {t} from '@i18n'; import NetworkManager from '@init/network_manager'; -import {queryExpandedLinks} from '@queries/servers/system'; +import {getExpandedLinks} from '@queries/servers/system'; import {forceLogoutIfNecessary} from './session'; @@ -69,7 +69,7 @@ export const getRedirectLocation = async (serverUrl: string, link: string) => { try { const expandedLink = await client.getRedirectLocation(link); if (expandedLink?.location) { - const storedLinks = await queryExpandedLinks(operator.database); + const storedLinks = await getExpandedLinks(operator.database); storedLinks[link] = expandedLink.location; const expanded: IdValue = { id: SYSTEM_IDENTIFIERS.EXPANDED_LINKS, diff --git a/app/actions/remote/notifications.ts b/app/actions/remote/notifications.ts index 728c013343..b4d635795c 100644 --- a/app/actions/remote/notifications.ts +++ b/app/actions/remote/notifications.ts @@ -10,11 +10,11 @@ import {fetchMyTeam} from '@actions/remote/team'; import {Preferences} from '@constants'; import DatabaseManager from '@database/manager'; import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; -import {queryChannelsById, queryMyChannel} from '@queries/servers/channel'; +import {getMyChannel, getChannelById} from '@queries/servers/channel'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; -import {queryCommonSystemValues, queryWebSocketLastDisconnected} from '@queries/servers/system'; -import {queryMyTeamById} from '@queries/servers/team'; -import {queryCurrentUser} from '@queries/servers/user'; +import {getCommonSystemValues, getWebSocketLastDisconnected} from '@queries/servers/system'; +import {getMyTeamById} from '@queries/servers/team'; +import {getCurrentUser} from '@queries/servers/user'; import {emitNotificationError} from '@utils/notification'; const fetchNotificationData = async (serverUrl: string, notification: NotificationWithData, skipEvents = false) => { @@ -31,7 +31,7 @@ const fetchNotificationData = async (serverUrl: string, notification: Notificati } const {database} = operator; - const system = await queryCommonSystemValues(database); + const system = await getCommonSystemValues(database); let teamId = notification.payload?.team_id; let isDirectChannel = false; @@ -42,8 +42,8 @@ const fetchNotificationData = async (serverUrl: string, notification: Notificati } // To make the switch faster we determine if we already have the team & channel - const myChannel = await queryMyChannel(database, channelId); - const myTeam = await queryMyTeamById(database, teamId); + const myChannel = await getMyChannel(database, channelId); + const myTeam = await getMyTeamById(database, teamId); if (!myTeam) { const teamsReq = await fetchMyTeam(serverUrl, teamId, false); @@ -68,12 +68,12 @@ const fetchNotificationData = async (serverUrl: string, notification: Notificati } if (isDirectChannel) { - const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT); - const currentUser = await queryCurrentUser(database); + const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch(); + const currentUser = await getCurrentUser(database); const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], system.config, system.license); - const channel = await queryChannelsById(database, [channelId]); - if (channel?.length) { - fetchMissingSidebarInfo(serverUrl, [channel[0].toApi()], currentUser?.locale, teammateDisplayNameSetting, currentUser?.id); + const channel = await getChannelById(database, channelId); + if (channel) { + fetchMissingSidebarInfo(serverUrl, [channel.toApi()], currentUser?.locale, teammateDisplayNameSetting, currentUser?.id); } } } @@ -91,7 +91,7 @@ export const backgroundNotification = async (serverUrl: string, notification: No return; } - const lastDisconnectedAt = await queryWebSocketLastDisconnected(database); + const lastDisconnectedAt = await getWebSocketLastDisconnected(database); if (lastDisconnectedAt) { if (Platform.OS === 'ios') { @@ -113,7 +113,7 @@ export const openNotification = async (serverUrl: string, notification: Notifica await markChannelAsRead(serverUrl, channelId); const {database} = operator; - const system = await queryCommonSystemValues(database); + const system = await getCommonSystemValues(database); const currentServerUrl = await DatabaseManager.getActiveServerUrl(); let teamId = notification.payload?.team_id; @@ -127,8 +127,8 @@ export const openNotification = async (serverUrl: string, notification: Notifica } // To make the switch faster we determine if we already have the team & channel - const myChannel = await queryMyChannel(database, channelId); - const myTeam = await queryMyTeamById(database, teamId); + const myChannel = await getMyChannel(database, channelId); + const myTeam = await getMyTeamById(database, teamId); if (myChannel && myTeam) { switchToChannelById(serverUrl, channelId, teamId); diff --git a/app/actions/remote/permalink.ts b/app/actions/remote/permalink.ts index edbd1ccdea..98b7d72313 100644 --- a/app/actions/remote/permalink.ts +++ b/app/actions/remote/permalink.ts @@ -3,8 +3,8 @@ import {fetchMyChannelsForTeam} from '@actions/remote/channel'; import DatabaseManager from '@database/manager'; -import {queryCommonSystemValues} from '@queries/servers/system'; -import {queryTeamById, queryTeamByName} from '@queries/servers/team'; +import {getCommonSystemValues} from '@queries/servers/system'; +import {getTeamById, getTeamByName} from '@queries/servers/team'; import {permalinkBadTeam} from '@utils/draft'; import {displayPermalink} from '@utils/permalink'; import {PERMALINK_GENERIC_TEAM_NAME_REDIRECT} from '@utils/url'; @@ -21,16 +21,16 @@ export const showPermalink = async (serverUrl: string, teamName: string, postId: try { let name = teamName; let team: TeamModel | undefined; - const system = await queryCommonSystemValues(database); + const system = await getCommonSystemValues(database); if (!name || name === PERMALINK_GENERIC_TEAM_NAME_REDIRECT) { - team = await queryTeamById(database, system.currentTeamId); + team = await getTeamById(database, system.currentTeamId); if (team) { name = team.name; } } if (!team) { - team = await queryTeamByName(database, name); + team = await getTeamByName(database, name); if (!team) { permalinkBadTeam(intl); return {error: 'Bad Permalink team'}; diff --git a/app/actions/remote/post.ts b/app/actions/remote/post.ts index 4fcb737ee7..8f80c543de 100644 --- a/app/actions/remote/post.ts +++ b/app/actions/remote/post.ts @@ -16,10 +16,10 @@ import {filterPostsInOrderedArray} from '@helpers/api/post'; import {getNeededAtMentionedUsernames} from '@helpers/api/user'; import {extractRecordsForTable} from '@helpers/database'; import NetworkManager from '@init/network_manager'; -import {prepareMissingChannelsForAllTeams, queryAllMyChannelIds} from '@queries/servers/channel'; +import {prepareMissingChannelsForAllTeams, queryAllMyChannel} from '@queries/servers/channel'; import {queryAllCustomEmojis} from '@queries/servers/custom_emoji'; -import {queryPostById, queryRecentPostsInChannel} from '@queries/servers/post'; -import {queryCurrentUserId, queryCurrentChannelId} from '@queries/servers/system'; +import {getPostById, getRecentPostsInChannel} from '@queries/servers/post'; +import {getCurrentUserId, getCurrentChannelId} from '@queries/servers/system'; import {queryAllUsers} from '@queries/servers/user'; import {getValidEmojis, matchEmoticons} from '@utils/emoji/helpers'; import {getPostIdsForCombinedUserActivityPost} from '@utils/post_list'; @@ -62,11 +62,11 @@ export const createPost = async (serverUrl: string, post: Partial, files: return {error}; } - const currentUserId = await queryCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(operator.database); const timestamp = Date.now(); const pendingPostId = post.pending_post_id || `${currentUserId}:${timestamp}`; - const existing = await queryPostById(operator.database, pendingPostId); + const existing = await getPostById(operator.database, pendingPostId); if (existing && !existing.props.failed) { return {data: false}; } @@ -111,7 +111,7 @@ export const createPost = async (serverUrl: string, post: Partial, files: initialPostModels.push(...postModels); } - const customEmojis = await queryAllCustomEmojis(operator.database); + const customEmojis = await queryAllCustomEmojis(operator.database).fetch(); const emojisInMessage = matchEmoticons(newPost.message); const reactionModels = await addRecentReaction(serverUrl, getValidEmojis(emojisInMessage, customEmojis), true); if (!('error' in reactionModels) && reactionModels.length) { @@ -164,7 +164,7 @@ export const fetchPostsForCurrentChannel = async (serverUrl: string) => { return {error: `${serverUrl} database not found`}; } - const currentChannelId = await queryCurrentChannelId(database); + const currentChannelId = await getCurrentChannelId(database); return fetchPostsForChannel(serverUrl, currentChannelId); }; @@ -176,7 +176,7 @@ export const fetchPostsForChannel = async (serverUrl: string, channelId: string, let postAction: Promise|undefined; let actionType: string|undefined; - const postsInChannel = await queryRecentPostsInChannel(operator.database, channelId); + const postsInChannel = await getRecentPostsInChannel(operator.database, channelId); if (!postsInChannel || postsInChannel.length < General.POST_CHUNK_SIZE) { postAction = fetchPosts(serverUrl, channelId, 0, General.POST_CHUNK_SIZE, true); actionType = ActionType.POSTS.RECEIVED_IN_CHANNEL; @@ -364,8 +364,8 @@ export const fetchPostAuthors = async (serverUrl: string, posts: Post[], fetchOn return {error}; } - const currentUserId = await queryCurrentUserId(operator.database); - const users = await queryAllUsers(operator.database); + const currentUserId = await getCurrentUserId(operator.database); + const users = await queryAllUsers(operator.database).fetch(); const existingUserIds = new Set(); const existingUserNames = new Set(); let excludeUsername; @@ -552,7 +552,7 @@ export async function fetchMissingChannelsFromPosts(serverUrl: string, posts: Po } try { - const channelIds = await queryAllMyChannelIds(operator.database); + const channelIds = await queryAllMyChannel(operator.database).fetchIds(); const channelPromises: Array> = []; const userPromises: Array> = []; @@ -654,7 +654,7 @@ export const togglePinPost = async (serverUrl: string, postId: string) => { } try { - const post = await queryPostById(database, postId); + const post = await getPostById(database, postId); if (post) { const isPinned = post.isPinned; const request = isPinned ? client.unpinPost : client.pinPost; @@ -716,7 +716,7 @@ export const markPostAsUnread = async (serverUrl: string, postId: string) => { } try { - const [userId, post] = await Promise.all([queryCurrentUserId(database), queryPostById(database, postId)]); + const [userId, post] = await Promise.all([getCurrentUserId(database), getPostById(database, postId)]); if (post && userId) { await client.markPostAsUnread(userId, postId); const {channelId} = post; @@ -756,7 +756,7 @@ export const editPost = async (serverUrl: string, postId: string, postMessage: s } try { - const post = await queryPostById(database, postId); + const post = await getPostById(database, postId); if (post) { const {update_at, edit_at, message: updatedMessage} = await client.patchPost({message: postMessage, id: postId}); await database.write(async () => { @@ -789,7 +789,7 @@ export async function fetchSavedPosts(serverUrl: string, teamId?: string, channe } try { - const userId = await queryCurrentUserId(operator.database); + const userId = await getCurrentUserId(operator.database); const data = await client.getSavedPosts(userId, channelId, teamId, page, perPage); const posts = data.posts || {}; const order = data.order || []; diff --git a/app/actions/remote/preference.ts b/app/actions/remote/preference.ts index 55fa4c5c32..f30d160f9f 100644 --- a/app/actions/remote/preference.ts +++ b/app/actions/remote/preference.ts @@ -5,7 +5,7 @@ import {Preferences} from '@constants'; import DatabaseManager from '@database/manager'; import NetworkManager from '@init/network_manager'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; -import {queryCurrentUserId} from '@queries/servers/system'; +import {getCurrentUserId} from '@queries/servers/system'; import {forceLogoutIfNecessary} from './session'; @@ -53,7 +53,7 @@ export const saveFavoriteChannel = async (serverUrl: string, channelId: string, try { // Todo: @shaz I think you'll need to add the category handler here so that the channel is added/removed from the favorites category - const userId = await queryCurrentUserId(operator.database); + const userId = await getCurrentUserId(operator.database); const favPref: PreferenceType = { category: CATEGORY_FAVORITE_CHANNEL, name: channelId, @@ -73,7 +73,7 @@ export const savePostPreference = async (serverUrl: string, postId: string) => { } try { - const userId = await queryCurrentUserId(operator.database); + const userId = await getCurrentUserId(operator.database); const pref: PreferenceType = { user_id: userId, category: CATEGORY_SAVED_POST, @@ -100,7 +100,7 @@ export const savePreference = async (serverUrl: string, preferences: PreferenceT } try { - const userId = await queryCurrentUserId(operator.database); + const userId = await getCurrentUserId(operator.database); client.savePreferences(userId, preferences); await operator.handlePreferences({ preferences, @@ -126,8 +126,8 @@ export const deleteSavedPost = async (serverUrl: string, postId: string) => { return {error}; } try { - const userId = await queryCurrentUserId(operator.database); - const records = await queryPreferencesByCategoryAndName(operator.database, CATEGORY_SAVED_POST, postId); + const userId = await getCurrentUserId(operator.database); + const records = await queryPreferencesByCategoryAndName(operator.database, CATEGORY_SAVED_POST, postId).fetch(); const postPreferenceRecord = records.find((r) => postId === r.name); const pref = { user_id: userId, diff --git a/app/actions/remote/reactions.ts b/app/actions/remote/reactions.ts index 3444df6ab9..2988b5ba32 100644 --- a/app/actions/remote/reactions.ts +++ b/app/actions/remote/reactions.ts @@ -1,14 +1,14 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Model, Q} from '@nozbe/watermelondb'; +import {Model} from '@nozbe/watermelondb'; import {addRecentReaction} from '@actions/local/reactions'; -import {MM_TABLES} from '@constants/database'; import DatabaseManager from '@database/manager'; import NetworkManager from '@init/network_manager'; -import {queryRecentPostsInChannel, queryRecentPostsInThread} from '@queries/servers/post'; -import {queryCurrentChannelId, queryCurrentUserId} from '@queries/servers/system'; +import {getRecentPostsInChannel, getRecentPostsInThread} from '@queries/servers/post'; +import {queryReaction} from '@queries/servers/reactions'; +import {getCurrentChannelId, getCurrentUserId} from '@queries/servers/system'; import {forceLogoutIfNecessary} from './session'; @@ -29,7 +29,7 @@ export const addReaction = async (serverUrl: string, postId: string, emojiName: } try { - const currentUserId = await queryCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(operator.database); const reaction = await client.addReaction(currentUserId, postId, emojiName); const models: Model[] = []; @@ -73,15 +73,11 @@ export const removeReaction = async (serverUrl: string, postId: string, emojiNam } try { - const currentUserId = await queryCurrentUserId(database); + const currentUserId = await getCurrentUserId(database); await client.removeReaction(currentUserId, postId, emojiName); // should return one or no reaction - const reaction = await database.get(MM_TABLES.SERVER.REACTION).query( - Q.where('emoji_name', emojiName), - Q.where('post_id', postId), - Q.where('user_id', currentUserId), - ).fetch(); + const reaction = await queryReaction(database, emojiName, postId, currentUserId).fetch(); if (reaction.length) { await database.write(async () => { @@ -105,10 +101,10 @@ export const handleReactionToLatestPost = async (serverUrl: string, emojiName: s try { let posts: PostModel[]; if (rootId) { - posts = await queryRecentPostsInThread(operator.database, rootId); + posts = await getRecentPostsInThread(operator.database, rootId); } else { - const channelId = await queryCurrentChannelId(operator.database); - posts = await queryRecentPostsInChannel(operator.database, channelId); + const channelId = await getCurrentChannelId(operator.database); + posts = await getRecentPostsInChannel(operator.database, channelId); } if (add) { diff --git a/app/actions/remote/retry.ts b/app/actions/remote/retry.ts index 350c355007..7a4ee0bd63 100644 --- a/app/actions/remote/retry.ts +++ b/app/actions/remote/retry.ts @@ -8,9 +8,9 @@ import {selectDefaultTeam} from '@helpers/api/team'; import NetworkManager from '@init/network_manager'; import {prepareMyChannelsForTeam} from '@queries/servers/channel'; import {prepareMyPreferences, queryPreferencesByCategoryAndName} from '@queries/servers/preference'; -import {prepareCommonSystemValues, queryCommonSystemValues} from '@queries/servers/system'; +import {prepareCommonSystemValues, getCommonSystemValues} from '@queries/servers/system'; import {prepareMyTeams} from '@queries/servers/team'; -import {queryCurrentUser} from '@queries/servers/user'; +import {getCurrentUser} from '@queries/servers/user'; import {selectDefaultChannelForTeam} from '@utils/channel'; import {fetchMissingSidebarInfo, fetchMyChannelsForTeam, MyChannelsRequest} from './channel'; @@ -38,7 +38,7 @@ export const retryInitialTeamAndChannel = async (serverUrl: string) => { let initialTeam: Team|undefined; let initialChannel: Channel|undefined; - const user = await queryCurrentUser(database); + const user = await getCurrentUser(database); if (!user) { return {error: true}; } @@ -161,19 +161,19 @@ export const retryInitialChannel = async (serverUrl: string, teamId: string) => let initialChannel: Channel|undefined; const rolesToFetch = new Set(); - const user = await queryCurrentUser(database); + const user = await getCurrentUser(database); if (!user) { return {error: true}; } - const prefs = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT); + const prefs = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch(); const preferences: PreferenceType[] = prefs.map((p) => ({ category: p.category, name: p.name, user_id: p.userId, value: p.value, })); - const {config, license} = await queryCommonSystemValues(database); + const {config, license} = await getCommonSystemValues(database); // fetch channels / channel membership for initial team const chData = await fetchMyChannelsForTeam(serverUrl, teamId, false, 0, true); diff --git a/app/actions/remote/role.ts b/app/actions/remote/role.ts index e2b80977ce..3afe06cafd 100644 --- a/app/actions/remote/role.ts +++ b/app/actions/remote/role.ts @@ -26,7 +26,7 @@ export const fetchRolesIfNeeded = async (serverUrl: string, updatedRoles: string const database = DatabaseManager.serverDatabases[serverUrl].database; const operator = DatabaseManager.serverDatabases[serverUrl].operator; - const existingRoles = await queryRoles(database); + const existingRoles = await queryRoles(database).fetch(); const roleNames = existingRoles.map((role) => { return role.name; diff --git a/app/actions/remote/search.ts b/app/actions/remote/search.ts index 16bc9f695a..3923ccab2d 100644 --- a/app/actions/remote/search.ts +++ b/app/actions/remote/search.ts @@ -6,7 +6,7 @@ import {SYSTEM_IDENTIFIERS} from '@constants/database'; import DatabaseManager from '@database/manager'; import NetworkManager from '@init/network_manager'; import {prepareMissingChannelsForAllTeams} from '@queries/servers/channel'; -import {queryCurrentUser} from '@queries/servers/user'; +import {getCurrentUser} from '@queries/servers/user'; import {fetchPostAuthors, fetchMissingChannelsFromPosts} from './post'; import {forceLogoutIfNecessary} from './session'; @@ -39,7 +39,7 @@ export async function fetchRecentMentions(serverUrl: string): Promise { } const {database} = operator; - const {config, license}: { config: Partial; license: Partial } = await queryCommonSystemValues(database); + const {config, license}: { config: Partial; license: Partial } = await getCommonSystemValues(database); if (!Object.keys(config)?.length || !Object.keys(license)?.length) { return null; @@ -68,7 +68,7 @@ export const forceLogoutIfNecessary = async (serverUrl: string, err: ClientError return {error: `${serverUrl} database not found`}; } - const currentUserId = await queryCurrentUserId(database); + const currentUserId = await getCurrentUserId(database); if ('status_code' in err && err.status_code === HTTP_UNAUTHORIZED && err?.url?.indexOf('/login') === -1 && currentUserId) { await logout(serverUrl); diff --git a/app/actions/remote/systems.ts b/app/actions/remote/systems.ts index 77057da433..0b3dada704 100644 --- a/app/actions/remote/systems.ts +++ b/app/actions/remote/systems.ts @@ -9,7 +9,7 @@ import {SYSTEM_IDENTIFIERS} from '@constants/database'; import DatabaseManager from '@database/manager'; import {getServerCredentials} from '@init/credentials'; import NetworkManager from '@init/network_manager'; -import {queryCommonSystemValues} from '@queries/servers/system'; +import {getCommonSystemValues} from '@queries/servers/system'; import type ClientError from '@client/rest/error'; @@ -73,7 +73,7 @@ export const fetchConfigAndLicense = async (serverUrl: string, fetchOnly = false const credentials = await getServerCredentials(serverUrl); const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; if (credentials && operator) { - const current = await queryCommonSystemValues(operator.database); + const current = await getCommonSystemValues(operator.database); const systems: IdValue[] = []; if (!deepEqual(config, current.config)) { systems.push({ diff --git a/app/actions/remote/team.ts b/app/actions/remote/team.ts index 30cc2850ed..4e017563a2 100644 --- a/app/actions/remote/team.ts +++ b/app/actions/remote/team.ts @@ -8,9 +8,9 @@ import {removeUserFromTeam as localRemoveUserFromTeam} from '@actions/local/team import {Events} from '@constants'; import DatabaseManager from '@database/manager'; import NetworkManager from '@init/network_manager'; -import {prepareMyChannelsForTeam, queryDefaultChannelForTeam} from '@queries/servers/channel'; -import {prepareCommonSystemValues, queryCurrentTeamId, queryWebSocketLastDisconnected} from '@queries/servers/system'; -import {addTeamToTeamHistory, prepareDeleteTeam, prepareMyTeams, queryNthLastChannelFromTeam, queryTeamsById, syncTeamTable} from '@queries/servers/team'; +import {prepareMyChannelsForTeam, getDefaultChannelForTeam} from '@queries/servers/channel'; +import {prepareCommonSystemValues, getCurrentTeamId, getWebSocketLastDisconnected} from '@queries/servers/system'; +import {addTeamToTeamHistory, prepareDeleteTeam, prepareMyTeams, getNthLastChannelFromTeam, queryTeamsById, syncTeamTable} from '@queries/servers/team'; import {isTablet} from '@utils/helpers'; import {fetchMyChannelsForTeam, switchToChannelById} from './channel'; @@ -61,7 +61,7 @@ export const addUserToTeam = async (serverUrl: string, teamId: string, userId: s } if (await isTablet()) { - const channel = await queryDefaultChannelForTeam(operator.database, teamId); + const channel = await getDefaultChannelForTeam(operator.database, teamId); if (channel) { fetchPostsForChannel(serverUrl, channel.id); } @@ -104,8 +104,8 @@ export const fetchMyTeams = async (serverUrl: string, fetchOnly = false): Promis if (removeTeamIds.length) { if (removeTeamIds?.length) { // Immediately delete myTeams so that the UI renders only teams the user is a member of. - const removeTeams = await queryTeamsById(operator.database, removeTeamIds); - removeTeams?.forEach((team) => { + const removeTeams = await queryTeamsById(operator.database, removeTeamIds).fetch(); + removeTeams.forEach((team) => { modelPromises.push(prepareDeleteTeam(team)); }); } @@ -268,7 +268,7 @@ export const handleTeamChange = async (serverUrl: string, teamId: string) => { } const {database} = operator; - const currentTeamId = await queryCurrentTeamId(database); + const currentTeamId = await getCurrentTeamId(database); if (currentTeamId === teamId) { return; @@ -276,7 +276,7 @@ export const handleTeamChange = async (serverUrl: string, teamId: string) => { let channelId = ''; if (await isTablet()) { - channelId = await queryNthLastChannelFromTeam(database, teamId); + channelId = await getNthLastChannelFromTeam(database, teamId); if (channelId) { await switchToChannelById(serverUrl, channelId, teamId); return; @@ -298,7 +298,7 @@ export const handleTeamChange = async (serverUrl: string, teamId: string) => { } // If WebSocket is not disconnected we fetch everything since this moment - const lastDisconnectedAt = (await queryWebSocketLastDisconnected(database)) || Date.now(); + const lastDisconnectedAt = (await getWebSocketLastDisconnected(database)) || Date.now(); const {channels, memberships, error} = await fetchMyChannelsForTeam(serverUrl, teamId, true, lastDisconnectedAt, false, true); if (error) { diff --git a/app/actions/remote/user.ts b/app/actions/remote/user.ts index 2aaaf7ef93..31a2ddb35b 100644 --- a/app/actions/remote/user.ts +++ b/app/actions/remote/user.ts @@ -1,26 +1,25 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Model, Q} from '@nozbe/watermelondb'; +import {Model} from '@nozbe/watermelondb'; import {chunk} from 'lodash'; import {updateChannelsDisplayName} from '@actions/local/channel'; import {updateRecentCustomStatuses, updateLocalUser} from '@actions/local/user'; import {fetchRolesIfNeeded} from '@actions/remote/role'; -import {removeUserFromList} from '@app/utils/user'; -import {Database, General} from '@constants'; -import {MM_TABLES} from '@constants/database'; +import {General} from '@constants'; import DatabaseManager from '@database/manager'; import {debounce} from '@helpers/api/general'; import NetworkManager from '@init/network_manager'; -import {queryCurrentTeamId, queryCurrentUserId} from '@queries/servers/system'; -import {prepareUsers, queryAllUsers, queryCurrentUser, queryUsersById, queryUsersByUsername} from '@queries/servers/user'; +import {queryChannelsByTypes} from '@queries/servers/channel'; +import {getCurrentTeamId, getCurrentUserId} from '@queries/servers/system'; +import {getCurrentUser, prepareUsers, queryAllUsers, queryUsersById, queryUsersByUsername} from '@queries/servers/user'; +import {removeUserFromList} from '@utils/user'; import {forceLogoutIfNecessary} from './session'; import type {Client} from '@client/rest'; import type ClientError from '@client/rest/error'; -import type ChannelModel from '@typings/database/models/servers/channel'; import type UserModel from '@typings/database/models/servers/user'; export type MyUserRequest = { @@ -39,8 +38,6 @@ export type ProfilesInChannelRequest = { error?: unknown; } -const {SERVER: {CHANNEL}} = MM_TABLES; - export const fetchMe = async (serverUrl: string, fetchOnly = false): Promise => { let client; try { @@ -224,7 +221,7 @@ export const fetchStatusByIds = async (serverUrl: string, userIds: string[], fet if (!fetchOnly && DatabaseManager.serverDatabases[serverUrl]) { const {database, operator} = DatabaseManager.serverDatabases[serverUrl]; if (operator) { - const users = await database.get(Database.MM_TABLES.SERVER.USER).query(Q.where('id', Q.oneOf(userIds))).fetch() as UserModel[]; + const users = await queryUsersById(database, userIds).fetch(); for (const user of users) { const status = statuses.find((s) => s.user_id === user.id); user.prepareStatus(status?.status || General.OFFLINE); @@ -258,8 +255,8 @@ export const fetchUsersByIds = async (serverUrl: string, userIds: string[], fetc } try { - const currentUser = await queryCurrentUser(operator.database); - const existingUsers = await queryUsersById(operator.database, userIds); + const currentUser = await getCurrentUser(operator.database); + const existingUsers = await queryUsersById(operator.database, userIds).fetch(); if (userIds.includes(currentUser!.id)) { existingUsers.push(currentUser!); } @@ -299,8 +296,8 @@ export const fetchUsersByUsernames = async (serverUrl: string, usernames: string } try { - const currentUser = await queryCurrentUser(operator.database); - const exisingUsers = await queryUsersByUsername(operator.database, usernames); + const currentUser = await getCurrentUser(operator.database); + const exisingUsers = await queryUsersByUsername(operator.database, usernames).fetch(); const usersToLoad = usernames.filter((username) => (username !== currentUser?.username && !exisingUsers.find((u) => u.username === username))); const users = await client.getProfilesByUsernames([...new Set(usersToLoad)]); @@ -335,7 +332,7 @@ export const fetchProfiles = async (serverUrl: string, page = 0, perPage: number const users = await client.getProfiles(page, perPage, options); if (!fetchOnly) { - const currentUserId = await queryCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(operator.database); const toStore = removeUserFromList(currentUserId, users); await operator.handleUsers({ users: toStore, @@ -367,7 +364,7 @@ export const fetchProfilesInTeam = async (serverUrl: string, teamId: string, pag const users = await client.getProfilesInTeam(teamId, page, perPage, sort, options); if (!fetchOnly) { - const currentUserId = await queryCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(operator.database); const toStore = removeUserFromList(currentUserId, users); await operator.handleUsers({ @@ -397,7 +394,7 @@ export const searchProfiles = async (serverUrl: string, term: string, options: a } try { - const currentUserId = await queryCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(operator.database); const users = await client.searchUsers(term, options); if (!fetchOnly) { @@ -462,6 +459,7 @@ export const updateAllUsersSince = async (serverUrl: string, since: number, fetc if (!operator) { return {error: `${serverUrl} database not found`}; } + const database = operator.database; let client: Client; try { @@ -470,9 +468,8 @@ export const updateAllUsersSince = async (serverUrl: string, since: number, fetc return {error}; } - const currentUserId = await queryCurrentUserId(operator.database); - const users = await queryAllUsers(operator.database); - const userIds = users.map((u) => u.id).filter((id) => id !== currentUserId); + const currentUserId = await getCurrentUserId(database); + const userIds = (await queryAllUsers(database).fetchIds()).filter((id) => id !== currentUserId); let userUpdates: UserProfile[] = []; try { userUpdates = await client.getProfilesByIds(userIds, {since}); @@ -480,9 +477,7 @@ export const updateAllUsersSince = async (serverUrl: string, since: number, fetc const modelsToBatch: Model[] = []; const userModels = await operator.handleUsers({users: userUpdates, prepareRecordsOnly: true}); modelsToBatch.push(...userModels); - const directChannels = await operator.database.get(CHANNEL). - query(Q.where('type', Q.oneOf([General.DM_CHANNEL, General.GM_CHANNEL]))). - fetch(); + const directChannels = await queryChannelsByTypes(database, [General.DM_CHANNEL, General.GM_CHANNEL]).fetch(); const {models} = await updateChannelsDisplayName(serverUrl, directChannels, userUpdates, true); if (models?.length) { modelsToBatch.push(...models); @@ -515,10 +510,10 @@ export const updateUsersNoLongerVisible = async (serverUrl: string, prepareRecor const models: Model[] = []; try { const knownUsers = new Set(await client.getKnownUsers()); - const currentUserId = await queryCurrentUserId(serverDatabase.database); + const currentUserId = await getCurrentUserId(serverDatabase.database); knownUsers.add(currentUserId); - const allUsers = await queryAllUsers(serverDatabase.database); + const allUsers = await queryAllUsers(serverDatabase.database).fetch(); for (const user of allUsers) { if (!knownUsers.has(user.id)) { user.prepareDestroyPermanently(); @@ -648,7 +643,7 @@ export const uploadUserProfileImage = async (serverUrl: string, localPath: strin } try { - const currentUser = await queryCurrentUser(database); + const currentUser = await getCurrentUser(database); if (currentUser) { const endpoint = `${client.getUserRoute(currentUser.id)}/image`; @@ -680,7 +675,7 @@ export const searchUsers = async (serverUrl: string, term: string, channelId?: s } try { - const currentTeamId = await queryCurrentTeamId(database); + const currentTeamId = await getCurrentTeamId(database); const users = await client.autocompleteUsers(term, currentTeamId, channelId); return {users}; } catch (error) { diff --git a/app/actions/websocket/category.ts b/app/actions/websocket/category.ts index f98c4c34a2..a666116f0b 100644 --- a/app/actions/websocket/category.ts +++ b/app/actions/websocket/category.ts @@ -92,7 +92,7 @@ export async function handleCategoryOrderUpdatedEvent(serverUrl: string, msg: We // Update category order if (msg.data.order?.length) { const order = msg.data.order; - const categories = await queryCategoriesById(database, order); + const categories = await queryCategoriesById(database, order).fetch(); categories.forEach((c) => { const findOrder = (id: string) => id === c.id; c.prepareUpdate(() => { diff --git a/app/actions/websocket/channel.ts b/app/actions/websocket/channel.ts index c0007d5cb1..7b9729440c 100644 --- a/app/actions/websocket/channel.ts +++ b/app/actions/websocket/channel.ts @@ -11,10 +11,10 @@ import {fetchUsersByIds, updateUsersNoLongerVisible} from '@actions/remote/user' import Events from '@constants/events'; import DatabaseManager from '@database/manager'; import {queryActiveServer} from '@queries/app/servers'; -import {deleteChannelMembership, prepareMyChannelsForTeam, queryChannelsById, queryCurrentChannel} from '@queries/servers/channel'; -import {prepareCommonSystemValues, queryConfig, setCurrentChannelId} from '@queries/servers/system'; -import {queryNthLastChannelFromTeam} from '@queries/servers/team'; -import {queryCurrentUser, queryUserById} from '@queries/servers/user'; +import {deleteChannelMembership, getChannelById, prepareMyChannelsForTeam, getCurrentChannel} from '@queries/servers/channel'; +import {prepareCommonSystemValues, getConfig, setCurrentChannelId} from '@queries/servers/system'; +import {getNthLastChannelFromTeam} from '@queries/servers/team'; +import {getCurrentUser, getUserById} from '@queries/servers/user'; import {dismissAllModals, popToRoot} from '@screens/navigation'; import {isTablet} from '@utils/helpers'; @@ -23,13 +23,13 @@ export async function handleUserAddedToChannelEvent(serverUrl: string, msg: any) if (!database) { return; } - const currentUser = await queryCurrentUser(database.database); + const currentUser = await getCurrentUser(database.database); const {team_id: teamId, user_id: userId} = msg.data; const {channel_id: channelId} = msg.broadcast; const models: Model[] = []; try { - const addedUser = queryUserById(database.database, userId); + const addedUser = getUserById(database.database, userId); if (!addedUser) { // TODO Potential improvement https://mattermost.atlassian.net/browse/MM-40581 const {users} = await fetchUsersByIds(serverUrl, [userId], true); @@ -66,8 +66,8 @@ export async function handleUserAddedToChannelEvent(serverUrl: string, msg: any) models.push(...await database.operator.handleUsers({users: authors, prepareRecordsOnly: true})); } } else { - const channels = await queryChannelsById(database.database, [channelId]); - if (channels?.[0]) { + const channel = await getChannelById(database.database, channelId); + if (channel) { models.push(...await database.operator.handleChannelMembership({ channelMemberships: [{channel_id: channelId, user_id: userId}], prepareRecordsOnly: true, @@ -87,8 +87,8 @@ export async function handleUserRemovedFromChannelEvent(serverUrl: string, msg: return; } - const channel = await queryCurrentChannel(database.database); - const user = await queryCurrentUser(database.database); + const channel = await getCurrentChannel(database.database); + const user = await getCurrentUser(database.database); if (!user) { return; } @@ -121,7 +121,7 @@ export async function handleUserRemovedFromChannelEvent(serverUrl: string, msg: await popToRoot(); if (await isTablet()) { - const channelToJumpTo = await queryNthLastChannelFromTeam(database.database, channel?.teamId); + const channelToJumpTo = await getNthLastChannelFromTeam(database.database, channel?.teamId); if (channelToJumpTo) { const {models: switchChannelModels} = await switchToChannel(serverUrl, channelToJumpTo, '', true); if (switchChannelModels) { @@ -152,15 +152,15 @@ export async function handleChannelDeletedEvent(serverUrl: string, msg: WebSocke return; } - const currentChannel = await queryCurrentChannel(database.database); - const user = await queryCurrentUser(database.database); + const currentChannel = await getCurrentChannel(database.database); + const user = await getCurrentUser(database.database); if (!user) { return; } const {channel_id: channelId, delete_at: deleteAt} = msg.data; - const config = await queryConfig(database.database); + const config = await getConfig(database.database); await setChannelDeleteAt(serverUrl, channelId, deleteAt); @@ -180,7 +180,7 @@ export async function handleChannelDeletedEvent(serverUrl: string, msg: WebSocke await popToRoot(); if (await isTablet()) { - const channelToJumpTo = await queryNthLastChannelFromTeam(database.database, currentChannel?.teamId); + const channelToJumpTo = await getNthLastChannelFromTeam(database.database, currentChannel?.teamId); if (channelToJumpTo) { switchToChannel(serverUrl, channelToJumpTo); } // TODO else jump to "join a channel" screen diff --git a/app/actions/websocket/index.ts b/app/actions/websocket/index.ts index 4f4cb1c83b..7eb89cd64b 100644 --- a/app/actions/websocket/index.ts +++ b/app/actions/websocket/index.ts @@ -12,9 +12,9 @@ import {General, WebsocketEvents} from '@constants'; import {SYSTEM_IDENTIFIERS} from '@constants/database'; import DatabaseManager from '@database/manager'; import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; -import {queryChannelsById, queryDefaultChannelForTeam} from '@queries/servers/channel'; +import {queryChannelsById, getDefaultChannelForTeam} from '@queries/servers/channel'; import {prepareModels} from '@queries/servers/entry'; -import {queryCommonSystemValues, queryConfig, queryCurrentChannelId, queryWebSocketLastDisconnected, resetWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system'; +import {getCommonSystemValues, getConfig, getCurrentChannelId, getWebSocketLastDisconnected, resetWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system'; import {isTablet} from '@utils/helpers'; import {handleCategoryCreatedEvent, handleCategoryDeletedEvent, handleCategoryOrderUpdatedEvent, handleCategoryUpdatedEvent} from './category'; @@ -36,11 +36,11 @@ export async function handleFirstConnect(serverUrl: string) { return; } const {database} = operator; - const config = await queryConfig(database); - const lastDisconnect = await queryWebSocketLastDisconnected(database); + const config = await getConfig(database); + const lastDisconnect = await getWebSocketLastDisconnected(database); // ESR: 5.37 - if (lastDisconnect && config.EnableReliableWebSockets !== 'true' && alreadyConnected.has(serverUrl)) { + if (lastDisconnect && config?.EnableReliableWebSockets !== 'true' && alreadyConnected.has(serverUrl)) { handleReconnect(serverUrl); return; } @@ -78,8 +78,8 @@ async function doReconnect(serverUrl: string) { const {database} = operator; const tabletDevice = await isTablet(); - const system = await queryCommonSystemValues(database); - const lastDisconnectedAt = await queryWebSocketLastDisconnected(database); + const system = await getCommonSystemValues(database); + const lastDisconnectedAt = await getWebSocketLastDisconnected(database); resetWebSocketLastDisconnected(operator); let {config, license} = await fetchConfigAndLicense(serverUrl); @@ -125,7 +125,7 @@ async function doReconnect(serverUrl: string) { let cId = ''; if (tabletDevice) { if (!cId) { - const channel = await queryDefaultChannelForTeam(database, initialTeamId); + const channel = await getDefaultChannelForTeam(database, initialTeamId); if (channel) { cId = channel.id; } @@ -140,7 +140,7 @@ async function doReconnect(serverUrl: string) { let removeChannels; if (removeChannelIds?.length) { - removeChannels = await queryChannelsById(database, removeChannelIds); + removeChannels = await queryChannelsById(database, removeChannelIds).fetch(); } const modelPromises = await prepareModels({operator, initialTeamId, removeTeams, removeChannels, teamData, chData, prefData, meData}); @@ -161,7 +161,7 @@ async function doReconnect(serverUrl: string) { } } - const currentChannelId = await queryCurrentChannelId(database); + const currentChannelId = await getCurrentChannelId(database); if (currentChannelId) { // https://mattermost.atlassian.net/browse/MM-40098 fetchPostsSince(serverUrl, currentChannelId, lastDisconnectedAt); diff --git a/app/actions/websocket/posts.ts b/app/actions/websocket/posts.ts index a4d351dd6a..4a6b1561e4 100644 --- a/app/actions/websocket/posts.ts +++ b/app/actions/websocket/posts.ts @@ -10,9 +10,9 @@ import {fetchMyChannel, markChannelAsRead} from '@actions/remote/channel'; import {fetchPostAuthors, fetchPostById} from '@actions/remote/post'; import {ActionType, Events} from '@constants'; import DatabaseManager from '@database/manager'; -import {queryMyChannel} from '@queries/servers/channel'; -import {queryPostById} from '@queries/servers/post'; -import {queryCurrentChannelId, queryCurrentUserId} from '@queries/servers/system'; +import {getMyChannel} from '@queries/servers/channel'; +import {getPostById} from '@queries/servers/post'; +import {getCurrentChannelId, getCurrentUserId} from '@queries/servers/system'; import {isFromWebhook, isSystemMessage, shouldIgnorePost} from '@utils/post'; import type MyChannelModel from '@typings/database/models/servers/my_channel'; @@ -37,9 +37,9 @@ export async function handleNewPostEvent(serverUrl: string, msg: WebSocketMessag } catch { return; } - const currentUserId = await queryCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(operator.database); - const existing = await queryPostById(operator.database, post.pending_post_id); + const existing = await getPostById(operator.database, post.pending_post_id); if (existing) { return; @@ -59,7 +59,7 @@ export async function handleNewPostEvent(serverUrl: string, msg: WebSocketMessag } // Ensure the channel membership - let myChannel = await queryMyChannel(operator.database, post.channel_id); + let myChannel = await getMyChannel(operator.database, post.channel_id); if (myChannel) { const {member} = await updateLastPostAt(serverUrl, post.channel_id, post.create_at, false); if (member) { @@ -77,7 +77,7 @@ export async function handleNewPostEvent(serverUrl: string, msg: WebSocketMessag return; } - myChannel = await queryMyChannel(operator.database, post.channel_id); + myChannel = await getMyChannel(operator.database, post.channel_id); if (!myChannel) { return; } @@ -85,14 +85,14 @@ export async function handleNewPostEvent(serverUrl: string, msg: WebSocketMessag // If we don't have the root post for this post, fetch it from the server if (post.root_id) { - const rootPost = await queryPostById(operator.database, post.root_id); + const rootPost = await getPostById(operator.database, post.root_id); if (!rootPost) { fetchPostById(serverUrl, post.root_id); } } - const currentChannelId = await queryCurrentChannelId(operator.database); + const currentChannelId = await getCurrentChannelId(operator.database); if (post.channel_id === currentChannelId) { const data = { diff --git a/app/actions/websocket/preferences.ts b/app/actions/websocket/preferences.ts index 7fc5b79bf0..0b30055cca 100644 --- a/app/actions/websocket/preferences.ts +++ b/app/actions/websocket/preferences.ts @@ -4,7 +4,7 @@ import {fetchPostById} from '@actions/remote/post'; import {Preferences} from '@constants'; import DatabaseManager from '@database/manager'; -import {queryPostById} from '@queries/servers/post'; +import {getPostById} from '@queries/servers/post'; import {deletePreferences} from '@queries/servers/preference'; export async function handlePreferenceChangedEvent(serverUrl: string, msg: WebSocketMessage): Promise { @@ -70,7 +70,7 @@ async function handleSavePostAdded(serverUrl: string, preferences: PreferenceTyp const savedPosts = preferences.filter((p) => p.category === Preferences.CATEGORY_SAVED_POST); for await (const saved of savedPosts) { - const post = await queryPostById(database, saved.name); + const post = await getPostById(database, saved.name); if (!post) { await fetchPostById(serverUrl, saved.name, false); } diff --git a/app/actions/websocket/reactions.ts b/app/actions/websocket/reactions.ts index 0922fe9b61..fe3bc8c5c4 100644 --- a/app/actions/websocket/reactions.ts +++ b/app/actions/websocket/reactions.ts @@ -1,10 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; - -import {MM_TABLES} from '@constants/database'; import DatabaseManager from '@database/manager'; +import {queryReaction} from '@queries/servers/reactions'; export async function handleAddCustomEmoji(serverUrl: string, msg: WebSocketMessage): Promise { const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; @@ -52,11 +50,7 @@ export async function handleReactionRemovedFromPostEvent(serverUrl: string, msg: try { const msgReaction = JSON.parse(msg.data.reaction) as Reaction; - const reaction = await database.get(MM_TABLES.SERVER.REACTION).query( - Q.where('emoji_name', msgReaction.emoji_name), - Q.where('post_id', msgReaction.post_id), - Q.where('user_id', msgReaction.user_id), - ).fetch(); + const reaction = await queryReaction(database, msgReaction.emoji_name, msgReaction.post_id, msgReaction.user_id).fetch(); if (reaction.length) { await database.write(async () => { diff --git a/app/actions/websocket/roles.ts b/app/actions/websocket/roles.ts index 24d3b9ca73..3a20a726d8 100644 --- a/app/actions/websocket/roles.ts +++ b/app/actions/websocket/roles.ts @@ -3,9 +3,9 @@ import {fetchRolesIfNeeded} from '@actions/remote/role'; import DatabaseManager from '@database/manager'; -import {queryRoleById} from '@queries/servers/role'; -import {queryCurrentUserId} from '@queries/servers/system'; -import {queryCurrentUser} from '@queries/servers/user'; +import {getRoleById} from '@queries/servers/role'; +import {getCurrentUserId} from '@queries/servers/system'; +import {getCurrentUser} from '@queries/servers/user'; import type {Model} from '@nozbe/watermelondb'; @@ -18,7 +18,7 @@ export async function handleRoleUpdatedEvent(serverUrl: string, msg: WebSocketMe // only update Role records that exist in the Role Table try { const role = JSON.parse(msg.data.role) as Role; - const dbRole = await queryRoleById(operator.database, role.id); + const dbRole = await getRoleById(operator.database, role.id); if (!dbRole) { return; } @@ -38,7 +38,7 @@ export async function handleUserRoleUpdatedEvent(serverUrl: string, msg: WebSock return; } - const currentUserId = await queryCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(operator.database); if (currentUserId !== msg.data.user_id) { return; } @@ -60,7 +60,7 @@ export async function handleUserRoleUpdatedEvent(serverUrl: string, msg: WebSock } // update User Table record - const user = await queryCurrentUser(operator.database); + const user = await getCurrentUser(operator.database); if (user) { user!.prepareUpdate((u) => { u.roles = msg.data.roles; @@ -82,7 +82,7 @@ export async function handleTeamMemberRoleUpdatedEvent(serverUrl: string, msg: W try { const member = JSON.parse(msg.data.member); - const currentUserId = await queryCurrentUserId(operator.database); + const currentUserId = await getCurrentUserId(operator.database); if (currentUserId !== member.user_id) { return; } diff --git a/app/actions/websocket/teams.ts b/app/actions/websocket/teams.ts index 9aefd7d35a..c338ad0de6 100644 --- a/app/actions/websocket/teams.ts +++ b/app/actions/websocket/teams.ts @@ -11,9 +11,9 @@ import {updateUsersNoLongerVisible} from '@actions/remote/user'; import Events from '@constants/events'; import DatabaseManager from '@database/manager'; import {queryActiveServer} from '@queries/app/servers'; -import {queryCurrentTeamId} from '@queries/servers/system'; -import {queryLastTeam, prepareMyTeams} from '@queries/servers/team'; -import {queryCurrentUser} from '@queries/servers/user'; +import {getCurrentTeamId} from '@queries/servers/system'; +import {getLastTeam, prepareMyTeams} from '@queries/servers/team'; +import {getCurrentUser} from '@queries/servers/user'; import {dismissAllModals, popToRoot} from '@screens/navigation'; export async function handleLeaveTeamEvent(serverUrl: string, msg: WebSocketMessage) { @@ -22,8 +22,8 @@ export async function handleLeaveTeamEvent(serverUrl: string, msg: WebSocketMess return; } - const currentTeamId = await queryCurrentTeamId(database.database); - const user = await queryCurrentUser(database.database); + const currentTeamId = await getCurrentTeamId(database.database); + const user = await getCurrentUser(database.database); if (!user) { return; } @@ -46,7 +46,7 @@ export async function handleLeaveTeamEvent(serverUrl: string, msg: WebSocketMess await popToRoot(); } - const teamToJumpTo = await queryLastTeam(database.database); + const teamToJumpTo = await getLastTeam(database.database); if (teamToJumpTo) { handleTeamChange(serverUrl, teamToJumpTo); } // TODO else jump to "join a team" screen diff --git a/app/actions/websocket/users.ts b/app/actions/websocket/users.ts index eca665cc35..0ff5f2f3ae 100644 --- a/app/actions/websocket/users.ts +++ b/app/actions/websocket/users.ts @@ -1,25 +1,21 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Model, Q} from '@nozbe/watermelondb'; +import {Model} from '@nozbe/watermelondb'; import {DeviceEventEmitter} from 'react-native'; import {updateChannelsDisplayName} from '@actions/local/channel'; import {fetchMe, fetchUsersByIds} from '@actions/remote/user'; import {General, Events, Preferences} from '@constants'; -import {MM_TABLES} from '@constants/database'; import DatabaseManager from '@database/manager'; import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; import WebsocketManager from '@init/websocket_manager'; +import {queryChannelsByTypes, queryUserChannelsByTypes} from '@queries/servers/channel'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; -import {queryCommonSystemValues} from '@queries/servers/system'; -import {queryCurrentUser} from '@queries/servers/user'; +import {getCommonSystemValues} from '@queries/servers/system'; +import {getCurrentUser} from '@queries/servers/user'; import {displayUsername} from '@utils/user'; -import type ChannelModel from '@typings/database/models/servers/channel'; - -const {SERVER: {CHANNEL, CHANNEL_MEMBERSHIP}} = MM_TABLES; - export async function handleUserUpdatedEvent(serverUrl: string, msg: WebSocketMessage) { const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; if (!operator) { @@ -27,7 +23,7 @@ export async function handleUserUpdatedEvent(serverUrl: string, msg: WebSocketMe } const {database} = operator; - const currentUser = await queryCurrentUser(database); + const currentUser = await getCurrentUser(database); if (!currentUser) { return; } @@ -51,8 +47,7 @@ export async function handleUserUpdatedEvent(serverUrl: string, msg: WebSocketMe // Update GMs display name if locale has changed if (user.locale !== currentUser.locale) { - const channels = await database.get(CHANNEL).query( - Q.where('type', Q.eq(General.GM_CHANNEL))).fetch(); + const channels = await queryChannelsByTypes(database, [General.GM_CHANNEL]).fetch(); if (channels.length) { const {models} = await updateChannelsDisplayName(serverUrl, channels, [user], true); if (models?.length) { @@ -62,9 +57,7 @@ export async function handleUserUpdatedEvent(serverUrl: string, msg: WebSocketMe } } } else { - const channels = await database.get(CHANNEL).query( - Q.where('type', Q.oneOf([General.DM_CHANNEL, General.GM_CHANNEL])), - Q.on(CHANNEL_MEMBERSHIP, Q.where('user_id', user.id))).fetch(); + const channels = await queryUserChannelsByTypes(database, user.id, [General.DM_CHANNEL, General.GM_CHANNEL]).fetch(); if (channels.length) { const {models} = await updateChannelsDisplayName(serverUrl, channels, [user], true); if (models?.length) { @@ -91,14 +84,14 @@ export async function handleUserTypingEvent(serverUrl: string, msg: WebSocketMes return; } - const {config, license} = await queryCommonSystemValues(database); + const {config, license} = await getCommonSystemValues(database); const {users, existingUsers} = await fetchUsersByIds(serverUrl, [msg.data.user_id]); const user = users?.[0] || existingUsers?.[0]; - const namePreference = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT); + const namePreference = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch(); const teammateDisplayNameSetting = getTeammateNameDisplaySetting(namePreference, config, license); - const currentUser = await queryCurrentUser(database); + const currentUser = await getCurrentUser(database); const username = displayUsername(user, currentUser?.locale, teammateDisplayNameSetting); const data = { channelId: msg.broadcast.channel_id, diff --git a/app/client/websocket/index.ts b/app/client/websocket/index.ts index 27faaacd1f..df6d90df37 100644 --- a/app/client/websocket/index.ts +++ b/app/client/websocket/index.ts @@ -6,7 +6,7 @@ import {Platform} from 'react-native'; import {WebsocketEvents} from '@constants'; import DatabaseManager from '@database/manager'; -import {queryCommonSystemValues} from '@queries/servers/system'; +import {getCommonSystemValues} from '@queries/servers/system'; const MAX_WEBSOCKET_FAILS = 7; const MIN_WEBSOCKET_RETRY_TIME = 3000; // 3 sec @@ -74,7 +74,7 @@ export default class WebSocketClient { return; } - const system = await queryCommonSystemValues(database); + const system = await getCommonSystemValues(database); const connectionUrl = (system.config.WebsocketURL || this.serverUrl) + '/api/v4/websocket'; if (this.connectingCallback) { diff --git a/app/components/autocomplete/at_mention/index.ts b/app/components/autocomplete/at_mention/index.ts index f56b25bf1d..6d9e8c52b2 100644 --- a/app/components/autocomplete/at_mention/index.ts +++ b/app/components/autocomplete/at_mention/index.ts @@ -7,37 +7,30 @@ import {of as of$, from as from$, combineLatest, Observable} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {Permissions} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeChannel} from '@queries/servers/channel'; +import {observeLicense} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {hasPermissionForChannel} from '@utils/role'; import AtMention from './at_mention'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type ChannelModel from '@typings/database/models/servers/channel'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {SYSTEM, USER, CHANNEL}} = MM_TABLES; type OwnProps = {channelId?: string} const enhanced = withObservables([], ({database, channelId}: WithDatabaseArgs & OwnProps) => { - const currentUser = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => of$(value)), - ).pipe( - switchMap((id) => database.get(USER).findAndObserve(id)), - ); + const currentUser = observeCurrentUser(database); - const hasLicense = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.LICENSE).pipe( - switchMap(({value}) => of$(value?.IsLicensed === 'true')), + const hasLicense = observeLicense(database).pipe( + switchMap((lcs) => of$(lcs?.IsLicensed === 'true')), ); let useChannelMentions: Observable; let useGroupMentions: Observable; if (channelId) { - const currentChannel = database.get(CHANNEL).findAndObserve(channelId); - useChannelMentions = combineLatest([currentUser, currentChannel]).pipe(switchMap(([u, c]) => from$(hasPermissionForChannel(c, u, Permissions.USE_CHANNEL_MENTIONS, false)))); + const currentChannel = observeChannel(database, channelId); + useChannelMentions = combineLatest([currentUser, currentChannel]).pipe(switchMap(([u, c]) => (u && c ? from$(hasPermissionForChannel(c, u, Permissions.USE_CHANNEL_MENTIONS, false)) : of$(false)))); useGroupMentions = combineLatest([currentUser, currentChannel, hasLicense]).pipe( - switchMap(([u, c, lcs]) => (lcs ? from$(hasPermissionForChannel(c, u, Permissions.USE_GROUP_MENTIONS, false)) : of$(false))), + switchMap(([u, c, lcs]) => (lcs && u && c ? from$(hasPermissionForChannel(c, u, Permissions.USE_GROUP_MENTIONS, false)) : of$(false))), ); } else { useChannelMentions = of$(false); diff --git a/app/components/autocomplete/at_mention_item/index.ts b/app/components/autocomplete/at_mention_item/index.ts index eed265068a..aa6f06dddc 100644 --- a/app/components/autocomplete/at_mention_item/index.ts +++ b/app/components/autocomplete/at_mention_item/index.ts @@ -6,27 +6,21 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeConfig, observeCurrentUserId} from '@queries/servers/system'; import AtMentionItem from './at_mention_item'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; -const {SERVER: {SYSTEM}} = MM_TABLES; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { - const config = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap(({value}) => of$(value as ClientConfig)), - ); + const config = observeConfig(database); const isCustomStatusEnabled = config.pipe( - switchMap((cfg) => of$(cfg.EnableCustomUserStatuses === 'true')), + switchMap((cfg) => of$(cfg?.EnableCustomUserStatuses === 'true')), ); const showFullName = config.pipe( - switchMap((cfg) => of$(cfg.ShowFullName === 'true')), - ); - const currentUserId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => of$(value)), + switchMap((cfg) => of$(cfg?.ShowFullName === 'true')), ); + const currentUserId = observeCurrentUserId(database); return { isCustomStatusEnabled, showFullName, diff --git a/app/components/autocomplete/channel_mention/index.ts b/app/components/autocomplete/channel_mention/index.ts index 42fe679107..08e83bc99f 100644 --- a/app/components/autocomplete/channel_mention/index.ts +++ b/app/components/autocomplete/channel_mention/index.ts @@ -4,17 +4,15 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {MM_TABLES} from '@constants/database'; +import {queryAllMyChannel} from '@queries/servers/channel'; import ChannelMention from './channel_mention'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type MyChannelModel from '@typings/database/models/servers/my_channel'; -const {SERVER: {MY_CHANNEL}} = MM_TABLES; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { return { - myMembers: database.get(MY_CHANNEL).query().observe(), + myMembers: queryAllMyChannel(database).observe(), }; }); diff --git a/app/components/autocomplete/channel_mention_item/channel_mention_item.tsx b/app/components/autocomplete/channel_mention_item/channel_mention_item.tsx index aa0a741306..cc5c597f89 100644 --- a/app/components/autocomplete/channel_mention_item/channel_mention_item.tsx +++ b/app/components/autocomplete/channel_mention_item/channel_mention_item.tsx @@ -5,7 +5,7 @@ import React, {useMemo} from 'react'; import {Text, View} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; -import ChannelIcon from '@app/components/channel_icon'; +import ChannelIcon from '@components/channel_icon'; import {BotTag, GuestTag} from '@components/tag'; import TouchableWithFeedback from '@components/touchable_with_feedback'; import {General} from '@constants'; diff --git a/app/components/autocomplete/channel_mention_item/index.ts b/app/components/autocomplete/channel_mention_item/index.ts index 8231897d2a..6a8063b3a5 100644 --- a/app/components/autocomplete/channel_mention_item/index.ts +++ b/app/components/autocomplete/channel_mention_item/index.ts @@ -7,7 +7,7 @@ import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {General} from '@constants'; -import {MM_TABLES} from '@constants/database'; +import {observeUser} from '@queries/servers/user'; import ChannelMentionItem from './channel_mention_item'; @@ -18,11 +18,10 @@ type OwnProps = { channel: Channel; } -const {SERVER: {USER}} = MM_TABLES; const enhanced = withObservables([], ({database, channel}: WithDatabaseArgs & OwnProps) => { let user = of$(undefined); if (channel.type === General.DM_CHANNEL) { - user = database.get(USER).findAndObserve(channel.teammate_id!); + user = observeUser(database, channel.teammate_id!); } const isBot = user.pipe(switchMap((u) => of$(u ? u.isBot : false))); diff --git a/app/components/autocomplete/emoji_suggestion/index.ts b/app/components/autocomplete/emoji_suggestion/index.ts index a800d4e4e6..bfbed429e9 100644 --- a/app/components/autocomplete/emoji_suggestion/index.ts +++ b/app/components/autocomplete/emoji_suggestion/index.ts @@ -1,40 +1,32 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {queryAllCustomEmojis} from '@queries/servers/custom_emoji'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {observeConfigBooleanValue} from '@queries/servers/system'; import EmojiSuggestion from './emoji_suggestion'; import type {WithDatabaseArgs} from '@typings/database/database'; import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; -const {SERVER: {SYSTEM, CUSTOM_EMOJI, PREFERENCE}} = MM_TABLES; const emptyEmojiList: CustomEmojiModel[] = []; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { - const isCustomEmojisEnabled = database.get(SYSTEM). - findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap((config) => of$(config.value.EnableCustomEmoji === 'true')), - ); + const isCustomEmojisEnabled = observeConfigBooleanValue(database, 'EnableCustomEmoji'); return { customEmojis: isCustomEmojisEnabled.pipe( switchMap((enabled) => (enabled ? - database.get(CUSTOM_EMOJI).query().observe() : + queryAllCustomEmojis(database).observe() : of$(emptyEmojiList)), ), ), - skinTone: database.get(PREFERENCE).query( - Q.where('category', Preferences.CATEGORY_EMOJI), - Q.where('name', Preferences.EMOJI_SKINTONE), - ).observe().pipe( + skinTone: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_EMOJI, Preferences.EMOJI_SKINTONE).observe().pipe( switchMap((prefs) => of$(prefs?.[0]?.value ?? 'default')), ), }; diff --git a/app/components/autocomplete/index.ts b/app/components/autocomplete/index.ts index ab5224b7a8..4513679be4 100644 --- a/app/components/autocomplete/index.ts +++ b/app/components/autocomplete/index.ts @@ -3,20 +3,15 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeConfigBooleanValue} from '@queries/servers/system'; import Autocomplete from './autocomplete'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({ - isAppsEnabled: database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap((cfg) => of$(cfg.value.FeatureFlagAppsEnabled === 'true')), - ), + isAppsEnabled: observeConfigBooleanValue(database, 'FeatureFlagAppsEnabled'), })); export default withDatabase(enhanced(Autocomplete)); diff --git a/app/components/autocomplete_selector/index.tsx b/app/components/autocomplete_selector/index.tsx index a4c0a178e8..9dd6d19056 100644 --- a/app/components/autocomplete_selector/index.tsx +++ b/app/components/autocomplete_selector/index.tsx @@ -1,30 +1,24 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import React, {ReactNode, useCallback, useState} from 'react'; import {useIntl} from 'react-intl'; import {Text, View} from 'react-native'; -import {combineLatest} from 'rxjs'; -import {map} from 'rxjs/operators'; import CompasIcon from '@components/compass_icon'; import FormattedText from '@components/formatted_text'; import TouchableWithFeedback from '@components/touchable_with_feedback'; -import {Preferences, Screens, View as ViewConstants} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {Screens, View as ViewConstants} from '@constants'; import {useTheme} from '@context/theme'; -import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; +import {observeTeammateNameDisplay} from '@queries/servers/user'; import {goToScreen} from '@screens/navigation'; import {preventDoubleTap} from '@utils/tap'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {displayUsername} from '@utils/user'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; type AutoCompleteSelectorProps = { dataSource?: string; @@ -43,8 +37,6 @@ type AutoCompleteSelectorProps = { teammateNameDisplay: string; } -const {SERVER: {PREFERENCE, SYSTEM}} = MM_TABLES; - const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { const input = { borderWidth: 1, @@ -240,16 +232,8 @@ const AutoCompleteSelector = ({ }; const withTeammateNameDisplay = withObservables([], ({database}: WithDatabaseArgs) => { - const config = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG); - const license = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.LICENSE); - const preferences = database.get(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS)).observe(); - const teammateNameDisplay = combineLatest([config, license, preferences]).pipe( - map( - ([{value: cfg}, {value: lcs}, prefs]) => getTeammateNameDisplaySetting(prefs, cfg, lcs), - ), - ); return { - teammateNameDisplay, + teammateNameDisplay: observeTeammateNameDisplay(database), }; }); diff --git a/app/components/channel_icon/dm_avatar/index.ts b/app/components/channel_icon/dm_avatar/index.ts index 0f2359f441..ba43c85e8d 100644 --- a/app/components/channel_icon/dm_avatar/index.ts +++ b/app/components/channel_icon/dm_avatar/index.ts @@ -1,28 +1,21 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeCurrentUserId} from '@queries/servers/system'; +import {observeUser} from '@queries/servers/user'; import {getUserIdFromChannelName} from '@utils/user'; import DmAvatar from './dm_avatar'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {USER, SYSTEM}} = MM_TABLES; -const {CURRENT_USER_ID} = SYSTEM_IDENTIFIERS; const enhance = withObservables(['channelName'], ({channelName, database}: {channelName: string} & WithDatabaseArgs) => { - const currentUserId = database.get(SYSTEM).findAndObserve(CURRENT_USER_ID).pipe( - switchMap(({value}) => of$(value)), - ); + const currentUserId = observeCurrentUserId(database); const authorId = currentUserId.pipe( switchMap((userId) => of$(getUserIdFromChannelName(userId, channelName))), @@ -30,10 +23,7 @@ const enhance = withObservables(['channelName'], ({channelName, database}: {chan const author = authorId.pipe( switchMap((id) => { - return database.get(USER).query(Q.where('id', id)).observe().pipe( - // eslint-disable-next-line max-nested-callbacks - switchMap((users) => (users[0] ? of$(users[0]) : of$(undefined))), - ); + return observeUser(database, id); }), ); diff --git a/app/components/channel_list/categories/body/channel/index.ts b/app/components/channel_list/categories/body/channel/index.ts index 9e0ca27175..bfc4323d48 100644 --- a/app/components/channel_list/categories/body/channel/index.ts +++ b/app/components/channel_list/categories/body/channel/index.ts @@ -7,28 +7,22 @@ import {combineLatest, of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {General} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeMyChannel} from '@queries/servers/channel'; +import {observeCurrentUserId} from '@queries/servers/system'; import {getUserIdFromChannelName} from '@utils/user'; import ChannelListItem from './channel_list_item'; import type {WithDatabaseArgs} from '@typings/database/database'; import type ChannelModel from '@typings/database/models/servers/channel'; -import type MyChannelModel from '@typings/database/models/servers/my_channel'; import type MyChannelSettingsModel from '@typings/database/models/servers/my_channel_settings'; -import type SystemModel from '@typings/database/models/servers/system'; - -const {SERVER: {MY_CHANNEL, SYSTEM}} = MM_TABLES; -const {CURRENT_USER_ID} = SYSTEM_IDENTIFIERS; const enhance = withObservables(['channelId'], ({channelId, database}: {channelId: string} & WithDatabaseArgs) => { - const myChannel = database.get(MY_CHANNEL).findAndObserve(channelId); - const currentUserId = database.get(SYSTEM).findAndObserve(CURRENT_USER_ID).pipe( - switchMap(({value}) => of$(value)), - ); + const myChannel = observeMyChannel(database, channelId); + const currentUserId = observeCurrentUserId(database); - const channel = myChannel.pipe(switchMap((my) => my.channel.observe())); - const settings = channel.pipe(switchMap((c) => c.settings.observe())); + const channel = myChannel.pipe(switchMap((my) => (my ? my.channel.observe() : of$(undefined)))); + const settings = channel.pipe(switchMap((c) => (c ? c.settings.observe() : of$(undefined)))); const isOwnDirectMessage = combineLatest([currentUserId, channel]).pipe( switchMap(([userId, ch]) => { diff --git a/app/components/channel_list/categories/body/index.ts b/app/components/channel_list/categories/body/index.ts index 9acf7d37ec..4debf398ad 100644 --- a/app/components/channel_list/categories/body/index.ts +++ b/app/components/channel_list/categories/body/index.ts @@ -1,14 +1,15 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Database, Q} from '@nozbe/watermelondb'; +import {Database} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {combineLatest, of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES} from '@app/constants/database'; import {General, Preferences} from '@constants'; +import {queryMyChannelSettingsByIds} from '@queries/servers/channel'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import {WithDatabaseArgs} from '@typings/database/database'; import CategoryBody from './category_body'; @@ -16,14 +17,11 @@ import CategoryBody from './category_body'; import type CategoryModel from '@typings/database/models/servers/category'; import type ChannelModel from '@typings/database/models/servers/channel'; import type MyChannelSettingsModel from '@typings/database/models/servers/my_channel_settings'; -import type PreferenceModel from '@typings/database/models/servers/preference'; type ChannelData = Pick & { isMuted: boolean; }; -const {SERVER: {MY_CHANNEL_SETTINGS, PREFERENCE}} = MM_TABLES; - const sortAlpha = (locale: string, a: ChannelData, b: ChannelData) => { if (a.isMuted && !b.isMuted) { return 1; @@ -49,12 +47,9 @@ const buildAlphaData = (channels: ChannelModel[], settings: MyChannelSettingsMod return of$(combined.map((c) => c.id)); }; -const querySettings = (database: Database, channels: ChannelModel[]) => { +const observeSettings = (database: Database, channels: ChannelModel[]) => { const ids = channels.map((c) => c.id); - return database.get(MY_CHANNEL_SETTINGS). - query( - Q.where('id', Q.oneOf(ids)), - ).observeWithColumns(['notify_props']); + return queryMyChannelSettingsByIds(database, ids).observeWithColumns(['notify_props']); }; const getSortedIds = (database: Database, category: CategoryModel, locale: string) => { @@ -62,7 +57,7 @@ const getSortedIds = (database: Database, category: CategoryModel, locale: strin case 'alpha': { const channels = category.channels.observeWithColumns(['display_name']); const settings = channels.pipe( - switchMap((cs) => querySettings(database, cs)), + switchMap((cs) => observeSettings(database, cs)), ); return combineLatest([channels, settings]).pipe( switchMap(([cs, st]) => buildAlphaData(cs, st, locale)), @@ -90,21 +85,11 @@ const enhance = withObservables(['category'], ({category, locale, database}: {ca let limit = of$(0); if (category.type === 'direct_messages') { - limit = database.get(PREFERENCE). - query( - Q.where('category', Preferences.CATEGORY_SIDEBAR_SETTINGS), - Q.where('name', 'limit_visible_dms_gms'), - ).observe().pipe( - switchMap( - (val) => { - if (val[0]) { - return of$(parseInt(val[0].value, 10)); - } - - return of$(0); - }, - ), - ); + limit = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, 'limit_visible_dms_gms').observe().pipe( + switchMap((val) => { + return val[0] ? of$(parseInt(val[0].value, 10)) : of$(0); + }), + ); } return { diff --git a/app/components/channel_list/categories/header/header.tsx b/app/components/channel_list/categories/header/header.tsx index 3c376e55d7..e502680c64 100644 --- a/app/components/channel_list/categories/header/header.tsx +++ b/app/components/channel_list/categories/header/header.tsx @@ -6,8 +6,8 @@ import {Text, TouchableOpacity, View} from 'react-native'; import Animated, {Easing, useAnimatedStyle, useDerivedValue, useSharedValue, withTiming} from 'react-native-reanimated'; import {toggleCollapseCategory} from '@actions/local/category'; -import CompassIcon from '@app/components/compass_icon'; -import {useServerUrl} from '@app/context/server'; +import CompassIcon from '@components/compass_icon'; +import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; diff --git a/app/components/channel_list/categories/index.ts b/app/components/channel_list/categories/index.ts index cf60109651..3029ba1234 100644 --- a/app/components/channel_list/categories/index.ts +++ b/app/components/channel_list/categories/index.ts @@ -1,34 +1,23 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {queryCategoriesByTeamIds} from '@queries/servers/categories'; +import {observeCurrentChannelId} from '@queries/servers/system'; import Categories from './categories'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type CategoryModel from '@typings/database/models/servers/category'; -import type SystemModel from '@typings/database/models/servers/system'; - -const {SERVER: {CATEGORY, SYSTEM}} = MM_TABLES; -const {CURRENT_CHANNEL_ID} = SYSTEM_IDENTIFIERS; type WithDatabaseProps = {currentTeamId: string } & WithDatabaseArgs const enhanced = withObservables( ['currentTeamId'], ({currentTeamId, database}: WithDatabaseProps) => { - const currentChannelId = database.get(SYSTEM).findAndObserve(CURRENT_CHANNEL_ID).pipe( - switchMap(({value}) => of$(value)), - ); - const categories = database.get(CATEGORY).query( - Q.where('team_id', currentTeamId), - ).observeWithColumns(['sort_order']); + const currentChannelId = observeCurrentChannelId(database); + const categories = queryCategoriesByTeamIds(database, [currentTeamId]).observeWithColumns(['sort_order']); return { currentChannelId, diff --git a/app/components/channel_list/header/index.ts b/app/components/channel_list/header/index.ts index 2e55756f26..9a11dc7482 100644 --- a/app/components/channel_list/header/index.ts +++ b/app/components/channel_list/header/index.ts @@ -3,43 +3,33 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {catchError, combineLatest, of as of$, from as from$} from 'rxjs'; +import {combineLatest, of as of$, from as from$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {Permissions} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeCurrentTeam} from '@queries/servers/team'; +import {observeCurrentUser} from '@queries/servers/user'; import {hasPermissionForTeam} from '@utils/role'; import ChannelListHeader from './header'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; -import type TeamModel from '@typings/database/models/servers/team'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {SYSTEM, TEAM, USER}} = MM_TABLES; -const {CURRENT_TEAM_ID, CURRENT_USER_ID} = SYSTEM_IDENTIFIERS; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { - const team = database.get(SYSTEM).findAndObserve(CURRENT_TEAM_ID).pipe( - switchMap((id) => database.get(TEAM).findAndObserve(id.value)), - catchError(() => of$({displayName: ''})), - ); + const team = observeCurrentTeam(database); - const currentUser = database.get(SYSTEM).findAndObserve(CURRENT_USER_ID).pipe( - switchMap(({value}) => database.get(USER).findAndObserve(value)), - ); + const currentUser = observeCurrentUser(database); const canJoinChannels = combineLatest([currentUser, team]).pipe( - switchMap(([u, t]) => (('id' in t) ? from$(hasPermissionForTeam(t, u, Permissions.JOIN_PUBLIC_CHANNELS, true)) : of$(false))), + switchMap(([u, t]) => (t && u ? from$(hasPermissionForTeam(t, u, Permissions.JOIN_PUBLIC_CHANNELS, true)) : of$(false))), ); const canCreatePublicChannels = combineLatest([currentUser, team]).pipe( - switchMap(([u, t]) => (('id' in t) ? from$(hasPermissionForTeam(t, u, Permissions.CREATE_PUBLIC_CHANNEL, true)) : of$(false))), + switchMap(([u, t]) => (t && u ? from$(hasPermissionForTeam(t, u, Permissions.CREATE_PUBLIC_CHANNEL, true)) : of$(false))), ); const canCreatePrivateChannels = combineLatest([currentUser, team]).pipe( - switchMap(([u, t]) => (('id' in t) ? from$(hasPermissionForTeam(t, u, Permissions.CREATE_PRIVATE_CHANNEL, false)) : of$(false))), + switchMap(([u, t]) => (t && u ? from$(hasPermissionForTeam(t, u, Permissions.CREATE_PRIVATE_CHANNEL, false)) : of$(false))), ); const canCreateChannels = combineLatest([canCreatePublicChannels, canCreatePrivateChannels]).pipe( @@ -50,7 +40,7 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { canCreateChannels, canJoinChannels, displayName: team.pipe( - switchMap((t) => of$(t.displayName)), + switchMap((t) => of$(t?.displayName)), ), }; }); diff --git a/app/components/channel_list/index.test.tsx b/app/components/channel_list/index.test.tsx index 6533469c64..151f58a600 100644 --- a/app/components/channel_list/index.test.tsx +++ b/app/components/channel_list/index.test.tsx @@ -5,8 +5,7 @@ import Database from '@nozbe/watermelondb/Database'; import React from 'react'; import {SafeAreaProvider} from 'react-native-safe-area-context'; -import {MM_TABLES} from '@constants/database'; -import {TeamModel} from '@database/models/server'; +import {getTeamById} from '@queries/servers/team'; import {renderWithEverything} from '@test/intl-test-helper'; import TestHelper from '@test/test_helper'; @@ -18,7 +17,7 @@ describe('components/channel_list', () => { const server = await TestHelper.setupServerDatabase(); database = server.database; - const team = await database.get(MM_TABLES.SERVER.TEAM).find(TestHelper.basicTeam!.id); + const team = await getTeamById(database, TestHelper.basicTeam!.id); await database.write(async () => { await team?.update(() => { team.displayName = 'Test Team!'; diff --git a/app/components/channel_list/load_channels_error/index.ts b/app/components/channel_list/load_channels_error/index.ts index 6f99af8101..5339f103be 100644 --- a/app/components/channel_list/load_channels_error/index.ts +++ b/app/components/channel_list/load_channels_error/index.ts @@ -6,20 +6,18 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES} from '@constants/database'; +import {observeTeam} from '@queries/servers/team'; -const {SERVER: {TEAM}} = MM_TABLES; import LoadChannelsError from './load_channel_error'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type TeamModel from '@typings/database/models/servers/team'; const enhanced = withObservables(['teamId'], ({database, teamId}: {teamId: string} & WithDatabaseArgs) => { - const team = database.get(TEAM).findAndObserve(teamId); + const team = observeTeam(database, teamId); return { teamDisplayName: team.pipe( - switchMap((t) => of$(t.displayName)), + switchMap((t) => of$(t?.displayName)), ), }; }); diff --git a/app/components/custom_status/custom_status_expiry.tsx b/app/components/custom_status/custom_status_expiry.tsx index 8b430dae37..f99ad9f081 100644 --- a/app/components/custom_status/custom_status_expiry.tsx +++ b/app/components/custom_status/custom_status_expiry.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import moment, {Moment} from 'moment-timezone'; @@ -15,13 +14,12 @@ import FormattedDate from '@components/formatted_date'; import FormattedText from '@components/formatted_text'; import FormattedTime from '@components/formatted_time'; import {Preferences} from '@constants'; -import {MM_TABLES} from '@constants/database'; import {getPreferenceAsBool} from '@helpers/api/preference'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import {getCurrentMomentForTimezone} from '@utils/helpers'; import {makeStyleSheetFromTheme} from '@utils/theme'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type PreferenceModel from '@typings/database/models/servers/preference'; import type UserModel from '@typings/database/models/servers/user'; type Props = { @@ -128,14 +126,11 @@ const CustomStatusExpiry = ({currentUser, isMilitaryTime, showPrefix, showTimeCo }; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({ - isMilitaryTime: database.get(MM_TABLES.SERVER.PREFERENCE). - query( - Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS), - ).observe().pipe( - switchMap( - (preferences) => of$(getPreferenceAsBool(preferences, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', false)), - ), + isMilitaryTime: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS).observe().pipe( + switchMap( + (preferences) => of$(getPreferenceAsBool(preferences, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', false)), ), + ), })); export default withDatabase(enhanced(CustomStatusExpiry)); diff --git a/app/components/emoji/index.tsx b/app/components/emoji/index.tsx index 3bb5e1080f..d97a2d87c5 100644 --- a/app/components/emoji/index.tsx +++ b/app/components/emoji/index.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import React from 'react'; @@ -16,14 +15,14 @@ import FastImage, {ImageStyle} from 'react-native-fast-image'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {useServerUrl} from '@context/server'; import NetworkManager from '@init/network_manager'; +import {queryCustomEmojisByName} from '@queries/servers/custom_emoji'; +import {observeConfigBooleanValue} from '@queries/servers/system'; import {EmojiIndicesByAlias, Emojis} from '@utils/emoji'; import type {WithDatabaseArgs} from '@typings/database/database'; import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji'; -import type SystemModel from '@typings/database/models/servers/system'; const assetImages = new Map([['mattermost.png', require('@assets/images/emojis/mattermost.png')]]); @@ -149,13 +148,13 @@ const Emoji = (props: Props) => { const withCustomEmojis = withObservables(['emojiName'], ({database, emojiName}: WithDatabaseArgs & {emojiName: string}) => { const hasEmojiBuiltIn = EmojiIndicesByAlias.has(emojiName); - const displayTextOnly = hasEmojiBuiltIn ? of$(false) : database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap((config) => of$(config.value.EnableCustomEmoji !== 'true')), + const displayTextOnly = hasEmojiBuiltIn ? of$(false) : observeConfigBooleanValue(database, 'EnableCustomEmoji').pipe( + switchMap((value) => of$(!value)), ); return { displayTextOnly, - customEmojis: hasEmojiBuiltIn ? of$([]) : database.get(MM_TABLES.SERVER.CUSTOM_EMOJI).query(Q.where('name', emojiName)).observe(), + customEmojis: hasEmojiBuiltIn ? of$([]) : queryCustomEmojisByName(database, [emojiName]).observe(), }; }); diff --git a/app/components/emoji_picker/index.tsx b/app/components/emoji_picker/index.tsx index 11f1860d43..08fc4e3247 100644 --- a/app/components/emoji_picker/index.tsx +++ b/app/components/emoji_picker/index.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import React, {useCallback, useState} from 'react'; @@ -9,16 +8,17 @@ import {useIntl} from 'react-intl'; import {LayoutChangeEvent, Platform, View} from 'react-native'; import {Edge, SafeAreaView} from 'react-native-safe-area-context'; import {of as of$} from 'rxjs'; -import {catchError, switchMap} from 'rxjs/operators'; +import {switchMap} from 'rxjs/operators'; import {searchCustomEmojis} from '@actions/remote/custom_emoji'; import SearchBar from '@components/search_bar'; import {Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; import {debounce} from '@helpers/api/general'; -import {safeParseJSON} from '@utils/helpers'; +import {queryAllCustomEmojis} from '@queries/servers/custom_emoji'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {observeConfigBooleanValue, observeRecentReactions} from '@queries/servers/system'; import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme'; import EmojiFiltered from './filtered'; @@ -26,8 +26,6 @@ import EmojiSections from './sections'; import type {WithDatabaseArgs} from '@typings/database/database'; import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; export const SCROLLVIEW_NATIVE_ID = 'emojiSelector'; const edges: Edge[] = ['bottom', 'left', 'right']; @@ -150,21 +148,10 @@ const EmojiPicker = ({customEmojis, customEmojisEnabled, onEmojiPress, recentEmo }; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({ - customEmojisEnabled: database.get(MM_TABLES.SERVER.SYSTEM). - findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap((config) => of$(config.value.EnableCustomEmoji === 'true')), - ), - customEmojis: database.get(MM_TABLES.SERVER.CUSTOM_EMOJI).query().observe(), - recentEmojis: database.get(MM_TABLES.SERVER.SYSTEM). - findAndObserve(SYSTEM_IDENTIFIERS.RECENT_REACTIONS). - pipe( - switchMap((recent) => of$(safeParseJSON(recent.value) as string[])), - catchError(() => of$([])), - ), - skinTone: database.get(MM_TABLES.SERVER.PREFERENCE).query( - Q.where('category', Preferences.CATEGORY_EMOJI), - Q.where('name', Preferences.EMOJI_SKINTONE), - ).observe().pipe( + customEmojisEnabled: observeConfigBooleanValue(database, 'EnableCustomEmoji'), + customEmojis: queryAllCustomEmojis(database).observe(), + recentEmojis: observeRecentReactions(database), + skinTone: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_EMOJI, Preferences.EMOJI_SKINTONE).observe().pipe( switchMap((prefs) => of$(prefs?.[0]?.value ?? 'default')), ), })); diff --git a/app/components/markdown/at_mention/index.ts b/app/components/markdown/at_mention/index.ts index c2fb74520d..f91eafe4e6 100644 --- a/app/components/markdown/at_mention/index.ts +++ b/app/components/markdown/at_mention/index.ts @@ -1,37 +1,19 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {combineLatest, of as of$} from 'rxjs'; -import {map, switchMap} from 'rxjs/operators'; -import {Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; -import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; +import {observeCurrentUserId} from '@queries/servers/system'; +import {observeTeammateNameDisplay, queryUsersLike} from '@queries/servers/user'; import AtMention from './at_mention'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; - -const {SERVER: {PREFERENCE, SYSTEM, USER}} = MM_TABLES; -const {CONFIG, CURRENT_USER_ID, LICENSE} = SYSTEM_IDENTIFIERS; const enhance = withObservables(['mentionName'], ({database, mentionName}: {mentionName: string} & WithDatabaseArgs) => { - const config = database.get(SYSTEM).findAndObserve(CONFIG); - const license = database.get(SYSTEM).findAndObserve(LICENSE); - const preferences = database.get(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS)).observe(); - const currentUserId = database.get(SYSTEM).findAndObserve(CURRENT_USER_ID).pipe( - switchMap(({value}) => of$(value)), - ); - const teammateNameDisplay = combineLatest([config, license, preferences]).pipe( - map( - ([{value: cfg}, {value: lcs}, prefs]) => getTeammateNameDisplaySetting(prefs, cfg, lcs), - ), - ); + const currentUserId = observeCurrentUserId(database); + const teammateNameDisplay = observeTeammateNameDisplay(database); let mn = mentionName.toLowerCase(); if ((/[._-]$/).test(mn)) { @@ -41,11 +23,7 @@ const enhance = withObservables(['mentionName'], ({database, mentionName}: {ment return { currentUserId, teammateNameDisplay, - users: database.get(USER).query( - Q.where('username', Q.like( - `%${Q.sanitizeLikeString(mn)}%`, - )), - ).observeWithColumns(['username']), + users: queryUsersLike(database, mn).observeWithColumns(['username']), }; }); diff --git a/app/components/markdown/channel_mention/index.ts b/app/components/markdown/channel_mention/index.ts index ba35a484b8..9a7a55cc3e 100644 --- a/app/components/markdown/channel_mention/index.ts +++ b/app/components/markdown/channel_mention/index.ts @@ -1,38 +1,34 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {map, switchMap} from 'rxjs/operators'; +import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {queryAllChannelsForTeam} from '@queries/servers/channel'; +import {observeCurrentTeamId, observeCurrentUserId} from '@queries/servers/system'; +import {observeTeam} from '@queries/servers/team'; import ChannelMention from './channel_mention'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type ChannelModel from '@typings/database/models/servers/channel'; -import type SystemModel from '@typings/database/models/servers/system'; -import type TeamModel from '@typings/database/models/servers/team'; export type ChannelMentions = Record; -const {SERVER: {CHANNEL, SYSTEM, TEAM}} = MM_TABLES; - const enhance = withObservables([], ({database}: WithDatabaseArgs) => { - const currentTeamId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID); - const currentUserId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID); + const currentTeamId = observeCurrentTeamId(database); + const currentUserId = observeCurrentUserId(database); const channels = currentTeamId.pipe( - switchMap(({value}) => database.get(CHANNEL).query(Q.where('team_id', value)).observeWithColumns(['display_name'])), + switchMap((id) => queryAllChannelsForTeam(database, id).observeWithColumns(['display_name'])), ); const team = currentTeamId.pipe( - switchMap(({value}) => database.get(TEAM).findAndObserve(value)), + switchMap((id) => observeTeam(database, id)), ); return { channels, - currentTeamId: currentTeamId.pipe(map((ct) => ct.value)), - currentUserId: currentUserId.pipe(map((cu) => cu.value)), + currentTeamId, + currentUserId, team, }; }); diff --git a/app/components/markdown/markdown_link/index.ts b/app/components/markdown/markdown_link/index.ts index 7df6fdf40c..c18ff26610 100644 --- a/app/components/markdown/markdown_link/index.ts +++ b/app/components/markdown/markdown_link/index.ts @@ -6,24 +6,19 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeConfig} from '@queries/servers/system'; import MarkdownLink from './markdown_link'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; - -type ConfigValue = { - value: ClientConfig; -} const enhance = withObservables([], ({database}: WithDatabaseArgs) => { - const config = database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG); + const config = observeConfig(database); const experimentalNormalizeMarkdownLinks = config.pipe( - switchMap(({value}: ConfigValue) => of$(value.ExperimentalNormalizeMarkdownLinks)), + switchMap((cfg) => of$(cfg?.ExperimentalNormalizeMarkdownLinks)), ); const siteURL = config.pipe( - switchMap(({value}: ConfigValue) => of$(value.SiteURL)), + switchMap((cfg) => of$(cfg?.SiteURL)), ); return { diff --git a/app/components/post_draft/draft_handler/index.ts b/app/components/post_draft/draft_handler/index.ts index c2738f9ea7..15293c40ac 100644 --- a/app/components/post_draft/draft_handler/index.ts +++ b/app/components/post_draft/draft_handler/index.ts @@ -6,40 +6,33 @@ import withObservables from '@nozbe/with-observables'; import {combineLatest, of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {DEFAULT_SERVER_MAX_FILE_SIZE} from '@constants/post_draft'; +import {observeConfig, observeLicense} from '@queries/servers/system'; import {isMinimumServerVersion} from '@utils/helpers'; import DraftHandler from './draft_handler'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; - -const {SERVER: {SYSTEM}} = MM_TABLES; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { - const config = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap(({value}) => of$(value as ClientConfig)), - ); + const config = observeConfig(database); - const license = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.LICENSE).pipe( - switchMap(({value}) => of$(value as ClientLicense)), - ); + const license = observeLicense(database); const canUploadFiles = combineLatest([config, license]).pipe( switchMap(([c, l]) => of$( - c.EnableFileAttachments !== 'false' && - (l.IsLicensed === 'false' || l.Compliance === 'false' || c.EnableMobileFileUpload !== 'false'), + c?.EnableFileAttachments === 'true' || + (l?.IsLicensed !== 'true' && l?.Compliance !== 'true' && c?.EnableMobileFileUpload === 'true'), ), ), ); const maxFileSize = config.pipe( - switchMap((cfg) => of$(parseInt(cfg.MaxFileSize || '0', 10) || DEFAULT_SERVER_MAX_FILE_SIZE)), + switchMap((cfg) => of$(parseInt(cfg?.MaxFileSize || '0', 10) || DEFAULT_SERVER_MAX_FILE_SIZE)), ); const maxFileCount = config.pipe( - switchMap((cfg) => of$(isMinimumServerVersion(cfg.Version, 6, 0) ? 10 : 5)), + switchMap((cfg) => of$(isMinimumServerVersion(cfg?.Version || '', 6, 0) ? 10 : 5)), ); return { diff --git a/app/components/post_draft/index.ts b/app/components/post_draft/index.ts index 7bf3a39e64..eeffba8795 100644 --- a/app/components/post_draft/index.ts +++ b/app/components/post_draft/index.ts @@ -1,26 +1,23 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {combineLatest, of as of$, from as from$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {General, Permissions} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeChannel} from '@queries/servers/channel'; +import {queryDraft} from '@queries/servers/drafts'; +import {observeConfigBooleanValue, observeCurrentChannelId} from '@queries/servers/system'; +import {observeCurrentUser, observeUser} from '@queries/servers/user'; import {hasPermissionForChannel} from '@utils/role'; import {isSystemAdmin, getUserIdFromChannelName} from '@utils/user'; import PostDraft from './post_draft'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type ChannelModel from '@typings/database/models/servers/channel'; import type DraftModel from '@typings/database/models/servers/draft'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {DRAFT, SYSTEM, USER, CHANNEL}} = MM_TABLES; type OwnProps = { channelId: string; @@ -28,50 +25,50 @@ type OwnProps = { rootId?: string; } +const observeFirst = (v: DraftModel[]) => v[0]?.observe() || of$(undefined); + const enhanced = withObservables([], (ownProps: WithDatabaseArgs & OwnProps) => { const {database, rootId = ''} = ownProps; - const draft = database.get(DRAFT).query( - Q.where('channel_id', ownProps.channelId), - Q.where('root_id', rootId), - ).observeWithColumns(['message', 'files']).pipe(switchMap((v) => of$(v[0]))); + let channelId = of$(ownProps.channelId); + if (!ownProps.channelId) { + channelId = observeCurrentChannelId(database); + } + + const draft = channelId.pipe( + switchMap((cId) => queryDraft(database, cId, rootId).observeWithColumns(['message', 'files']).pipe( + switchMap(observeFirst), + )), + ); const files = draft.pipe(switchMap((d) => of$(d?.files))); const message = draft.pipe(switchMap((d) => of$(d?.message))); - const currentUser = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => database.get(USER).findAndObserve(value)), - ); - - let channelId = of$(ownProps.channelId); - if (!ownProps.channelId) { - channelId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID).pipe( - switchMap((t) => of$(t.value)), - ); - } + const currentUser = observeCurrentUser(database); const channel = channelId.pipe( - switchMap((id) => database.get(CHANNEL).findAndObserve(id!)), + switchMap((id) => observeChannel(database, id!)), ); - const canPost = combineLatest([channel, currentUser]).pipe(switchMap(([c, u]) => from$(hasPermissionForChannel(c, u, Permissions.CREATE_POST, false)))); - const channelIsArchived = channel.pipe(switchMap((c) => (ownProps.channelIsArchived ? of$(true) : of$(c.deleteAt !== 0)))); + const canPost = combineLatest([channel, currentUser]).pipe(switchMap(([c, u]) => (c && u ? from$(hasPermissionForChannel(c, u, Permissions.CREATE_POST, false)) : of$(false)))); + const channelIsArchived = channel.pipe(switchMap((c) => (ownProps.channelIsArchived ? of$(true) : of$(c?.deleteAt !== 0)))); - const experimentalTownSquareIsReadOnly = database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap(({value}: {value: ClientConfig}) => of$(value.ExperimentalTownSquareIsReadOnly === 'true')), - ); + const experimentalTownSquareIsReadOnly = observeConfigBooleanValue(database, 'ExperimentalTownSquareIsReadOnly'); const channelIsReadOnly = combineLatest([currentUser, channel, experimentalTownSquareIsReadOnly]).pipe( - switchMap(([u, c, readOnly]) => of$(c?.name === General.DEFAULT_CHANNEL && !isSystemAdmin(u.roles) && readOnly)), + switchMap(([u, c, readOnly]) => of$(c?.name === General.DEFAULT_CHANNEL && !isSystemAdmin(u?.roles || '') && readOnly)), ); const deactivatedChannel = combineLatest([currentUser, channel]).pipe( switchMap(([u, c]) => { + if (!u || !c) { + return of$(false); + } if (c.type !== General.DM_CHANNEL) { return of$(false); } const teammateId = getUserIdFromChannelName(u.id, c.name); if (teammateId) { - return database.get(USER).findAndObserve(teammateId).pipe( - switchMap((u2) => of$(Boolean(u2.deleteAt))), // eslint-disable-line max-nested-callbacks + return observeUser(database, teammateId).pipe( + switchMap((u2) => (u2 ? of$(Boolean(u2.deleteAt)) : of$(false))), // eslint-disable-line max-nested-callbacks ); } return of$(true); diff --git a/app/components/post_draft/post_input/index.ts b/app/components/post_draft/post_input/index.ts index 42e6b63d69..b01d4f695e 100644 --- a/app/components/post_draft/post_input/index.ts +++ b/app/components/post_draft/post_input/index.ts @@ -6,16 +6,13 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeChannel, observeCurrentChannel} from '@queries/servers/channel'; +import {observeConfig} from '@queries/servers/system'; import PostInput from './post_input'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type ChannelModel from '@typings/database/models/servers/channel'; import type ChannelInfoModel from '@typings/database/models/servers/channel_info'; -import type SystemModel from '@typings/database/models/servers/system'; - -const {SERVER: {SYSTEM, CHANNEL}} = MM_TABLES; type OwnProps = { channelId: string; @@ -23,35 +20,33 @@ type OwnProps = { } const enhanced = withObservables([], ({database, channelId, rootId}: WithDatabaseArgs & OwnProps) => { - const config = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG); + const config = observeConfig(database); const timeBetweenUserTypingUpdatesMilliseconds = config.pipe( - switchMap(({value}: {value: ClientConfig}) => of$(parseInt(value.TimeBetweenUserTypingUpdatesMilliseconds, 10))), + switchMap((cfg) => of$(parseInt(cfg?.TimeBetweenUserTypingUpdatesMilliseconds || '0', 10))), ); const enableUserTypingMessage = config.pipe( - switchMap(({value}: {value: ClientConfig}) => of$(value.EnableUserTypingMessages === 'true')), + switchMap((cfg) => of$(cfg?.EnableUserTypingMessages === 'true')), ); const maxNotificationsPerChannel = config.pipe( - switchMap(({value}: {value: ClientConfig}) => of$(parseInt(value.MaxNotificationsPerChannel, 10))), + switchMap((cfg) => of$(parseInt(cfg?.MaxNotificationsPerChannel || '0', 10))), ); let channel; if (rootId) { - channel = database.get(CHANNEL).findAndObserve(channelId); + channel = observeChannel(database, channelId); } else { - channel = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID).pipe( - switchMap((t) => database.get(CHANNEL).findAndObserve(t.value)), - ); + channel = observeCurrentChannel(database); } const channelDisplayName = channel.pipe( - switchMap((c) => of$(c.displayName)), + switchMap((c) => of$(c?.displayName)), ); - const channelInfo = channel.pipe(switchMap((c) => c.info.observe())); - - const membersInChannel = channelInfo.pipe( + const membersInChannel = channel.pipe( + switchMap((c) => (c ? c.info.observe() : of$({memberCount: 0}))), + ).pipe( switchMap((i: ChannelInfoModel) => of$(i.memberCount)), ); diff --git a/app/components/post_draft/quick_actions/index.ts b/app/components/post_draft/quick_actions/index.ts index b5b9dbef50..f2d1598180 100644 --- a/app/components/post_draft/quick_actions/index.ts +++ b/app/components/post_draft/quick_actions/index.ts @@ -6,34 +6,27 @@ import withObservables from '@nozbe/with-observables'; import {combineLatest, of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeConfig, observeLicense} from '@queries/servers/system'; import {isMinimumServerVersion} from '@utils/helpers'; import QuickActions from './quick_actions'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; - -const {SERVER: {SYSTEM}} = MM_TABLES; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { - const config = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap(({value}) => of$(value as ClientConfig)), - ); - const license = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.LICENSE).pipe( - switchMap(({value}) => of$(value as ClientLicense)), - ); + const config = observeConfig(database); + const license = observeLicense(database); const canUploadFiles = combineLatest([config, license]).pipe( switchMap(([c, l]) => of$( - c.EnableFileAttachments !== 'false' && - (l.IsLicensed === 'false' || l.Compliance === 'false' || c.EnableMobileFileUpload !== 'false'), + c?.EnableFileAttachments === 'true' || + (l?.IsLicensed !== 'true' && l?.Compliance !== 'true' && c?.EnableMobileFileUpload === 'true'), ), ), ); const maxFileCount = config.pipe( - switchMap((cfg) => of$(isMinimumServerVersion(cfg.Version, 6, 0) ? 10 : 5)), + switchMap((cfg) => of$(isMinimumServerVersion(cfg?.Version || '', 6, 0) ? 10 : 5)), ); return { diff --git a/app/components/post_draft/send_handler/index.ts b/app/components/post_draft/send_handler/index.ts index 4ccaeb2eae..554039d4e4 100644 --- a/app/components/post_draft/send_handler/index.ts +++ b/app/components/post_draft/send_handler/index.ts @@ -7,20 +7,16 @@ import {combineLatest, of as of$, from as from$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {General, Permissions} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {MAX_MESSAGE_LENGTH_FALLBACK} from '@constants/post_draft'; +import {observeChannel, observeCurrentChannel} from '@queries/servers/channel'; +import {queryAllCustomEmojis} from '@queries/servers/custom_emoji'; +import {observeConfig, observeCurrentUserId} from '@queries/servers/system'; +import {observeUser} from '@queries/servers/user'; import {hasPermissionForChannel} from '@utils/role'; import SendHandler from './send_handler'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type ChannelModel from '@typings/database/models/servers/channel'; -import type ChannelInfoModel from '@typings/database/models/servers/channel_info'; -import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {SYSTEM, USER, CHANNEL, CUSTOM_EMOJI}} = MM_TABLES; type OwnProps = { rootId: string; @@ -33,34 +29,28 @@ const enhanced = withObservables([], (ownProps: WithDatabaseArgs & OwnProps) => const {rootId, channelId} = ownProps; let channel; if (rootId) { - channel = database.get(CHANNEL).findAndObserve(channelId); + channel = observeChannel(database, channelId); } else { - channel = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID).pipe( - switchMap((t) => database.get(CHANNEL).findAndObserve(t.value)), - ); + channel = observeCurrentChannel(database); } - const currentUserId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => of$(value)), - ); + const currentUserId = observeCurrentUserId(database); const currentUser = currentUserId.pipe( - switchMap((id) => database.get(USER).findAndObserve(id)), - ); + switchMap((id) => observeUser(database, id), + )); const userIsOutOfOffice = currentUser.pipe( - switchMap((u) => of$(u.status === General.OUT_OF_OFFICE)), + switchMap((u) => of$(u?.status === General.OUT_OF_OFFICE)), ); - const config = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap(({value}) => of$(value as ClientConfig)), - ); + const config = observeConfig(database); const enableConfirmNotificationsToChannel = config.pipe( - switchMap((cfg) => of$(Boolean(cfg.EnableConfirmNotificationsToChannel === 'true'))), + switchMap((cfg) => of$(Boolean(cfg?.EnableConfirmNotificationsToChannel === 'true'))), ); const isTimezoneEnabled = config.pipe( - switchMap((cfg) => of$(Boolean(cfg.ExperimentalTimezone === 'true'))), + switchMap((cfg) => of$(Boolean(cfg?.ExperimentalTimezone === 'true'))), ); const maxMessageLength = config.pipe( - switchMap((cfg) => of$(parseInt(cfg.MaxPostSize || '0', 10) || MAX_MESSAGE_LENGTH_FALLBACK)), + switchMap((cfg) => of$(parseInt(cfg?.MaxPostSize || '0', 10) || MAX_MESSAGE_LENGTH_FALLBACK)), ); const useChannelMentions = combineLatest([channel, currentUser]).pipe( @@ -69,16 +59,16 @@ const enhanced = withObservables([], (ownProps: WithDatabaseArgs & OwnProps) => return of$(true); } - return from$(hasPermissionForChannel(c, u, Permissions.USE_CHANNEL_MENTIONS, false)); + return u ? from$(hasPermissionForChannel(c, u, Permissions.USE_CHANNEL_MENTIONS, false)) : of$(false); }), ); - const channelInfo = channel.pipe(switchMap((c) => c.info.observe())); + const channelInfo = channel.pipe(switchMap((c) => (c ? c.info.observe() : of$(undefined)))); const membersCount = channelInfo.pipe( - switchMap((i: ChannelInfoModel) => of$(i.memberCount)), + switchMap((i) => (i ? of$(i.memberCount) : of$(0))), ); - const customEmojis = database.get(CUSTOM_EMOJI).query().observe(); + const customEmojis = queryAllCustomEmojis(database).observe(); return { currentUserId, diff --git a/app/components/post_list/combined_user_activity/index.ts b/app/components/post_list/combined_user_activity/index.ts index 179d5f7dce..84fd85ecf3 100644 --- a/app/components/post_list/combined_user_activity/index.ts +++ b/app/components/post_list/combined_user_activity/index.ts @@ -1,50 +1,38 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {combineLatest, from as from$, of as of$} from 'rxjs'; import {map, switchMap} from 'rxjs/operators'; import {Permissions} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {queryPostsById} from '@queries/servers/post'; +import {observeCurrentUserId} from '@queries/servers/system'; +import {observeUser, queryUsersByIdsOrUsernames} from '@queries/servers/user'; import {generateCombinedPost, getPostIdsForCombinedUserActivityPost} from '@utils/post_list'; import {hasPermissionForPost} from '@utils/role'; import CombinedUserActivity from './combined_user_activity'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type PostModel from '@typings/database/models/servers/post'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {POST, SYSTEM, USER}} = MM_TABLES; const withCombinedPosts = withObservables(['postId'], ({database, postId}: WithDatabaseArgs & {postId: string}) => { - const currentUserId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - map(({value}: {value: string}) => value), - ); + const currentUserId = observeCurrentUserId(database); const currentUser = currentUserId.pipe( - switchMap((value) => database.get(USER).findAndObserve(value)), + switchMap((value) => observeUser(database, value)), ); const postIds = getPostIdsForCombinedUserActivityPost(postId); - const posts = database.get(POST).query( - Q.where('id', Q.oneOf(postIds)), - ).observe(); + const posts = queryPostsById(database, postIds).observe(); const post = posts.pipe(map((ps) => generateCombinedPost(postId, ps))); const canDelete = combineLatest([posts, currentUser]).pipe( - switchMap(([ps, u]) => from$(hasPermissionForPost(ps[0], u, Permissions.DELETE_OTHERS_POSTS, false))), + switchMap(([ps, u]) => (u ? from$(hasPermissionForPost(ps[0], u, Permissions.DELETE_OTHERS_POSTS, false)) : of$(false))), ); const usernamesById = post.pipe( switchMap( - (p) => database.get(USER).query( - Q.or( - Q.where('id', Q.oneOf(p.props.user_activity.allUserIds)), - Q.where('username', Q.oneOf(p.props.user_activity.allUsernames)), - )).observe(). + (p) => queryUsersByIdsOrUsernames(database, p.props.user_activity.allUserIds, p.props.user_activity.allUsernames).observe(). pipe( // eslint-disable-next-line max-nested-callbacks switchMap((users) => { diff --git a/app/components/post_list/more_messages/index.ts b/app/components/post_list/more_messages/index.ts index 478014d5af..7e44043234 100644 --- a/app/components/post_list/more_messages/index.ts +++ b/app/components/post_list/more_messages/index.ts @@ -6,20 +6,16 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {Database} from '@constants'; +import {observeMyChannel} from '@queries/servers/channel'; import MoreMessages from './more_messages'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type MyChannelModel from '@typings/database/models/servers/my_channel'; - -const {MM_TABLES} = Database; -const {SERVER: {MY_CHANNEL}} = MM_TABLES; const enhanced = withObservables(['channelId'], ({channelId, database}: {channelId: string} & WithDatabaseArgs) => { - const myChannel = database.get(MY_CHANNEL).findAndObserve(channelId); - const isManualUnread = myChannel.pipe(switchMap((ch) => of$(ch.manuallyUnread))); - const unreadCount = myChannel.pipe(switchMap((ch) => of$(ch.messageCount))); + const myChannel = observeMyChannel(database, channelId); + const isManualUnread = myChannel.pipe(switchMap((ch) => of$(ch?.manuallyUnread))); + const unreadCount = myChannel.pipe(switchMap((ch) => of$(ch?.messageCount))); return { isManualUnread, diff --git a/app/components/post_list/post/avatar/index.ts b/app/components/post_list/post/avatar/index.ts index 777d28d666..58b17970b3 100644 --- a/app/components/post_list/post/avatar/index.ts +++ b/app/components/post_list/post/avatar/index.ts @@ -3,21 +3,16 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import enhance from '@nozbe/with-observables'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeConfigBooleanValue} from '@queries/servers/system'; import Avatar from './avatar'; import type {WithDatabaseArgs} from '@typings/database/database'; import type PostModel from '@typings/database/models/servers/post'; -import type SystemModel from '@typings/database/models/servers/system'; const withPost = enhance(['post'], ({database, post}: {post: PostModel} & WithDatabaseArgs) => { - const enablePostIconOverride = database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap((cfg) => of$(cfg.value.EnablePostIconOverride === 'true')), - ); + const enablePostIconOverride = observeConfigBooleanValue(database, 'EnablePostIconOverride'); return { author: post.author.observe(), 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 cf3d0d026d..9073a69f14 100644 --- a/app/components/post_list/post/body/add_members/index.ts +++ b/app/components/post_list/post/body/add_members/index.ts @@ -6,21 +6,16 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +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'; -import type SystemModel from '@typings/database/models/servers/system'; - -const {SERVER: {SYSTEM, USER}} = MM_TABLES; const enhance = withObservables(['post'], ({database, post}: WithDatabaseArgs & {post: PostModel}) => ({ - currentUser: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => database.get(USER).findAndObserve(value)), - ), + currentUser: observeCurrentUser(database), channelType: post.channel.observe().pipe( switchMap( (channel: ChannelModel) => (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 550e07b9ae..748abd8efc 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 @@ -9,8 +9,8 @@ import {map} from 'rxjs/operators'; import {doAppCall, postEphemeralCallResponseForPost} from '@actions/remote/apps'; import {AppExpandLevels, AppBindingLocations, AppCallTypes, AppCallResponseTypes} from '@constants/apps'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {useServerUrl} from '@context/server'; +import {observeCurrentTeamId} from '@queries/servers/system'; import {createCallContext, createCallRequest} from '@utils/apps'; import {getStatusColors} from '@utils/message_attachment_colors'; import {preventDoubleTap} from '@utils/tap'; @@ -20,7 +20,6 @@ import ButtonBindingText from './button_binding_text'; import type ChannelModel from '@typings/database/models/servers/channel'; import type PostModel from '@typings/database/models/servers/post'; -import type SystemModel from '@typings/database/models/servers/system'; type Props = { currentTeamId: string; @@ -30,8 +29,6 @@ type Props = { theme: Theme; } -const {SERVER: {SYSTEM}} = MM_TABLES; - const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { const STATUS_COLORS = getStatusColors(theme); return { @@ -136,9 +133,7 @@ const ButtonBinding = ({currentTeamId, binding, post, teamID, theme}: Props) => const withTeamId = withObservables(['post'], ({post}: {post: PostModel}) => ({ teamID: post.channel.observe().pipe(map((channel: ChannelModel) => channel.teamId)), - currentTeamId: post.collections.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe( - map(({value}) => value), - ), + currentTeamId: observeCurrentTeamId(post.database), })); export default 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 ceb8d57b7a..ff20e5e55e 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 @@ -9,13 +9,12 @@ import {map} from 'rxjs/operators'; import {doAppCall, postEphemeralCallResponseForPost} from '@actions/remote/apps'; import AutocompleteSelector from '@components/autocomplete_selector'; import {AppExpandLevels, AppBindingLocations, AppCallTypes, AppCallResponseTypes} from '@constants/apps'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {useServerUrl} from '@context/server'; +import {observeCurrentTeamId} from '@queries/servers/system'; import {createCallContext, createCallRequest} from '@utils/apps'; import type ChannelModel from '@typings/database/models/servers/channel'; import type PostModel from '@typings/database/models/servers/post'; -import type SystemModel from '@typings/database/models/servers/system'; type Props = { binding: AppBinding; @@ -25,8 +24,6 @@ type Props = { theme: Theme; } -const {SERVER: {SYSTEM}} = MM_TABLES; - const MenuBinding = ({binding, currentTeamId, post, teamID, theme}: Props) => { const [selected, setSelected] = useState(); const intl = useIntl(); @@ -109,9 +106,7 @@ const MenuBinding = ({binding, currentTeamId, post, teamID, theme}: Props) => { const withTeamId = withObservables(['post'], ({post}: {post: PostModel}) => ({ teamID: post.channel.observe().pipe(map((channel: ChannelModel) => channel.teamId)), - currentTeamId: post.collections.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe( - map(({value}) => value), - ), + currentTeamId: observeCurrentTeamId(post.database), })); export default withTeamId(MenuBinding); diff --git a/app/components/post_list/post/body/content/image_preview/index.ts b/app/components/post_list/post/body/content/image_preview/index.ts index b4729240a1..f9f6e1e35d 100644 --- a/app/components/post_list/post/body/content/image_preview/index.ts +++ b/app/components/post_list/post/body/content/image_preview/index.ts @@ -1,28 +1,24 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeExpandedLinks} from '@queries/servers/system'; import ImagePreview from './image_preview'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; const enhance = withObservables(['metadata'], ({database, metadata}: WithDatabaseArgs & {metadata: PostMetadata}) => { const link = metadata.embeds?.[0].url; return { - expandedLink: database.get(MM_TABLES.SERVER.SYSTEM).query( - Q.where('id', SYSTEM_IDENTIFIERS.EXPANDED_LINKS), - ).observe().pipe( - switchMap((values: SystemModel[]) => ( - (link && values.length) ? of$((values[0].value as Record)[link]) : of$(undefined)), + expandedLink: observeExpandedLinks(database).pipe( + switchMap((expandedLinks) => ( + link ? of$(expandedLinks[link]) : of$(undefined)), ), ), link: of$(link), diff --git a/app/components/post_list/post/body/content/opengraph/index.ts b/app/components/post_list/post/body/content/opengraph/index.ts index b38d0c9f0c..6cc45c1d1f 100644 --- a/app/components/post_list/post/body/content/opengraph/index.ts +++ b/app/components/post_list/post/body/content/opengraph/index.ts @@ -1,21 +1,18 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; +import {of as of$, combineLatest} from 'rxjs'; import {Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {getPreferenceAsBool} from '@helpers/api/preference'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {observeConfig} from '@queries/servers/system'; import Opengraph from './opengraph'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; const enhance = withObservables( ['removeLinkPreview'], @@ -24,21 +21,12 @@ const enhance = withObservables( return {showLinkPreviews: of$(false)}; } - const showLinkPreviews = database.get(MM_TABLES.SERVER.PREFERENCE).query( - Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS), - Q.where('name', Preferences.LINK_PREVIEW_DISPLAY), - ).observe().pipe( - switchMap( - (preferences: PreferenceModel[]) => database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - // eslint-disable-next-line max-nested-callbacks - switchMap((config: SystemModel) => { - const cfg: ClientConfig = config.value; - const previewsEnabled = getPreferenceAsBool(preferences, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.LINK_PREVIEW_DISPLAY, true); - return of$(previewsEnabled && cfg.EnableLinkPreviews === 'true'); - }), - ), - ), - ); + const config = observeConfig(database); + const linkPreviewPreference = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.LINK_PREVIEW_DISPLAY).observe(); + const showLinkPreviews = combineLatest([config, linkPreviewPreference], (cfg, pref) => { + const previewsEnabled = getPreferenceAsBool(pref, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.LINK_PREVIEW_DISPLAY, true); + return of$(previewsEnabled && cfg?.EnableLinkPreviews === 'true'); + }); return {showLinkPreviews}; }, diff --git a/app/components/post_list/post/body/content/youtube/index.ts b/app/components/post_list/post/body/content/youtube/index.ts index 99111d1ad6..22ad39fbc7 100644 --- a/app/components/post_list/post/body/content/youtube/index.ts +++ b/app/components/post_list/post/body/content/youtube/index.ts @@ -3,22 +3,15 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeConfigValue} from '@queries/servers/system'; import YouTube from './youtube'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; const enhance = withObservables([], ({database}: WithDatabaseArgs) => ({ - googleDeveloperKey: database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap(({value}: {value: ClientConfig}) => { - return of$(value.GoogleDeveloperKey); - }), - ), + googleDeveloperKey: observeConfigValue(database, 'GoogleDeveloperKey'), })); export default withDatabase(enhance(YouTube)); diff --git a/app/components/post_list/post/body/files/index.ts b/app/components/post_list/post/body/files/index.ts index fe23f096f4..4f0b3da925 100644 --- a/app/components/post_list/post/body/files/index.ts +++ b/app/components/post_list/post/body/files/index.ts @@ -6,7 +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 {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeConfig, observeLicense} from '@queries/servers/system'; import {fileExists} from '@utils/file'; import Files from './files'; @@ -14,7 +14,6 @@ import Files from './files'; import type {WithDatabaseArgs} from '@typings/database/database'; import type FileModel from '@typings/database/models/servers/file'; import type PostModel from '@typings/database/models/servers/post'; -import type SystemModel from '@typings/database/models/servers/system'; type EnhanceProps = WithDatabaseArgs & { post: PostModel; @@ -37,17 +36,17 @@ const filesLocalPathValidation = async (files: FileModel[], authorId: string) => }; const enhance = withObservables(['post'], ({database, post}: EnhanceProps) => { - const config = database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG); + const config = observeConfig(database); const enableMobileFileDownload = config.pipe( - switchMap(({value}: {value: ClientConfig}) => of$(value.EnableMobileFileDownload !== 'false')), + switchMap((cfg) => of$(cfg?.EnableMobileFileDownload !== 'false')), ); const publicLinkEnabled = config.pipe( - switchMap(({value}: {value: ClientConfig}) => of$(value.EnablePublicLink !== 'false')), + switchMap((cfg) => of$(cfg?.EnablePublicLink !== 'false')), ); - const complianceDisabled = database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.LICENSE).pipe( - switchMap(({value}: {value: ClientLicense}) => of$(value.IsLicensed === 'false' || value.Compliance === 'false')), + const complianceDisabled = observeLicense(database).pipe( + switchMap((lcs) => of$(lcs?.IsLicensed === 'false' || lcs?.Compliance === 'false')), ); const canDownloadFiles = combineLatest([enableMobileFileDownload, complianceDisabled]).pipe( diff --git a/app/components/post_list/post/body/message/index.ts b/app/components/post_list/post/body/message/index.ts index 5cc21eda69..cb56ac2b66 100644 --- a/app/components/post_list/post/body/message/index.ts +++ b/app/components/post_list/post/body/message/index.ts @@ -3,22 +3,15 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeCurrentUser} from '@queries/servers/user'; import Message from './message'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {SYSTEM, USER}} = MM_TABLES; const withMessageInput = withObservables([], ({database}: WithDatabaseArgs) => { - const currentUser = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => database.get(USER).findAndObserve(value)), - ); + const currentUser = observeCurrentUser(database); return { currentUser, }; diff --git a/app/components/post_list/post/body/reactions/index.ts b/app/components/post_list/post/body/reactions/index.ts index 033f37ea11..a569e17513 100644 --- a/app/components/post_list/post/body/reactions/index.ts +++ b/app/components/post_list/post/body/reactions/index.ts @@ -7,7 +7,8 @@ import {combineLatest, from as from$, of as of$} from 'rxjs'; import {map, switchMap} from 'rxjs/operators'; import {General, Permissions} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeConfigBooleanValue, observeCurrentUserId} from '@queries/servers/system'; +import {observeUser} from '@queries/servers/user'; import {hasPermissionForPost} from '@utils/role'; import {isSystemAdmin} from '@utils/user'; @@ -17,30 +18,24 @@ import type {Relation} 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'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; type WithReactionsInput = WithDatabaseArgs & { post: PostModel; } const withReactions = withObservables(['post'], ({database, post}: WithReactionsInput) => { - const currentUserId = database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - map(({value}: {value: string}) => value), - ); + const currentUserId = observeCurrentUserId(database); const currentUser = currentUserId.pipe( - switchMap((id) => database.get(MM_TABLES.SERVER.USER).findAndObserve(id)), + switchMap((id) => observeUser(database, id)), ); const channel = (post.channel as Relation).observe(); - const experimentalTownSquareIsReadOnly = database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - map(({value}: {value: ClientConfig}) => value.ExperimentalTownSquareIsReadOnly === 'true'), - ); + const experimentalTownSquareIsReadOnly = observeConfigBooleanValue(database, 'ExperimentalTownSquareIsReadOnly'); const disabled = combineLatest([currentUser, channel, experimentalTownSquareIsReadOnly]).pipe( - map(([u, c, readOnly]) => ((c && c.deleteAt > 0) || (c?.name === General.DEFAULT_CHANNEL && !isSystemAdmin(u.roles) && readOnly))), + map(([u, c, readOnly]) => ((c && c.deleteAt > 0) || (c?.name === General.DEFAULT_CHANNEL && !isSystemAdmin(u?.roles || '') && readOnly))), ); - const canAddReaction = currentUser.pipe(switchMap((u) => from$(hasPermissionForPost(post, u, Permissions.ADD_REACTION, true)))); - const canRemoveReaction = currentUser.pipe(switchMap((u) => from$(hasPermissionForPost(post, u, Permissions.REMOVE_REACTION, true)))); + const canAddReaction = currentUser.pipe(switchMap((u) => (u ? from$(hasPermissionForPost(post, u, Permissions.ADD_REACTION, true)) : of$(true)))); + const canRemoveReaction = currentUser.pipe(switchMap((u) => (u ? from$(hasPermissionForPost(post, u, Permissions.REMOVE_REACTION, true)) : of$(true)))); return { canAddReaction, diff --git a/app/components/post_list/post/header/index.ts b/app/components/post_list/post/header/index.ts index 101d700e58..f11c750ea8 100644 --- a/app/components/post_list/post/header/index.ts +++ b/app/components/post_list/post/header/index.ts @@ -1,51 +1,43 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {combineLatest, of as of$} from 'rxjs'; import {map, switchMap} from 'rxjs/operators'; import {Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {getPreferenceAsBool, getTeammateNameDisplaySetting} from '@helpers/api/preference'; +import {queryPostsInThread} from '@queries/servers/post'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {observeConfig, observeLicense} from '@queries/servers/system'; import {isMinimumServerVersion} from '@utils/helpers'; import Header from './header'; import type {WithDatabaseArgs} from '@typings/database/database'; import type PostModel from '@typings/database/models/servers/post'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; type HeaderInputProps = { differentThreadSequence: boolean; post: PostModel; }; -const {SERVER: {POST, PREFERENCE, SYSTEM}} = MM_TABLES; - const withHeaderProps = withObservables( ['post', 'differentThreadSequence'], ({post, database, differentThreadSequence}: WithDatabaseArgs & HeaderInputProps) => { - const config = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe(switchMap(({value}) => of$(value as ClientConfig))); - const license = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.LICENSE).pipe(switchMap(({value}) => of$(value as ClientLicense))); - const preferences = database.get(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS)).observe(); + const config = observeConfig(database); + const license = observeLicense(database); + const preferences = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS).observe(); const author = post.author.observe(); - const enablePostUsernameOverride = config.pipe(map((cfg) => cfg.EnablePostUsernameOverride === 'true')); - const isTimezoneEnabled = config.pipe(map((cfg) => cfg.ExperimentalTimezone === 'true')); + const enablePostUsernameOverride = config.pipe(map((cfg) => cfg?.EnablePostUsernameOverride === 'true')); + const isTimezoneEnabled = config.pipe(map((cfg) => cfg?.ExperimentalTimezone === 'true')); const isMilitaryTime = preferences.pipe(map((prefs) => getPreferenceAsBool(prefs, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', false))); - const isCustomStatusEnabled = config.pipe(map((cfg) => cfg.EnableCustomUserStatuses === 'true' && isMinimumServerVersion(cfg.Version, 5, 36))); + const isCustomStatusEnabled = config.pipe(map((cfg) => cfg?.EnableCustomUserStatuses === 'true' && isMinimumServerVersion(cfg.Version, 5, 36))); const teammateNameDisplay = combineLatest([preferences, config, license]).pipe( map(([prefs, cfg, lcs]) => getTeammateNameDisplaySetting(prefs, cfg, lcs)), ); - const commentCount = database.get(POST).query( - Q.and( - Q.where('root_id', (post.rootId || post.id)), - Q.where('delete_at', Q.eq(0)), - ), - ).observeCount(); + const commentCount = queryPostsInThread(database, post.rootId || post.id).observeCount(); const rootPostAuthor = differentThreadSequence ? post.root.observe().pipe(switchMap((root) => { if (root.length) { return root[0].author.observe(); diff --git a/app/components/post_list/post/index.ts b/app/components/post_list/post/index.ts index 568fa8bcb0..3592b54683 100644 --- a/app/components/post_list/post/index.ts +++ b/app/components/post_list/post/index.ts @@ -1,15 +1,17 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {from as from$, of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {Permissions, Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; -import {appsEnabled} from '@utils/apps'; +import {queryAllCustomEmojis} from '@queries/servers/custom_emoji'; +import {queryPostsBetween} from '@queries/servers/post'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {observeConfigBooleanValue} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {hasJumboEmojiOnly} from '@utils/emoji/helpers'; import {areConsecutivePosts, isPostEphemeral} from '@utils/post'; import {canManageChannelMembers, hasPermissionForPost} from '@utils/role'; @@ -20,14 +22,10 @@ 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'; import type PostsInThreadModel from '@typings/database/models/servers/posts_in_thread'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; import type UserModel from '@typings/database/models/servers/user'; -const {SERVER: {CUSTOM_EMOJI, POST, PREFERENCE, SYSTEM, USER}} = MM_TABLES; - type PropsInput = WithDatabaseArgs & { - featureFlagAppsEnabled?: string; + appsEnabled: boolean; currentUser: UserModel; nextPost: PostModel | undefined; post: PostModel; @@ -38,13 +36,7 @@ async function shouldHighlightReplyBar(currentUser: UserModel, post: PostModel, let commentsNotifyLevel = Preferences.COMMENTS_NEVER; let threadCreatedByCurrentUser = false; let rootPost: PostModel | undefined; - const myPosts = await postsInThread.collections.get(POST).query( - Q.and( - Q.where('root_id', post.rootId || post.id), - Q.where('create_at', Q.between(postsInThread.earliest, postsInThread.latest)), - Q.where('user_id', currentUser.id), - ), - ).fetch(); + const myPosts = await queryPostsBetween(postsInThread.database, postsInThread.earliest, postsInThread.latest, null, currentUser.id, '', post.rootId || post.id).fetch(); const threadRepliedToByCurrentUser = myPosts.length > 0; const root = await post.root.fetch(); @@ -82,17 +74,13 @@ function isFirstReply(post: PostModel, previousPost?: PostModel) { } const withSystem = withObservables([], ({database}: WithDatabaseArgs) => ({ - featureFlagAppsEnabled: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap((cfg) => of$(cfg.value.FeatureFlagAppsEnabled)), - ), - currentUser: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap((currentUserId) => database.get(USER).findAndObserve(currentUserId.value)), - ), + appsEnabled: observeConfigBooleanValue(database, 'FeatureFlagAppsEnabled'), + currentUser: observeCurrentUser(database), })); const withPost = withObservables( ['currentUser', 'post', 'previousPost', 'nextPost'], - ({featureFlagAppsEnabled, currentUser, database, post, previousPost, nextPost}: PropsInput) => { + ({appsEnabled, currentUser, database, post, previousPost, nextPost}: PropsInput) => { let isJumboEmoji = of$(false); let isLastReply = of$(true); let isPostAddChannelMember = of$(false); @@ -100,10 +88,9 @@ const withPost = withObservables( const author = post.author.observe(); const canDelete = from$(hasPermissionForPost(post, currentUser, isOwner ? Permissions.DELETE_POST : Permissions.DELETE_OTHERS_POSTS, false)); const isEphemeral = of$(isPostEphemeral(post)); - const isSaved = database.get(PREFERENCE).query( - Q.where('category', Preferences.CATEGORY_SAVED_POST), - Q.where('name', post.id), - ).observe().pipe(switchMap((pref) => of$(Boolean(pref.length)))); + const isSaved = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SAVED_POST, post.id).observe().pipe( + switchMap((pref) => of$(Boolean(pref.length))), + ); if (post.props?.add_channel_member && isPostEphemeral(post)) { isPostAddChannelMember = from$(canManageChannelMembers(post, currentUser)); @@ -124,7 +111,7 @@ const withPost = withObservables( } if (post.message.length && !(/^\s{4}/).test(post.message)) { - isJumboEmoji = post.collections.get(CUSTOM_EMOJI).query().observe().pipe( + isJumboEmoji = queryAllCustomEmojis(post.database).observe().pipe( // eslint-disable-next-line max-nested-callbacks switchMap((customEmojis: CustomEmojiModel[]) => of$(hasJumboEmojiOnly(post.message, customEmojis.map((c) => c.name))), ), @@ -132,15 +119,11 @@ const withPost = withObservables( } const hasReplies = from$(post.hasReplies()); const isConsecutivePost = author.pipe( - switchMap((user) => of$(Boolean(post && previousPost && !user.isBot && areConsecutivePosts(post, previousPost)))), + switchMap((user) => of$(Boolean(post && previousPost && !user?.isBot && areConsecutivePosts(post, previousPost)))), ); - const partialConfig: Partial = { - FeatureFlagAppsEnabled: featureFlagAppsEnabled, - }; - return { - appsEnabled: of$(appsEnabled(partialConfig)), + appsEnabled: of$(appsEnabled), canDelete, differentThreadSequence: of$(differentThreadSequence), filesCount: post.files.observeCount(), diff --git a/app/components/post_list/thread_overview/index.ts b/app/components/post_list/thread_overview/index.ts index 5ae2bf418b..42b7716709 100644 --- a/app/components/post_list/thread_overview/index.ts +++ b/app/components/post_list/thread_overview/index.ts @@ -1,45 +1,30 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {Preferences} from '@constants'; -import {MM_TABLES} from '@constants/database'; +import {observePost, queryPostsInThread} from '@queries/servers/post'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import ThreadOverview from './thread_overview'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type PostModel from '@typings/database/models/servers/post'; -import type PreferenceModel from '@typings/database/models/servers/preference'; - -const {SERVER: {POST, PREFERENCE}} = MM_TABLES; const enhanced = withObservables( ['rootId'], ({database, rootId}: WithDatabaseArgs & {rootId: string}) => { return { - rootPost: database.get(POST).query( - Q.where('id', rootId), - ).observe().pipe( - - // Root post might not have loaded while the thread screen is opening - switchMap((posts) => posts[0]?.observe() || of$(undefined)), - ), - isSaved: database. - get(PREFERENCE). - query(Q.where('category', Preferences.CATEGORY_SAVED_POST), Q.where('name', rootId)). + rootPost: observePost(database, rootId), + isSaved: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SAVED_POST, rootId). observe(). pipe( switchMap((pref) => of$(Boolean(pref[0]?.value === 'true'))), ), - repliesCount: database.get(POST).query( - Q.where('root_id', rootId), - Q.where('delete_at', Q.eq(0)), - ).observeCount(), + repliesCount: queryPostsInThread(database, rootId).observeCount(), }; }); diff --git a/app/components/post_list/thread_overview/thread_overview.tsx b/app/components/post_list/thread_overview/thread_overview.tsx index 54d9e87b0d..bd4a97a1de 100644 --- a/app/components/post_list/thread_overview/thread_overview.tsx +++ b/app/components/post_list/thread_overview/thread_overview.tsx @@ -7,14 +7,14 @@ import {Keyboard, Platform, View} from 'react-native'; import {TouchableOpacity} from 'react-native-gesture-handler'; import {deleteSavedPost, savePostPreference} from '@actions/remote/preference'; -import FormattedText from '@app/components/formatted_text'; -import {Screens} from '@app/constants'; -import {preventDoubleTap} from '@app/utils/tap'; import CompassIcon from '@components/compass_icon'; +import FormattedText from '@components/formatted_text'; +import {Screens} from '@constants'; import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; import {useIsTablet} from '@hooks/device'; import {bottomSheetModalOptions, showModal, showModalOverCurrentContext} from '@screens/navigation'; +import {preventDoubleTap} from '@utils/tap'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; 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 93a9fdd74e..93240976f4 100644 --- a/app/components/post_with_channel_info/channel_info/index.ts +++ b/app/components/post_with_channel_info/channel_info/index.ts @@ -7,18 +7,17 @@ import {switchMap, of as of$} from 'rxjs'; import ChannelInfo from './channel_info'; import type PostModel from '@typings/database/models/servers/post'; -import type TeamModel from '@typings/database/models/servers/team'; const enhance = withObservables(['post'], ({post}: {post: PostModel}) => { const channel = post.channel.observe(); return { channelName: channel.pipe( - switchMap((chan) => of$(chan.displayName)), + switchMap((chan) => (chan ? of$(chan.displayName) : '')), ), teamName: channel.pipe( - switchMap((chan) => chan.team || of$(null)), - switchMap((team: TeamModel|null) => of$(team?.displayName || null)), + switchMap((chan) => (chan ? chan.team.observe() : of$(null))), + switchMap((team) => of$(team?.displayName || null)), ), }; }); diff --git a/app/components/server_version/index.tsx b/app/components/server_version/index.tsx index 3a21c2fd13..4ef7a34c1c 100644 --- a/app/components/server_version/index.tsx +++ b/app/components/server_version/index.tsx @@ -5,26 +5,22 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {useEffect} from 'react'; import {useIntl} from 'react-intl'; -import {of as of$} from 'rxjs'; -import {map, switchMap} from 'rxjs/operators'; +import {map} from 'rxjs/operators'; import {SupportedServer} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeConfigValue} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {isMinimumServerVersion} from '@utils/helpers'; import {unsupportedServer} from '@utils/server'; import {isSystemAdmin} from '@utils/user'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; type ServerVersionProps = WithDatabaseArgs & { version?: string; roles: string; }; -const {SERVER: {SYSTEM, USER}} = MM_TABLES; - const ServerVersion = ({version, roles}: ServerVersionProps) => { const intl = useIntl(); @@ -46,16 +42,9 @@ const ServerVersion = ({version, roles}: ServerVersionProps) => { }; const enahanced = withObservables([], ({database}: WithDatabaseArgs) => ({ - varsion: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap(({value}: {value: ClientConfig}) => of$(value.Version)), - ), - roles: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap( - ({value}) => database.get(USER).findAndObserve(value).pipe( - // eslint-disable-next-line max-nested-callbacks - map((user) => user.roles), - ), - ), + version: observeConfigValue(database, 'Version'), + roles: observeCurrentUser(database).pipe( + map((user) => user?.roles), ), })); diff --git a/app/components/system_header/index.tsx b/app/components/system_header/index.tsx index 4337d7ec47..4fd460b8c3 100644 --- a/app/components/system_header/index.tsx +++ b/app/components/system_header/index.tsx @@ -1,25 +1,24 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import React from 'react'; import {View} from 'react-native'; -import {map, switchMap} from 'rxjs/operators'; +import {map} from 'rxjs/operators'; import FormattedText from '@components/formatted_text'; import FormattedTime from '@components/formatted_time'; import {Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {getPreferenceAsBool} from '@helpers/api/preference'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {observeConfig} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; import {getUserTimezone} from '@utils/user'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; import type UserModel from '@typings/database/models/servers/user'; type Props = { @@ -30,8 +29,6 @@ type Props = { user: UserModel; } -const {SERVER: {PREFERENCE, SYSTEM, USER}} = MM_TABLES; - const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { return { displayName: { @@ -83,17 +80,13 @@ const SystemHeader = ({isMilitaryTime, isTimezoneEnabled, createAt, theme, user} }; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { - const config = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG); - const preferences = database.get(PREFERENCE).query( - Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS), Q.where('name', 'use_military_time'), - ).observe(); - const isTimezoneEnabled = config.pipe(map(({value}: {value: ClientConfig}) => value.ExperimentalTimezone === 'true')); + const config = observeConfig(database); + const preferences = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time').observe(); + const isTimezoneEnabled = config.pipe(map((cfg) => cfg?.ExperimentalTimezone === 'true')); const isMilitaryTime = preferences.pipe( map((prefs) => getPreferenceAsBool(prefs, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', false)), ); - const user = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => database.get(USER).findAndObserve(value)), - ); + const user = observeCurrentUser(database); return { isMilitaryTime, diff --git a/app/components/team_sidebar/add_team/team_list_item/index.ts b/app/components/team_sidebar/add_team/team_list_item/index.ts index e091c844c3..66ac27ccd4 100644 --- a/app/components/team_sidebar/add_team/team_list_item/index.ts +++ b/app/components/team_sidebar/add_team/team_list_item/index.ts @@ -3,21 +3,15 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeCurrentUserId} from '@queries/servers/system'; import TeamListItem from './team_list_item'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; - -const {SERVER: {SYSTEM}} = MM_TABLES; const withSystem = withObservables([], ({database}: WithDatabaseArgs) => ({ - currentUserId: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap((currentUserId) => of$(currentUserId.value))), + currentUserId: observeCurrentUserId(database), })); export default withDatabase(withSystem(TeamListItem)); diff --git a/app/components/team_sidebar/index.ts b/app/components/team_sidebar/index.ts index 023487ba26..db57595bd9 100644 --- a/app/components/team_sidebar/index.ts +++ b/app/components/team_sidebar/index.ts @@ -1,45 +1,33 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {Permissions} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {queryRolesByNames} from '@queries/servers/role'; +import {queryMyTeams, queryOtherTeams} from '@queries/servers/team'; +import {observeCurrentUser} from '@queries/servers/user'; import {hasPermission} from '@utils/role'; import TeamSidebar from './team_sidebar'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type MyTeam from '@typings/database/models/servers/my_team'; -import type RoleModel from '@typings/database/models/servers/role'; -import type SystemModel from '@typings/database/models/servers/system'; -import type TeamModel from '@typings/database/models/servers/team'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {SYSTEM, MY_TEAM, TEAM, USER, ROLE}} = MM_TABLES; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { - const currentUser = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => database.get(USER).findAndObserve(value)), - ); - const rolesArray = currentUser.pipe( - switchMap((u) => of$(u.roles.split(' '))), - ); - const roles = rolesArray.pipe( - switchMap((values) => database.get(ROLE).query(Q.where('name', Q.oneOf(values))).observe()), + const canCreateTeams = observeCurrentUser(database).pipe( + switchMap((u) => (u ? of$(u.roles.split(' ')) : of$([]))), + switchMap((values) => queryRolesByNames(database, values).observe()), + switchMap((r) => of$(hasPermission(r, Permissions.CREATE_TEAM, false))), ); - const canCreateTeams = roles.pipe(switchMap((r) => of$(hasPermission(r, Permissions.CREATE_TEAM, false)))); - - const otherTeams = database.get(MY_TEAM).query().observe().pipe( + const otherTeams = queryMyTeams(database).observe().pipe( switchMap((mm) => { // eslint-disable-next-line max-nested-callbacks const ids = mm.map((m) => m.id); - return database.get(TEAM).query(Q.where('id', Q.notIn(ids))).observe(); + return queryOtherTeams(database, ids).observe(); }), ); diff --git a/app/components/team_sidebar/team_list/index.ts b/app/components/team_sidebar/team_list/index.ts index 212e93f7f2..ae3229943f 100644 --- a/app/components/team_sidebar/team_list/index.ts +++ b/app/components/team_sidebar/team_list/index.ts @@ -3,34 +3,25 @@ /* eslint-disable max-nested-callbacks */ -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$, combineLatest} from 'rxjs'; import {switchMap, map} from 'rxjs/operators'; import {Preferences} from '@constants'; -import {MM_TABLES} from '@constants/database'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {queryJoinedTeams, queryMyTeams} from '@queries/servers/team'; import TeamList from './team_list'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type MyTeamModel from '@typings/database/models/servers/my_team'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type TeamModel from '@typings/database/models/servers/team'; - -const {SERVER: {MY_TEAM, PREFERENCE, TEAM}} = MM_TABLES; const withTeams = withObservables([], ({database}: WithDatabaseArgs) => { - const myTeams = database.get(MY_TEAM).query().observe(); - const teamIds = database.get(TEAM).query( - Q.on(MY_TEAM, Q.where('id', Q.notEq(''))), - ).observe().pipe( + const myTeams = queryMyTeams(database).observe(); + const teamIds = queryJoinedTeams(database).observe().pipe( map((ts) => ts.map((t) => ({id: t.id, displayName: t.displayName}))), ); - const order = database.get(PREFERENCE).query( - Q.where('category', Preferences.TEAMS_ORDER), - ).observe().pipe( + const order = queryPreferencesByCategoryAndName(database, Preferences.TEAMS_ORDER).observe().pipe( switchMap((p) => (p.length ? of$(p[0].value.split(',')) : of$([]))), ); const myOrderedTeams = combineLatest([myTeams, order, teamIds]).pipe( diff --git a/app/components/team_sidebar/team_list/team_item/index.ts b/app/components/team_sidebar/team_list/team_item/index.ts index 77ac495d49..1a630b3d79 100644 --- a/app/components/team_sidebar/team_list/team_item/index.ts +++ b/app/components/team_sidebar/team_list/team_item/index.ts @@ -1,29 +1,25 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; -import {MyChannelModel} from '@database/models/server'; +import {queryMyChannelsByTeam} from '@queries/servers/channel'; +import {observeCurrentTeamId} from '@queries/servers/system'; import TeamItem from './team_item'; import type {WithDatabaseArgs} from '@typings/database/database'; import type MyTeamModel from '@typings/database/models/servers/my_team'; -import type SystemModel from '@typings/database/models/servers/system'; - -const {SERVER: {SYSTEM, MY_CHANNEL, CHANNEL}} = MM_TABLES; type WithTeamsArgs = WithDatabaseArgs & { myTeam: MyTeamModel; } const enhance = withObservables(['myTeam'], ({myTeam, database}: WithTeamsArgs) => { - const myChannels = database.get(MY_CHANNEL).query(Q.on(CHANNEL, Q.and(Q.where('delete_at', Q.eq(0)), Q.where('team_id', Q.eq(myTeam.id))))).observeWithColumns(['mentions_count', 'is_unread']); + const myChannels = queryMyChannelsByTeam(database, myTeam.id).observeWithColumns(['mentions_count', 'is_unread']); const mentionCount = myChannels.pipe( // eslint-disable-next-line max-nested-callbacks switchMap((val) => of$(val.reduce((acc, v) => acc + v.mentionsCount, 0))), @@ -32,12 +28,9 @@ const enhance = withObservables(['myTeam'], ({myTeam, database}: WithTeamsArgs) // eslint-disable-next-line max-nested-callbacks switchMap((val) => of$(val.reduce((acc, v) => acc || v.isUnread, false))), ); - const currentTeamId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe( - switchMap((t) => of$(t.value)), - ); return { - currentTeamId, + currentTeamId: observeCurrentTeamId(database), team: myTeam.team.observe(), mentionCount, hasUnreads, diff --git a/app/components/toast/index.tsx b/app/components/toast/index.tsx index 31c0ce9a1f..b4da4dbc24 100644 --- a/app/components/toast/index.tsx +++ b/app/components/toast/index.tsx @@ -5,9 +5,9 @@ import React, {useMemo} from 'react'; import {StyleProp, Text, useWindowDimensions, View, ViewStyle} from 'react-native'; import Animated, {AnimatedStyleProp} from 'react-native-reanimated'; -import {changeOpacity, makeStyleSheetFromTheme} from '@app/utils/theme'; import CompassIcon from '@components/compass_icon'; import {useTheme} from '@context/theme'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; type ToastProps = { diff --git a/app/constants/general.ts b/app/constants/general.ts index 00c69eb87d..5c389fa264 100644 --- a/app/constants/general.ts +++ b/app/constants/general.ts @@ -16,10 +16,10 @@ export default { DND: 'dnd', STATUS_COMMANDS: ['offline', 'away', 'online', 'dnd'], DEFAULT_CHANNEL: 'town-square', - DM_CHANNEL: 'D', - OPEN_CHANNEL: 'O', - PRIVATE_CHANNEL: 'P', - GM_CHANNEL: 'G', + DM_CHANNEL: 'D' as const, + OPEN_CHANNEL: 'O' as const, + PRIVATE_CHANNEL: 'P' as const, + GM_CHANNEL: 'G' as const, TEAMMATE_NAME_DISPLAY: { SHOW_USERNAME: 'username', SHOW_NICKNAME_FULLNAME: 'nickname_full_name', diff --git a/app/context/theme/index.tsx b/app/context/theme/index.tsx index e45e02b737..211d7acfb2 100644 --- a/app/context/theme/index.tsx +++ b/app/context/theme/index.tsx @@ -1,19 +1,17 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import withObservables from '@nozbe/with-observables'; import React, {ComponentType, createContext, useEffect} from 'react'; import {Appearance, EventSubscription} from 'react-native'; -import {of as of$} from 'rxjs'; -import {catchError, switchMap} from 'rxjs/operators'; import {Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {observeCurrentTeamId} from '@queries/servers/system'; import EphemeralStore from '@store/ephemeral_store'; import {setNavigationStackStyles, setThemeDefaults} from '@utils/theme'; -import type {PreferenceModel, SystemModel} from '@database/models/server'; +import type {PreferenceModel} from '@database/models/server'; import type Database from '@nozbe/watermelondb/Database'; type Props = { @@ -26,8 +24,6 @@ type WithThemeProps = { theme: Theme; } -const {SERVER: {PREFERENCE, SYSTEM}} = MM_TABLES; - export function getDefaultThemeByAppearance(): Theme { if (Appearance.getColorScheme() === 'dark') { return Preferences.THEMES.onyx; @@ -98,11 +94,8 @@ export function useTheme(): Theme { } const enhancedThemeProvider = withObservables([], ({database}: {database: Database}) => ({ - currentTeamId: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe( - switchMap((row) => of$(row.value)), - catchError(() => of$(undefined)), - ), - themes: database.get(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_THEME)).observeWithColumns(['value']), + currentTeamId: observeCurrentTeamId(database), + themes: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_THEME).observeWithColumns(['value']), })); export default enhancedThemeProvider(ThemeProvider); diff --git a/app/context/user_locale/index.tsx b/app/context/user_locale/index.tsx index e87271384c..4c420dffc8 100644 --- a/app/context/user_locale/index.tsx +++ b/app/context/user_locale/index.tsx @@ -5,14 +5,12 @@ import withObservables from '@nozbe/with-observables'; import React, {ComponentType, createContext} from 'react'; import {IntlProvider} from 'react-intl'; import {of as of$} from 'rxjs'; -import {catchError, switchMap} from 'rxjs/operators'; +import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {DEFAULT_LOCALE, getTranslations} from '@i18n'; +import {observeCurrentUser} from '@queries/servers/user'; -import type {SystemModel} from '@database/models/server'; import type Database from '@nozbe/watermelondb/Database'; -import type UserModel from '@typings/database/models/servers/user'; type Props = { locale: string; @@ -23,8 +21,6 @@ type WithUserLocaleProps = { locale: string; } -const {SERVER: {USER, SYSTEM}} = MM_TABLES; - export const UserLocaleContext = createContext(DEFAULT_LOCALE); const {Consumer, Provider} = UserLocaleContext; @@ -64,12 +60,8 @@ export function useUserLocale(): string { } const enhancedThemeProvider = withObservables([], ({database}: {database: Database}) => ({ - locale: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => database.get(USER).findAndObserve(value).pipe( - // eslint-disable-next-line max-nested-callbacks - switchMap((user) => of$(user.locale)), - )), - catchError(() => of$(DEFAULT_LOCALE)), + locale: observeCurrentUser(database).pipe( + switchMap((user) => of$(user?.locale || DEFAULT_LOCALE)), ), })); diff --git a/app/init/draft_upload_manager/index.test.ts b/app/init/draft_upload_manager/index.test.ts index eaca299aec..8a712db4c0 100644 --- a/app/init/draft_upload_manager/index.test.ts +++ b/app/init/draft_upload_manager/index.test.ts @@ -8,7 +8,7 @@ import {addFilesToDraft} from '@actions/local/draft'; import {PROGRESS_TIME_TO_STORE} from '@constants/files'; import DatabaseManager from '@database/manager'; import ServerDataOperator from '@database/operator/server_data_operator'; -import {queryDraft} from '@queries/servers/drafts'; +import {getDraft} from '@queries/servers/drafts'; import TestHelper from '@test/test_helper'; import {exportedForTesting} from '.'; @@ -94,7 +94,7 @@ describe('draft upload manager', () => { // Wait for other promises (on complete write) to finish await new Promise(process.nextTick); - const draft = await queryDraft(operator.database, channelId, rootId); + const draft = await getDraft(operator.database, channelId, rootId); expect(draft?.files.length).toBe(1); expect(draft?.files[0].id).toBe(fileServerId); @@ -121,7 +121,7 @@ describe('draft upload manager', () => { await new Promise(process.nextTick); // There has been progress, but we are not storing in to the database since the app is still active. - let draft = await queryDraft(operator.database, channelId, rootId); + let draft = await getDraft(operator.database, channelId, rootId); expect(draft?.files.length).toBe(1); expect(draft?.files[0].bytesRead).toBeUndefined(); @@ -131,7 +131,7 @@ describe('draft upload manager', () => { await new Promise(process.nextTick); // After a failure, we store the progress on the database, so we can resume from the point before failure. - draft = await queryDraft(operator.database, channelId, rootId); + draft = await getDraft(operator.database, channelId, rootId); expect(draft?.files.length).toBe(1); expect(draft?.files[0].bytesRead).toBe(bytesRead); expect(draft?.files[0].failed).toBe(true); @@ -196,7 +196,7 @@ describe('draft upload manager', () => { for (let i = 0; i < 3; i++) { // eslint-disable-next-line no-await-in-loop - const draft = await queryDraft(operator.database, channelIds[i], rootIds[i]); + const draft = await getDraft(operator.database, channelIds[i], rootIds[i]); expect(draft?.files.length).toBe(1); expect(draft?.files[0].bytesRead).toBe(bytesReads[i]); } @@ -213,7 +213,7 @@ describe('draft upload manager', () => { for (let i = 0; i < 3; i++) { // eslint-disable-next-line no-await-in-loop - const draft = await queryDraft(operator.database, channelIds[i], rootIds[i]); + const draft = await getDraft(operator.database, channelIds[i], rootIds[i]); expect(draft?.files.length).toBe(1); expect(draft?.files[0].bytesRead).toBe(bytesReads[i]); } @@ -231,7 +231,7 @@ describe('draft upload manager', () => { for (let i = 0; i < 3; i++) { // eslint-disable-next-line no-await-in-loop - const draft = await queryDraft(operator.database, channelIds[i], rootIds[i]); + const draft = await getDraft(operator.database, channelIds[i], rootIds[i]); expect(draft?.files.length).toBe(1); expect(draft?.files[0].bytesRead).toBe(bytesReadsStore[i]); } @@ -245,7 +245,7 @@ describe('draft upload manager', () => { for (let i = 0; i < 3; i++) { // eslint-disable-next-line no-await-in-loop - const draft = await queryDraft(operator.database, channelIds[i], rootIds[i]); + const draft = await getDraft(operator.database, channelIds[i], rootIds[i]); expect(draft?.files.length).toBe(1); expect(draft?.files[0].bytesRead).toBe(bytesReadsStore[i]); } @@ -270,7 +270,7 @@ describe('draft upload manager', () => { // Wait for other promises (on complete write) to finish await new Promise(process.nextTick); - const draft = await queryDraft(operator.database, channelId, rootId); + const draft = await getDraft(operator.database, channelId, rootId); expect(draft?.files.length).toBe(1); expect(draft?.files[0].id).toBeUndefined(); expect(draft?.files[0].failed).toBe(true); @@ -294,7 +294,7 @@ describe('draft upload manager', () => { // Wait for other promises (on complete write) to finish await new Promise(process.nextTick); - const draft = await queryDraft(operator.database, channelId, rootId); + const draft = await getDraft(operator.database, channelId, rootId); expect(draft?.files.length).toBe(1); expect(draft?.files[0].id).toBeUndefined(); expect(draft?.files[0].failed).toBe(true); @@ -318,7 +318,7 @@ describe('draft upload manager', () => { // Wait for other promises (on complete write) to finish await new Promise(process.nextTick); - const draft = await queryDraft(operator.database, channelId, rootId); + const draft = await getDraft(operator.database, channelId, rootId); expect(draft?.files.length).toBe(1); expect(draft?.files[0].id).toBeUndefined(); expect(draft?.files[0].failed).toBe(true); diff --git a/app/init/global_event_handler.ts b/app/init/global_event_handler.ts index 034b7a486f..6fc39ea408 100644 --- a/app/init/global_event_handler.ts +++ b/app/init/global_event_handler.ts @@ -16,7 +16,7 @@ import {getLaunchPropsFromDeepLink, relaunchApp} from '@init/launch'; import NetworkManager from '@init/network_manager'; import PushNotifications from '@init/push_notifications'; import WebsocketManager from '@init/websocket_manager'; -import {queryCurrentUser} from '@queries/servers/user'; +import {getCurrentUser} from '@queries/servers/user'; import EphemeralStore from '@store/ephemeral_store'; import {LaunchType} from '@typings/launch'; import {deleteFileCache} from '@utils/file'; @@ -158,7 +158,7 @@ class GlobalEventHandler { resetLocale = async () => { if (Object.keys(DatabaseManager.serverDatabases).length) { const serverDatabase = await DatabaseManager.getActiveServerDatabase(); - const user = await queryCurrentUser(serverDatabase!); + const user = await getCurrentUser(serverDatabase!); resetMomentLocale(user?.locale); } else { resetMomentLocale(); diff --git a/app/init/launch.ts b/app/init/launch.ts index 95b438ddf2..c2af77a970 100644 --- a/app/init/launch.ts +++ b/app/init/launch.ts @@ -9,8 +9,8 @@ import {appEntry, pushNotificationEntry, upgradeEntry} from '@actions/remote/ent import {Screens} from '@constants'; import DatabaseManager from '@database/manager'; import {getActiveServerUrl, getServerCredentials, removeServerCredentials} from '@init/credentials'; -import {queryThemeForCurrentTeam} from '@queries/servers/preference'; -import {queryCurrentUserId} from '@queries/servers/system'; +import {getThemeForCurrentTeam} from '@queries/servers/preference'; +import {getCurrentUserId} from '@queries/servers/system'; import {goToScreen, resetToHome, resetToSelectServer} from '@screens/navigation'; import EphemeralStore from '@store/ephemeral_store'; import {DeepLinkChannel, DeepLinkDM, DeepLinkGM, DeepLinkPermalink, DeepLinkType, DeepLinkWithData, LaunchProps, LaunchType} from '@typings/launch'; @@ -81,8 +81,8 @@ const launchApp = async (props: LaunchProps, resetNavigation = true) => { const database = DatabaseManager.serverDatabases[serverUrl]?.database; let hasCurrentUser = false; if (database) { - EphemeralStore.theme = await queryThemeForCurrentTeam(database); - const currentUserId = await queryCurrentUserId(database); + EphemeralStore.theme = await getThemeForCurrentTeam(database); + const currentUserId = await getCurrentUserId(database); hasCurrentUser = Boolean(currentUserId); } diff --git a/app/init/push_notifications.ts b/app/init/push_notifications.ts index 21932bce66..f060333875 100644 --- a/app/init/push_notifications.ts +++ b/app/init/push_notifications.ts @@ -22,7 +22,7 @@ import DatabaseManager from '@database/manager'; import {DEFAULT_LOCALE, getLocalizedMessage, t} from '@i18n'; import NativeNotifications from '@notifications'; import {queryServerName} from '@queries/app/servers'; -import {queryCurrentChannelId} from '@queries/servers/system'; +import {getCurrentChannelId} from '@queries/servers/system'; import {showOverlay} from '@screens/navigation'; import EphemeralStore from '@store/ephemeral_store'; import {isTablet} from '@utils/helpers'; @@ -134,7 +134,7 @@ class PushNotifications { const isTabletDevice = await isTablet(); const activeServerUrl = await getActiveServerUrl(); const displayName = await queryServerName(DatabaseManager.appDatabase!.database, serverUrl); - const channelId = await queryCurrentChannelId(database); + const channelId = await getCurrentChannelId(database); let serverName; if (serverUrl !== activeServerUrl && Object.keys(DatabaseManager.serverDatabases).length > 1) { serverName = displayName; diff --git a/app/init/websocket_manager.ts b/app/init/websocket_manager.ts index d5f7dba486..37d8d50d24 100644 --- a/app/init/websocket_manager.ts +++ b/app/init/websocket_manager.ts @@ -12,7 +12,7 @@ import {handleClose, handleEvent, handleFirstConnect, handleReconnect} from '@ac import WebSocketClient from '@client/websocket'; import {General} from '@constants'; import DatabaseManager from '@database/manager'; -import {queryCurrentUserId} from '@queries/servers/system'; +import {getCurrentUserId} from '@queries/servers/system'; import {queryAllUsers} from '@queries/servers/user'; import type {ServerCredential} from '@typings/credentials'; @@ -157,13 +157,8 @@ class WebsocketManager { return; } - const currentUserId = await queryCurrentUserId(database.database); - const users = await queryAllUsers(database.database); - - const userIds = users.map((u) => u.id).filter((id) => id !== currentUserId); - if (!userIds.length) { - return; - } + const currentUserId = await getCurrentUserId(database.database); + const userIds = (await queryAllUsers(database.database).fetchIds()).filter((id) => id !== currentUserId); fetchStatusByIds(serverUrl, userIds); }; diff --git a/app/queries/servers/categories.ts b/app/queries/servers/categories.ts index 3cad2d35c4..1762c99112 100644 --- a/app/queries/servers/categories.ts +++ b/app/queries/servers/categories.ts @@ -10,7 +10,7 @@ import type CategoryModel from '@typings/database/models/servers/category'; const {SERVER: {CATEGORY}} = MM_TABLES; -export const queryCategoryById = async (database: Database, categoryId: string) => { +export const getCategoryById = async (database: Database, categoryId: string) => { try { const record = (await database.collections.get(CATEGORY).find(categoryId)); return record; @@ -19,61 +19,12 @@ export const queryCategoryById = async (database: Database, categoryId: string) } }; -export const queryCategoriesById = async (database: Database, categoryIds: string[]): Promise => { - try { - const records = (await database.get(CATEGORY).query(Q.where('id', Q.oneOf(categoryIds))).fetch()); - return records; - } catch { - return Promise.resolve([] as CategoryModel[]); - } +export const queryCategoriesById = (database: Database, categoryIds: string[]) => { + return database.get(CATEGORY).query(Q.where('id', Q.oneOf(categoryIds))); }; -export const queryCategoriesByType = async (database: Database, type: CategoryType): Promise => { - try { - const records = (await database.get(CATEGORY).query(Q.where('type', type)).fetch()); - return records; - } catch { - return Promise.resolve([] as CategoryModel[]); - } -}; - -export const queryCategoriesByTeamId = async (database: Database, teamId: string): Promise => { - try { - const records = (await database.get(CATEGORY).query(Q.where('team_id', teamId)).fetch()); - return records; - } catch { - return Promise.resolve([] as CategoryModel[]); - } -}; - -export const queryCategoriesByTeamIds = async (database: Database, teamIds: string[]): Promise => { - try { - const records = (await database.get(CATEGORY).query(Q.where('team_id', Q.oneOf(teamIds))).fetch()); - return records; - } catch { - return Promise.resolve([] as CategoryModel[]); - } -}; - -export const queryCategoriesByTypeTeamId = async (database: Database, type: CategoryType, teamId: string): Promise => { - try { - const records = await database.get(CATEGORY).query( - Q.where('team_id', teamId), - Q.where('type', type), - ).fetch(); - return records; - } catch { - return Promise.resolve([] as CategoryModel[]); - } -}; - -export const queryAllCategories = async (database: Database): Promise => { - try { - const records = await database.get(CATEGORY).query().fetch(); - return records; - } catch { - return Promise.resolve([] as CategoryModel[]); - } +export const queryCategoriesByTeamIds = (database: Database, teamIds: string[]) => { + return database.get(CATEGORY).query(Q.where('team_id', Q.oneOf(teamIds))); }; export const prepareCategories = (operator: ServerDataOperator, categories: CategoryWithChannels[]) => { diff --git a/app/queries/servers/channel.ts b/app/queries/servers/channel.ts index 48a9c03088..a13defe6d8 100644 --- a/app/queries/servers/channel.ts +++ b/app/queries/servers/channel.ts @@ -2,6 +2,8 @@ // See LICENSE.txt for license information. import {Database, Model, Q, Query, Relation} from '@nozbe/watermelondb'; +import {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; import {General, Permissions} from '@constants'; import {MM_TABLES} from '@constants/database'; @@ -9,14 +11,16 @@ import {hasPermission} from '@utils/role'; import {prepareDeletePost} from './post'; import {queryRoles} from './role'; -import {queryCurrentChannelId} from './system'; +import {observeCurrentChannelId, getCurrentChannelId} from './system'; import type ServerDataOperator from '@database/operator/server_data_operator'; import type ChannelModel from '@typings/database/models/servers/channel'; import type ChannelInfoModel from '@typings/database/models/servers/channel_info'; import type MyChannelModel from '@typings/database/models/servers/my_channel'; +import type MyChannelSettingsModel from '@typings/database/models/servers/my_channel_settings'; +import type UserModel from '@typings/database/models/servers/user'; -const {SERVER: {CHANNEL, MY_CHANNEL, CHANNEL_MEMBERSHIP}} = MM_TABLES; +const {SERVER: {CHANNEL, MY_CHANNEL, CHANNEL_MEMBERSHIP, MY_CHANNEL_SETTINGS, CHANNEL_INFO, USER}} = MM_TABLES; export function prepareMissingChannelsForAllTeams(operator: ServerDataOperator, channels: Channel[], channelMembers: ChannelMembership[]): Array> | undefined { const channelInfos: ChannelInfo[] = []; @@ -47,7 +51,7 @@ export function prepareMissingChannelsForAllTeams(operator: ServerDataOperator, } export const prepareMyChannelsForTeam = async (operator: ServerDataOperator, teamId: string, channels: Channel[], channelMembers: ChannelMembership[]) => { - const allChannelsForTeam = await queryAllChannelsForTeam(operator.database, teamId); + const allChannelsForTeam = await queryAllChannelsForTeam(operator.database, teamId).fetch(); const channelInfos: ChannelInfo[] = []; const memberships = channelMembers.map((cm) => ({...cm, id: cm.channel_id})); @@ -129,14 +133,14 @@ export const prepareDeleteChannel = async (channel: ChannelModel): Promise { - return database.get(CHANNEL).query(Q.where('team_id', teamId)).fetch(); + return database.get(CHANNEL).query(Q.where('team_id', teamId)); }; -export const queryAllMyChannelIds = (database: Database) => { - return database.get(MY_CHANNEL).query().fetchIds(); +export const queryAllMyChannel = (database: Database) => { + return database.get(MY_CHANNEL).query(); }; -export const queryMyChannel = async (database: Database, channelId: string) => { +export const getMyChannel = async (database: Database, channelId: string) => { try { const member = await database.get(MY_CHANNEL).find(channelId); return member; @@ -145,7 +149,13 @@ export const queryMyChannel = async (database: Database, channelId: string) => { } }; -export const queryChannelById = async (database: Database, channelId: string) => { +export const observeMyChannel = (database: Database, channelId: string) => { + return database.get(MY_CHANNEL).query(Q.where('id', channelId), Q.take(1)).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$(undefined))), + ); +}; + +export const getChannelById = async (database: Database, channelId: string) => { try { const channel = await database.get(CHANNEL).find(channelId); return channel; @@ -154,32 +164,30 @@ export const queryChannelById = async (database: Database, channelId: string) => } }; -export const queryChannelByName = async (database: Database, channelName: string) => { - try { - const channels = await database.get(CHANNEL).query(Q.where('name', channelName)).fetch() as ChannelModel[]; - if (channels.length) { - return channels[0]; - } - - return undefined; - } catch { - return undefined; - } +export const observeChannel = (database: Database, channelId: string) => { + return database.get(CHANNEL).query(Q.where('id', channelId), Q.take(1)).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$(undefined))), + ); }; -export const queryChannelsById = async (database: Database, channelIds: string[]): Promise => { - try { - const channels = (await database.get(CHANNEL).query(Q.where('id', Q.oneOf(channelIds))).fetch()) as ChannelModel[]; - return channels; - } catch { - return undefined; +export const getChannelByName = async (database: Database, channelName: string) => { + const channels = await database.get(CHANNEL).query(Q.where('name', channelName)).fetch(); + + // Check done to force types + if (channels.length) { + return channels[0]; } + return undefined; }; -export const queryDefaultChannelForTeam = async (database: Database, teamId: string) => { +export const queryChannelsById = (database: Database, channelIds: string[]) => { + return database.get(CHANNEL).query(Q.where('id', Q.oneOf(channelIds))); +}; + +export const getDefaultChannelForTeam = async (database: Database, teamId: string) => { let channel: ChannelModel|undefined; let canIJoinPublicChannelsInTeam = false; - const roles = await queryRoles(database); + const roles = await queryRoles(database).fetch(); if (roles.length) { canIJoinPublicChannelsInTeam = hasPermission(roles, Permissions.JOIN_PUBLIC_CHANNELS, true); @@ -207,18 +215,23 @@ export const queryDefaultChannelForTeam = async (database: Database, teamId: str return channel; }; -export const queryCurrentChannel = async (database: Database) => { - const currentChannelId = await queryCurrentChannelId(database); +export const getCurrentChannel = async (database: Database) => { + const currentChannelId = await getCurrentChannelId(database); if (currentChannelId) { - const channels = await queryChannelsById(database, [currentChannelId]); - if (channels?.length) { - return channels[0]; - } + return getChannelById(database, currentChannelId); } return undefined; }; +export const observeCurrentChannel = (database: Database) => { + return observeCurrentChannelId(database).pipe( + switchMap((id) => database.get(CHANNEL).query(Q.where('id', id), Q.take(1)).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$(undefined))), + ), + )); +}; + export const deleteChannelMembership = async (operator: ServerDataOperator, userId: string, channelId: string, prepareRecordsOnly = false) => { try { const channelMembership = await operator.database.get(CHANNEL_MEMBERSHIP).query(Q.where('user_id', Q.eq(userId)), Q.where('channel_id', Q.eq(channelId))).fetch(); @@ -244,6 +257,49 @@ export const addChannelMembership = async (operator: ServerDataOperator, userId: } }; -export const queryAllMyChannelMembershipIds = async (database: Database, userId: string) => { - return database.get(CHANNEL_MEMBERSHIP).query(Q.where('user_id', Q.eq(userId))).fetchIds(); +export const queryUsersOnChannel = (database: Database, channelId: string) => { + return database.get(USER).query(Q.on(CHANNEL_MEMBERSHIP, Q.where('channel_id', channelId))); +}; + +export const queryChannelsByTypes = (database: Database, channelTypes: ChannelType[]) => { + return database.get(CHANNEL).query( + Q.where('type', Q.oneOf(channelTypes))); +}; + +export const queryUserChannelsByTypes = (database: Database, userId: string, channelTypes: ChannelType[]) => { + return database.get(CHANNEL).query( + Q.where('type', Q.oneOf(channelTypes)), + Q.on(CHANNEL_MEMBERSHIP, Q.where('user_id', userId))); +}; + +export const queryTeamDefaultChannel = (database: Database, teamId: string) => { + return database.get(MM_TABLES.SERVER.CHANNEL).query( + Q.where('team_id', teamId), + Q.where('name', General.DEFAULT_CHANNEL), + ); +}; + +export const queryMyChannelsByTeam = (database: Database, teamId: string, includeDeleted = false) => { + const conditions: Q.Condition[] = [Q.where('team_id', Q.eq(teamId))]; + if (!includeDeleted) { + conditions.push(Q.where('delete_at', Q.eq(0))); + } + return database.get(MY_CHANNEL).query( + Q.on(CHANNEL, Q.and( + ...conditions, + )), + ); +}; + +export const observeChannelInfo = (database: Database, channelId: string) => { + return database.get(CHANNEL_INFO).query(Q.where('id', channelId), Q.take(1)).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$(undefined))), + ); +}; + +export const queryMyChannelSettingsByIds = (database: Database, ids: string[]) => { + return database.get(MY_CHANNEL_SETTINGS). + query( + Q.where('id', Q.oneOf(ids)), + ); }; diff --git a/app/queries/servers/custom_emoji.ts b/app/queries/servers/custom_emoji.ts index 3710985400..1a815cfc34 100644 --- a/app/queries/servers/custom_emoji.ts +++ b/app/queries/servers/custom_emoji.ts @@ -7,18 +7,10 @@ import {MM_TABLES} from '@constants/database'; import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji'; -export const queryAllCustomEmojis = async (database: Database): Promise => { - try { - return database.get(MM_TABLES.SERVER.CUSTOM_EMOJI).query().fetch(); - } catch { - return []; - } +export const queryAllCustomEmojis = (database: Database) => { + return database.get(MM_TABLES.SERVER.CUSTOM_EMOJI).query(); }; -export const queryCustomEmojisByName = async (database: Database, names: string[]): Promise => { - try { - return database.get(MM_TABLES.SERVER.CUSTOM_EMOJI).query(Q.where('name', Q.oneOf(names))).fetch(); - } catch { - return []; - } +export const queryCustomEmojisByName = (database: Database, names: string[]) => { + return database.get(MM_TABLES.SERVER.CUSTOM_EMOJI).query(Q.where('name', Q.oneOf(names))); }; diff --git a/app/queries/servers/drafts.ts b/app/queries/servers/drafts.ts index d0829915b4..b18b327c50 100644 --- a/app/queries/servers/drafts.ts +++ b/app/queries/servers/drafts.ts @@ -9,14 +9,19 @@ import type DraftModel from '@typings/database/models/servers/draft'; const {SERVER: {DRAFT}} = MM_TABLES; -export const queryDraft = async (database: Database, channelId: string, rootId = '') => { - try { - const record = await database.collections.get(DRAFT).query( - Q.where('channel_id', channelId), - Q.where('root_id', rootId), - ).fetch(); - return record?.[0]; - } catch { - return undefined; +export const getDraft = async (database: Database, channelId: string, rootId = '') => { + const record = await queryDraft(database, channelId, rootId).fetch(); + + // Check done to force types + if (record.length) { + return record[0]; } + return undefined; +}; + +export const queryDraft = (database: Database, channelId: string, rootId = '') => { + return database.collections.get(DRAFT).query( + Q.where('channel_id', channelId), + Q.where('root_id', rootId), + ); }; diff --git a/app/queries/servers/post.ts b/app/queries/servers/post.ts index 580c255c01..190388cea2 100644 --- a/app/queries/servers/post.ts +++ b/app/queries/servers/post.ts @@ -2,6 +2,8 @@ // See LICENSE.txt for license information. import {Database, Model, Q, Query, Relation} from '@nozbe/watermelondb'; +import {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; import {MM_TABLES} from '@constants/database'; @@ -38,78 +40,107 @@ export const prepareDeletePost = async (post: PostModel): Promise => { return preparedModels; }; -export const queryPostById = async (database: Database, postId: string) => { +export const getPostById = async (database: Database, postId: string) => { try { - const userRecord = (await database.collections.get(MM_TABLES.SERVER.POST).find(postId)) as PostModel; - return userRecord; + const postModel = await database.get(POST).find(postId); + return postModel; } catch { return undefined; } }; -export const queryPostsInChannel = (database: Database, channelId: string): Promise => { - try { - return database.get(POSTS_IN_CHANNEL).query( - Q.where('channel_id', channelId), - Q.sortBy('latest', Q.desc), - ).fetch(); - } catch { - return Promise.resolve([]); - } +export const observePost = (database: Database, postId: string) => { + return database.get(POST).query(Q.where('id', postId), Q.take(1)).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$(undefined))), + ); }; -export const queryPostsInThread = (database: Database, rootId: string): Promise => { - try { - return database.get(POSTS_IN_THREAD).query( - Q.where('root_id', rootId), - Q.sortBy('latest', Q.desc), - ).fetch(); - } catch { - return Promise.resolve([]); - } +export const queryPostsInChannel = (database: Database, channelId: string) => { + return database.get(POSTS_IN_CHANNEL).query( + Q.where('channel_id', channelId), + Q.sortBy('latest', Q.desc), + ); }; -export const queryRecentPostsInThread = async (database: Database, rootId: string): Promise => { - try { - const chunks = await queryPostsInThread(database, rootId); - if (chunks.length) { - const recent = chunks[0]; - const post = await queryPostById(database, rootId); - if (post) { - return queryPostsChunk(database, post.channelId, recent.earliest, recent.latest); - } +export const queryPostsInThread = (database: Database, rootId: string, sorted = false, includeDeleted = false) => { + const clauses: Q.Clause[] = [Q.where('root_id', rootId)]; + if (!includeDeleted) { + clauses.unshift(Q.experimentalJoinTables([POST])); + clauses.push(Q.on(POST, 'delete_at', Q.eq(0))); + } + + if (sorted) { + clauses.push(Q.sortBy('latest', Q.desc)); + } + return database.get(POSTS_IN_THREAD).query(...clauses); +}; + +export const getRecentPostsInThread = async (database: Database, rootId: string) => { + const chunks = await queryPostsInThread(database, rootId, true, true).fetch(); + if (chunks.length) { + const recent = chunks[0]; + const post = await getPostById(database, rootId); + if (post) { + return queryPostsChunk(database, post.channelId, recent.earliest, recent.latest).fetch(); } - return Promise.resolve([]); - } catch { - return Promise.resolve([]); } + return []; }; -export const queryPostsChunk = (database: Database, channelId: string, earliest: number, latest: number): Promise => { - try { - return database.get(POST).query( - Q.and( - Q.where('channel_id', channelId), - Q.where('create_at', Q.between(earliest, latest)), - Q.where('delete_at', Q.eq(0)), - ), - Q.sortBy('create_at', Q.desc), - ).fetch() as Promise; - } catch { - return Promise.resolve([]); +export const queryPostsChunk = (database: Database, id: string, earliest: number, latest: number, inThread = false, includeDeleted = false) => { + const conditions: Q.Condition[] = [Q.where('create_at', Q.between(earliest, latest))]; + if (inThread) { + conditions.push(Q.where('root_id', id)); + } else { + conditions.push(Q.where('channel_id', id)); } + + if (!includeDeleted) { + conditions.push(Q.where('delete_at', Q.eq(0))); + } + + return database.get(POST).query( + Q.and( + ...conditions, + ), + Q.sortBy('create_at', Q.desc), + ); }; -export const queryRecentPostsInChannel = async (database: Database, channelId: string): Promise => { - try { - const chunks = await queryPostsInChannel(database, channelId); - if (chunks.length) { - const recent = chunks[0]; - return queryPostsChunk(database, channelId, recent.earliest, recent.latest); - } - return Promise.resolve([]); - } catch { - return Promise.resolve([]); +export const getRecentPostsInChannel = async (database: Database, channelId: string, includeDeleted = false) => { + const chunks = await queryPostsInChannel(database, channelId).fetch(); + if (chunks.length) { + const recent = chunks[0]; + return queryPostsChunk(database, channelId, recent.earliest, recent.latest, false, includeDeleted).fetch(); } + return []; }; +export const queryPostsById = (database: Database, postIds: string[], sort?: Q.SortOrder) => { + const clauses: Q.Clause[] = [Q.where('id', Q.oneOf(postIds))]; + if (sort) { + clauses.push(Q.sortBy('create_at', sort)); + } + return database.get(POST).query(...clauses); +}; + +export const queryPostsBetween = (database: Database, earliest: number, latest: number, sort: Q.SortOrder | null, userId?: string, channelId?: string, rootId?: string) => { + const andClauses = [Q.where('create_at', Q.between(earliest, latest))]; + if (channelId) { + andClauses.push(Q.where('channel_id', channelId)); + } + + if (userId) { + andClauses.push(Q.where('user_id', userId)); + } + + if (rootId) { + andClauses.push(Q.where('root_id', rootId)); + } + + const clauses: Q.Clause[] = [Q.and(...andClauses)]; + if (sort != null) { + clauses.push(Q.sortBy('create_at', sort)); + } + return database.get(POST).query(...clauses); +}; diff --git a/app/queries/servers/preference.ts b/app/queries/servers/preference.ts index 3290f04dc9..3f90418369 100644 --- a/app/queries/servers/preference.ts +++ b/app/queries/servers/preference.ts @@ -7,7 +7,7 @@ import {Preferences} from '@constants'; import {MM_TABLES} from '@constants/database'; import {ServerDatabase} from '@typings/database/database'; -import {queryCurrentTeamId} from './system'; +import {getCurrentTeamId} from './system'; import type ServerDataOperator from '@database/operator/server_data_operator'; import type PreferenceModel from '@typings/database/models/servers/preference'; @@ -24,19 +24,20 @@ export const prepareMyPreferences = (operator: ServerDataOperator, preferences: } }; -export const queryPreferencesByCategoryAndName = (database: Database, category: string, name: string) => { - return database. - get(MM_TABLES.SERVER.PREFERENCE). - query( - Q.where('category', category), - Q.where('name', name), - ). - fetch(); +export const queryPreferencesByCategoryAndName = (database: Database, category: string, name?: string, value?: string) => { + const clauses = [Q.where('category', category)]; + if (name != null) { + clauses.push(Q.where('name', name)); + } + if (value != null) { + clauses.push(Q.where('value', value)); + } + return database.get(MM_TABLES.SERVER.PREFERENCE).query(...clauses); }; -export const queryThemeForCurrentTeam = async (database: Database) => { - const currentTeamId = await queryCurrentTeamId(database); - const teamTheme = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_THEME, currentTeamId); +export const getThemeForCurrentTeam = async (database: Database) => { + const currentTeamId = await getCurrentTeamId(database); + const teamTheme = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_THEME, currentTeamId).fetch(); if (teamTheme.length) { try { return JSON.parse(teamTheme[0].value) as Theme; @@ -52,7 +53,7 @@ export const deletePreferences = async (database: ServerDatabase, preferences: P try { const preparedModels: Model[] = []; for await (const pref of preferences) { - const myPrefs = await queryPreferencesByCategoryAndName(database.database, pref.category, pref.name); + const myPrefs = await queryPreferencesByCategoryAndName(database.database, pref.category, pref.name).fetch(); for (const p of myPrefs) { preparedModels.push(p.prepareDestroyPermanently()); } diff --git a/app/queries/servers/reactions.ts b/app/queries/servers/reactions.ts new file mode 100644 index 0000000000..8b71766184 --- /dev/null +++ b/app/queries/servers/reactions.ts @@ -0,0 +1,15 @@ +// 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'; +const {SERVER: {REACTION}} = MM_TABLES; + +export const queryReaction = (database: Database, emojiName: string, postId: string, userId: string) => { + return database.get(REACTION).query( + Q.where('emoji_name', emojiName), + Q.where('post_id', postId), + Q.where('user_id', userId), + ); +}; diff --git a/app/queries/servers/role.ts b/app/queries/servers/role.ts index b6ab08dc2d..e9aa72c1a0 100644 --- a/app/queries/servers/role.ts +++ b/app/queries/servers/role.ts @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Database} from '@nozbe/watermelondb'; +import {Database, Q} from '@nozbe/watermelondb'; import {Database as DatabaseConstants} from '@constants'; @@ -9,12 +9,11 @@ import type RoleModel from '@typings/database/models/servers/role'; const {ROLE} = DatabaseConstants.MM_TABLES.SERVER; -export const queryRoles = async (database: Database) => { - const roles = await database.collections.get(ROLE).query().fetch(); - return roles; +export const queryRoles = (database: Database) => { + return database.collections.get(ROLE).query(); }; -export const queryRoleById = async (database: Database, roleId: string): Promise => { +export const getRoleById = async (database: Database, roleId: string): Promise => { try { const role = (await database.get(ROLE).find(roleId)); return role; @@ -22,3 +21,7 @@ export const queryRoleById = async (database: Database, roleId: string): Promise return undefined; } }; + +export const queryRolesByNames = (database: Database, names: string[]) => { + return database.get(ROLE).query(Q.where('name', Q.oneOf(names))); +}; diff --git a/app/queries/servers/system.ts b/app/queries/servers/system.ts index 230cc1782c..38813b06ac 100644 --- a/app/queries/servers/system.ts +++ b/app/queries/servers/system.ts @@ -1,7 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Database} from '@nozbe/watermelondb'; +import {Database, Q} from '@nozbe/watermelondb'; +import {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; @@ -19,34 +21,62 @@ export type PrepareCommonSystemValuesArgs = { const {SERVER: {SYSTEM}} = MM_TABLES; -export const queryCurrentChannelId = async (serverDatabase: Database) => { +export const getCurrentChannelId = async (serverDatabase: Database): Promise => { try { - const currentChannelId = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID) as SystemModel; - return (currentChannelId?.value || '') as string; + const currentChannelId = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID); + return currentChannelId?.value || ''; } catch { return ''; } }; -export const queryCurrentTeamId = async (serverDatabase: Database) => { +export const querySystemValue = (database: Database, key: string) => { + return database.get(SYSTEM).query(Q.where('id', (key)), Q.take(1)); +}; + +export const observeCurrentChannelId = (database: Database) => { + return querySystemValue(database, SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$({value: ''}))), + ).pipe( + switchMap((model) => of$(model.value as string)), + ); +}; + +export const getCurrentTeamId = async (serverDatabase: Database): Promise => { try { - const currentTeamId = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID) as SystemModel; - return (currentTeamId?.value || '') as string; + const currentTeamId = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID); + return currentTeamId?.value || ''; } catch { return ''; } }; -export const queryCurrentUserId = async (serverDatabase: Database) => { +export const observeCurrentTeamId = (database: Database) => { + return querySystemValue(database, SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$({value: ''}))), + ).pipe( + switchMap((model) => of$(model.value as string)), + ); +}; + +export const getCurrentUserId = async (serverDatabase: Database): Promise => { try { - const currentUserId = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.CURRENT_USER_ID) as SystemModel; - return (currentUserId?.value || '') as string; + const currentUserId = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.CURRENT_USER_ID); + return currentUserId?.value || ''; } catch { return ''; } }; -export const queryCommonSystemValues = async (serverDatabase: Database) => { +export const observeCurrentUserId = (database: Database) => { + return querySystemValue(database, SYSTEM_IDENTIFIERS.CURRENT_USER_ID).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$({value: ''}))), + ).pipe( + switchMap((model) => of$(model.value as string)), + ); +}; + +export const getCommonSystemValues = async (serverDatabase: Database) => { const systemRecords = (await serverDatabase.collections.get(SYSTEM).query().fetch()) as SystemModel[]; let config = {}; let license = {}; @@ -82,44 +112,127 @@ export const queryCommonSystemValues = async (serverDatabase: Database) => { }; }; -export const queryConfig = async (serverDatabase: Database) => { +export const getConfig = async (serverDatabase: Database) => { try { - const config = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.CONFIG) as SystemModel; + const config = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.CONFIG); return (config?.value || {}) as ClientConfig; - } catch { - return {} as ClientConfig; - } -}; - -export const queryRecentCustomStatuses = async (serverDatabase: Database) => { - try { - const recent = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.RECENT_CUSTOM_STATUS); - return recent; } catch { return undefined; } }; -export const queryExpandedLinks = async (serverDatabase: Database) => { +export const observeConfig = (database: Database) => { + return querySystemValue(database, SYSTEM_IDENTIFIERS.CONFIG).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$({value: undefined}))), + ).pipe( + switchMap((model) => of$(model.value as ClientConfig | undefined)), + ); +}; + +export const observeConfigValue = (database: Database, key: keyof ClientConfig) => { + return observeConfig(database).pipe( + switchMap((cfg) => of$(cfg?.[key])), + ); +}; + +export const observeConfigBooleanValue = (database: Database, key: keyof ClientConfig) => { + return observeConfig(database).pipe( + switchMap((cfg) => of$(cfg?.[key] === 'true')), + ); +}; + +export const observeConfigIntValue = (database: Database, key: keyof ClientConfig, defaultValue = 0) => { + return observeConfig(database).pipe( + switchMap((cfg) => of$((parseInt(cfg?.[key] || '0', 10) || defaultValue))), + ); +}; + +export const observeLicense = (database: Database) => { + return querySystemValue(database, SYSTEM_IDENTIFIERS.LICENSE).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$({value: undefined}))), + ).pipe( + switchMap((model) => of$(model.value as ClientLicense | undefined)), + ); +}; + +export const getRecentCustomStatuses = async (database: Database) => { try { - const expandedLinks = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.EXPANDED_LINKS) as SystemModel; + const recent = await database.get(SYSTEM).find(SYSTEM_IDENTIFIERS.RECENT_CUSTOM_STATUS); + return recent.value as UserCustomStatus[]; + } catch { + return []; + } +}; + +export const getExpandedLinks = async (database: Database) => { + try { + const expandedLinks = await database.get(SYSTEM).find(SYSTEM_IDENTIFIERS.EXPANDED_LINKS); return (expandedLinks?.value || {}) as Record; } catch { return {}; } }; -export const queryWebSocketLastDisconnected = async (serverDatabase: Database) => { +export const observeExpandedLinks = (database: Database) => { + return querySystemValue(database, SYSTEM_IDENTIFIERS.EXPANDED_LINKS).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$({value: {}}))), + ).pipe( + switchMap((model) => of$(model.value as Record)), + ); +}; + +export const observeRecentMentions = (database: Database) => { + return querySystemValue(database, SYSTEM_IDENTIFIERS.RECENT_MENTIONS).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$({value: []}))), + ).pipe( + switchMap((model) => of$(model.value as string[])), + ); +}; + +export const getRecentReactions = async (database: Database) => { try { - const websocketLastDisconnected = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.WEBSOCKET) as SystemModel; + const reactions = await database.get(SYSTEM).find(SYSTEM_IDENTIFIERS.RECENT_REACTIONS); + return reactions.value as string[]; + } catch { + return []; + } +}; + +export const observeRecentReactions = (database: Database) => { + return querySystemValue(database, SYSTEM_IDENTIFIERS.RECENT_REACTIONS).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$({value: []}))), + ).pipe( + switchMap((model) => of$(model.value as string[])), + ); +}; + +export const observeRecentCustomStatus = (database: Database) => { + return querySystemValue(database, SYSTEM_IDENTIFIERS.RECENT_CUSTOM_STATUS).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$({value: []}))), + ).pipe( + switchMap((model) => of$(model.value as UserCustomStatus[])), + ); +}; + +export const getWebSocketLastDisconnected = async (serverDatabase: Database) => { + try { + const websocketLastDisconnected = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.WEBSOCKET); return (parseInt(websocketLastDisconnected?.value || 0, 10) || 0); } catch { return 0; } }; +export const observeWebsocket = (database: Database) => { + return querySystemValue(database, SYSTEM_IDENTIFIERS.WEBSOCKET).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$({value: '0'}))), + ).pipe( + switchMap((model) => of$(parseInt(model.value || 0, 10) || 0)), + ); +}; + export const resetWebSocketLastDisconnected = async (operator: ServerDataOperator, prepareRecordsOnly = false) => { - const lastDisconnectedAt = await queryWebSocketLastDisconnected(operator.database); + const lastDisconnectedAt = await getWebSocketLastDisconnected(operator.database); if (lastDisconnectedAt) { return operator.handleSystem({systems: [{ @@ -132,7 +245,7 @@ export const resetWebSocketLastDisconnected = async (operator: ServerDataOperato return []; }; -export const queryTeamHistory = async (serverDatabase: Database) => { +export const getTeamHistory = async (serverDatabase: Database) => { try { const teamHistory = await serverDatabase.get(SYSTEM).find(SYSTEM_IDENTIFIERS.TEAM_HISTORY); return (teamHistory.value) as string[]; @@ -226,3 +339,4 @@ export const setCurrentTeamAndChannelId = async (operator: ServerDataOperator, t return {error}; } }; + diff --git a/app/queries/servers/team.ts b/app/queries/servers/team.ts index 08e8f50bc5..3f0fd31030 100644 --- a/app/queries/servers/team.ts +++ b/app/queries/servers/team.ts @@ -2,6 +2,8 @@ // See LICENSE.txt for license information. import {Database, Model, Q, Query, Relation} from '@nozbe/watermelondb'; +import {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; import {Database as DatabaseConstants, Preferences} from '@constants'; import {getPreferenceValue} from '@helpers/api/preference'; @@ -9,10 +11,10 @@ import {selectDefaultTeam} from '@helpers/api/team'; import {DEFAULT_LOCALE} from '@i18n'; import {prepareDeleteCategory} from './categories'; -import {prepareDeleteChannel, queryDefaultChannelForTeam} from './channel'; +import {prepareDeleteChannel, getDefaultChannelForTeam} from './channel'; import {queryPreferencesByCategoryAndName} from './preference'; -import {patchTeamHistory, queryConfig, queryTeamHistory} from './system'; -import {queryCurrentUser} from './user'; +import {patchTeamHistory, getConfig, getTeamHistory, observeCurrentTeamId} from './system'; +import {getCurrentUser} from './user'; import type ServerDataOperator from '@database/operator/server_data_operator'; import type CategoryModel from '@typings/database/models/servers/category'; @@ -31,8 +33,8 @@ export const addChannelToTeamHistory = async (operator: ServerDataOperator, team if (!myChannel) { return []; } - const teamChannelHistory = (await operator.database.get(TEAM_CHANNEL_HISTORY).find(teamId)) as TeamChannelHistoryModel; - const channelIdSet = new Set(teamChannelHistory.channelIds); + const teamChannelHistory = await getTeamChannelHistory(operator.database, teamId); + const channelIdSet = new Set(teamChannelHistory); if (channelIdSet.has(channelId)) { channelIdSet.delete(channelId); } @@ -53,32 +55,44 @@ export const addChannelToTeamHistory = async (operator: ServerDataOperator, team return operator.handleTeamChannelHistory({teamChannelHistories: [tch], prepareRecordsOnly}); }; -export const queryNthLastChannelFromTeam = async (database: Database, teamId: string, n = 0) => { - const teamChannelHistory = await queryChannelHistory(database, teamId); - if (teamChannelHistory && teamChannelHistory.length > n + 1) { - return teamChannelHistory[n]; - } - - // No channel history for the team - const channel = await queryDefaultChannelForTeam(database, teamId); - return channel?.id || ''; -}; - -export const queryChannelHistory = async (database: Database, teamId: string) => { +export const getTeamChannelHistory = async (database: Database, teamId: string) => { try { - const teamChannelHistory = await database.get(TEAM_CHANNEL_HISTORY).find(teamId); - return teamChannelHistory.channelIds; + const history = await database.get(TEAM_CHANNEL_HISTORY).find(teamId); + return history.channelIds; } catch { return []; } }; +export const getNthLastChannelFromTeam = async (database: Database, teamId: string, n = 0) => { + let channelId = ''; + + try { + const teamChannelHistory = await getTeamChannelHistory(database, teamId); + if (teamChannelHistory.length > n + 1) { + channelId = teamChannelHistory[n]; + } + } catch { + //Do nothing + } + + if (!channelId) { + // No channel history for the team + const channel = await getDefaultChannelForTeam(database, teamId); + if (channel) { + channelId = channel.id; + } + } + + return channelId; +}; + export const removeChannelFromTeamHistory = async (operator: ServerDataOperator, teamId: string, channelId: string, prepareRecordsOnly = false) => { let tch: TeamChannelHistory; try { - const teamChannelHistory = (await operator.database.get(TEAM_CHANNEL_HISTORY).find(teamId)) as TeamChannelHistoryModel; - const channelIdSet = new Set(teamChannelHistory.channelIds); + const teamChannelHistory = await getTeamChannelHistory(operator.database, teamId); + const channelIdSet = new Set(teamChannelHistory); if (channelIdSet.has(channelId)) { channelIdSet.delete(channelId); } else { @@ -98,7 +112,7 @@ export const removeChannelFromTeamHistory = async (operator: ServerDataOperator, }; export const addTeamToTeamHistory = async (operator: ServerDataOperator, teamId: string, prepareRecordsOnly = false) => { - const teamHistory = (await queryTeamHistory(operator.database)); + const teamHistory = (await getTeamHistory(operator.database)); const teamHistorySet = new Set(teamHistory); if (teamHistorySet.has(teamId)) { teamHistorySet.delete(teamId); @@ -110,7 +124,7 @@ export const addTeamToTeamHistory = async (operator: ServerDataOperator, teamId: }; export const removeTeamFromTeamHistory = async (operator: ServerDataOperator, teamId: string, prepareRecordsOnly = false) => { - const teamHistory = (await queryTeamHistory(operator.database)); + const teamHistory = (await getTeamHistory(operator.database)); const teamHistorySet = new Set(teamHistory); if (!teamHistorySet.has(teamId)) { return undefined; @@ -122,13 +136,13 @@ export const removeTeamFromTeamHistory = async (operator: ServerDataOperator, te return patchTeamHistory(operator, teamIds, prepareRecordsOnly); }; -export const queryLastTeam = async (database: Database) => { - const teamHistory = (await queryTeamHistory(database)); +export const getLastTeam = async (database: Database) => { + const teamHistory = (await getTeamHistory(database)); if (teamHistory.length > 0) { return teamHistory[0]; } - return queryDefaultTeam(database); + return getDefaultTeamId(database); }; export const syncTeamTable = async (operator: ServerDataOperator, teams: Team[]) => { @@ -153,10 +167,10 @@ export const syncTeamTable = async (operator: ServerDataOperator, teams: Team[]) } }; -export const queryDefaultTeam = async (database: Database) => { - const user = await queryCurrentUser(database); - const config = await queryConfig(database); - const teamOrderPreferences = await queryPreferencesByCategoryAndName(database, Preferences.TEAMS_ORDER, ''); +export const getDefaultTeamId = async (database: Database) => { + const user = await getCurrentUser(database); + const config = await getConfig(database); + const teamOrderPreferences = await queryPreferencesByCategoryAndName(database, Preferences.TEAMS_ORDER, '').fetch(); let teamOrderPreference = ''; if (teamOrderPreferences.length) { teamOrderPreference = teamOrderPreferences[0].value; @@ -165,7 +179,7 @@ export const queryDefaultTeam = async (database: Database) => { const teamModels = await database.get(TEAM).query(Q.on(MY_TEAM, Q.where('id', Q.notEq('')))).fetch(); const teams = teamModels.map((t) => ({id: t.id, display_name: t.displayName, name: t.name} as Team)); - const defaultTeam = selectDefaultTeam(teams, user?.locale || DEFAULT_LOCALE, teamOrderPreference, config.ExperimentalPrimaryTeam); + const defaultTeam = selectDefaultTeam(teams, user?.locale || DEFAULT_LOCALE, teamOrderPreference, config?.ExperimentalPrimaryTeam); return defaultTeam?.id; }; @@ -264,65 +278,67 @@ export const prepareDeleteTeam = async (team: TeamModel): Promise => { } }; -export const queryMyTeamById = async (database: Database, teamId: string): Promise => { +export const getMyTeamById = async (database: Database, teamId: string) => { try { - const myTeam = (await database.get(MY_TEAM).find(teamId)) as MyTeamModel; + const myTeam = (await database.get(MY_TEAM).find(teamId)); return myTeam; } catch (err) { return undefined; } }; -export const queryTeamById = async (database: Database, teamId: string): Promise => { +export const getTeamById = async (database: Database, teamId: string) => { try { - const team = (await database.get(TEAM).find(teamId)) as TeamModel; + const team = (await database.get(TEAM).find(teamId)); return team; } catch { return undefined; } }; -export const queryTeamsById = async (database: Database, teamIds: string[]): Promise => { - try { - const teams = (await database.get(TEAM).query(Q.where('id', Q.oneOf(teamIds))).fetch()) as TeamModel[]; - return teams; - } catch { - return undefined; - } +export const observeTeam = (database: Database, teamId: string) => { + return database.get(TEAM).query(Q.where('id', teamId), Q.take(1)).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$(undefined))), + ); }; -export const queryMyTeamsById = async (database: Database, teamIds: string[]): Promise => { - try { - const teams = (await database.get(MY_TEAM).query(Q.where('id', Q.oneOf(teamIds))).fetch()); - return teams; - } catch { - return undefined; - } +export const queryTeamsById = (database: Database, teamIds: string[]) => { + return database.get(TEAM).query(Q.where('id', Q.oneOf(teamIds))); }; -export const queryTeamByName = async (database: Database, teamName: string): Promise => { - try { - const team = (await database.get(TEAM).query(Q.where('name', teamName)).fetch()) as TeamModel[]; - if (team.length) { - return team[0]; - } - - return undefined; - } catch { - return undefined; - } +export const queryTeamByName = async (database: Database, teamName: string) => { + return database.get(TEAM).query(Q.where('name', teamName)); }; -export const queryMyTeams = async (database: Database): Promise => { - try { - const teams = (await database.get(MY_TEAM).query().fetch()) as MyTeamModel[]; - return teams; - } catch { - return undefined; - } +export const queryOtherTeams = (database: Database, teamIds: string[]) => { + return database.get(TEAM).query(Q.where('id', Q.notIn(teamIds))); }; -export const queryAvailableTeamIds = async (database: Database, excludeTeamId: string, teams?: Team[], preferences?: PreferenceType[], locale?: string): Promise => { +export const queryJoinedTeams = (database: Database) => { + return database.get(TEAM).query( + Q.on(MY_TEAM, Q.where('id', Q.notEq(''))), + ); +}; + +export const getTeamByName = async (database: Database, teamName: string) => { + const teams = await database.get(TEAM).query(Q.where('name', teamName)).fetch(); + + // Check done to force types + if (teams.length) { + return teams[0]; + } + return undefined; +}; + +export const queryMyTeams = (database: Database) => { + return database.get(MY_TEAM).query(); +}; + +export const queryMyTeamsByIds = (database: Database, teamIds: string[]) => { + return database.get(MY_TEAM).query(Q.where('id', Q.oneOf(teamIds))); +}; + +export const getAvailableTeamIds = async (database: Database, excludeTeamId: string, teams?: Team[], preferences?: PreferenceType[], locale?: string): Promise => { let availableTeamIds: string[] = []; if (teams) { @@ -330,21 +346,27 @@ export const queryAvailableTeamIds = async (database: Database, excludeTeamId: s if (preferences) { teamOrderPreference = getPreferenceValue(preferences, Preferences.TEAMS_ORDER, '', '') as string; } else { - const dbPreferences = await queryPreferencesByCategoryAndName(database, Preferences.TEAMS_ORDER, ''); + const dbPreferences = await queryPreferencesByCategoryAndName(database, Preferences.TEAMS_ORDER, '').fetch(); teamOrderPreference = dbPreferences[0].value; } - const userLocale = locale || (await queryCurrentUser(database))?.locale; - const config = await queryConfig(database); - const defaultTeam = selectDefaultTeam(teams, userLocale, teamOrderPreference, config.ExperimentalPrimaryTeam); + const userLocale = locale || (await getCurrentUser(database))?.locale; + const config = await getConfig(database); + const defaultTeam = selectDefaultTeam(teams, userLocale, teamOrderPreference, config?.ExperimentalPrimaryTeam); - availableTeamIds = [defaultTeam!.id]; - } else { - const dbTeams = await queryMyTeams(database); - if (dbTeams) { - availableTeamIds = dbTeams.map((team) => team.id); + if (defaultTeam) { + availableTeamIds = [defaultTeam.id]; } + } else { + const dbTeams = await queryMyTeams(database).fetch(); + availableTeamIds = dbTeams.map((team) => team.id); } return availableTeamIds.filter((id) => id !== excludeTeamId); }; + +export const observeCurrentTeam = (database: Database) => { + return observeCurrentTeamId(database).pipe( + switchMap((id) => observeTeam(database, id)), + ); +}; diff --git a/app/queries/servers/user.ts b/app/queries/servers/user.ts index 3d1c4da2bf..8bf22553d5 100644 --- a/app/queries/servers/user.ts +++ b/app/queries/servers/user.ts @@ -2,57 +2,60 @@ // See LICENSE.txt for license information. import {Database, Q} from '@nozbe/watermelondb'; +import {combineLatest, of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; +import {Preferences} from '@constants'; import {MM_TABLES} from '@constants/database'; +import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; -import {queryCurrentUserId} from './system'; +import {queryPreferencesByCategoryAndName} from './preference'; +import {observeConfig, observeCurrentUserId, observeLicense, getCurrentUserId} from './system'; import type ServerDataOperator from '@database/operator/server_data_operator'; import type UserModel from '@typings/database/models/servers/user'; -export const queryUserById = async (database: Database, userId: string) => { +const {SERVER: {USER}} = MM_TABLES; +export const getUserById = async (database: Database, userId: string) => { try { - const userRecord = (await database.collections.get(MM_TABLES.SERVER.USER).find(userId)) as UserModel; + const userRecord = (await database.get(USER).find(userId)) as UserModel; return userRecord; } catch { return undefined; } }; -export const queryCurrentUser = async (database: Database) => { - const currentUserId = await queryCurrentUserId(database); +export const observeUser = (database: Database, userId: string) => { + return database.get(USER).query(Q.where('id', userId), Q.take(1)).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$(undefined))), + ); +}; + +export const getCurrentUser = async (database: Database) => { + const currentUserId = await getCurrentUserId(database); if (currentUserId) { - return queryUserById(database, currentUserId); + return getUserById(database, currentUserId); } return undefined; }; -export const queryAllUsers = async (database: Database): Promise => { - try { - const userRecords = (await database.get(MM_TABLES.SERVER.USER).query().fetch()) as UserModel[]; - return userRecords; - } catch { - return Promise.resolve([] as UserModel[]); - } +export const observeCurrentUser = (database: Database) => { + return observeCurrentUserId(database).pipe( + switchMap((id) => observeUser(database, id)), + ); }; -export const queryUsersById = async (database: Database, userIds: string[]): Promise => { - try { - const userRecords = (await database.get(MM_TABLES.SERVER.USER).query(Q.where('id', Q.oneOf(userIds))).fetch()) as UserModel[]; - return userRecords; - } catch { - return Promise.resolve([] as UserModel[]); - } +export const queryAllUsers = (database: Database) => { + return database.get(USER).query(); }; -export const queryUsersByUsername = async (database: Database, usernames: string[]): Promise => { - try { - const userRecords = (await database.get(MM_TABLES.SERVER.USER).query(Q.where('username', Q.oneOf(usernames))).fetch()) as UserModel[]; - return userRecords; - } catch { - return Promise.resolve([] as UserModel[]); - } +export const queryUsersById = (database: Database, userIds: string[]) => { + return database.get(USER).query(Q.where('id', Q.oneOf(userIds))); +}; + +export const queryUsersByUsername = (database: Database, usernames: string[]) => { + return database.get(USER).query(Q.where('username', Q.oneOf(usernames))); }; export const prepareUsers = (operator: ServerDataOperator, users: UserProfile[]) => { @@ -66,3 +69,30 @@ export const prepareUsers = (operator: ServerDataOperator, users: UserProfile[]) return undefined; } }; + +export const observeTeammateNameDisplay = (database: Database) => { + const config = observeConfig(database); + const license = observeLicense(database); + const preferences = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS).observe(); + return combineLatest([config, license, preferences]).pipe( + switchMap( + ([cfg, lcs, prefs]) => getTeammateNameDisplaySetting(prefs, cfg, lcs), + ), + ); +}; + +export const queryUsersLike = (database: Database, likeUsername: string) => { + return database.get(USER).query( + Q.where('username', Q.like( + `%${Q.sanitizeLikeString(likeUsername)}%`, + )), + ); +}; + +export const queryUsersByIdsOrUsernames = (database: Database, ids: string[], usernames: string[]) => { + return database.get(USER).query( + Q.or( + Q.where('id', Q.oneOf(ids)), + Q.where('username', Q.oneOf(usernames)), + )); +}; diff --git a/app/screens/about/index.tsx b/app/screens/about/index.tsx index ab271d4dd2..2db27425d6 100644 --- a/app/screens/about/index.tsx +++ b/app/screens/about/index.tsx @@ -14,9 +14,9 @@ import AppVersion from '@components/app_version'; import CompassIcon from '@components/compass_icon'; import FormattedText from '@components/formatted_text'; import AboutLinks from '@constants/about_links'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {useTheme} from '@context/theme'; import {t} from '@i18n'; +import {observeConfig, observeLicense} from '@queries/servers/system'; import {preventDoubleTap} from '@utils/tap'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {tryOpenURL} from '@utils/url'; @@ -28,7 +28,6 @@ import Title from './title'; import TosPrivacyContainer from './tos_privacy'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; const MATTERMOST_BUNDLE_IDS = ['com.mattermost.rnbeta', 'com.mattermost.rn']; @@ -118,8 +117,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { }); type ConnectedAboutProps = { - config: SystemModel; - license: SystemModel; + config: ClientConfig; + license: ClientLicense; } const ConnectedAbout = ({config, license}: ConnectedAboutProps) => { @@ -193,7 +192,7 @@ const ConnectedAbout = ({config, license}: ConnectedAboutProps) => { style={style.title} testID='about.site_name' > - {`${config.value.SiteName} `} + {`${config.SiteName} `} { defaultMessage='Database: {type}' style={style.info} values={{ - type: config.value.SQLDriverName, + type: config.SQLDriverName, }} testID='about.database' /> - {license.value.IsLicensed === 'true' && ( + {license.IsLicensed === 'true' && ( <View style={style.licenseContainer}> <FormattedText id={t('mobile.about.licensed')} defaultMessage='Licensed to: {company}' style={style.info} values={{ - company: license.value.Company, + company: license.Company, }} testID='about.licensee' /> @@ -239,7 +238,7 @@ const ConnectedAbout = ({config, license}: ConnectedAboutProps) => { defaultMessage='{site} is powered by Mattermost' style={style.footerText} values={{ - site: config.value.SiteName, + site: config.SiteName, }} testID='about.powered_by' /> @@ -300,7 +299,7 @@ const ConnectedAbout = ({config, license}: ConnectedAboutProps) => { style={style.footerText} testID='about.build_hash.value' > - {config.value.BuildHash} + {config.BuildHash} </Text> </View> <View style={style.footerGroup}> @@ -314,7 +313,7 @@ const ConnectedAbout = ({config, license}: ConnectedAboutProps) => { style={style.footerText} testID='about.build_hash_enterprise.value' > - {config.value.BuildHashEnterprise} + {config.BuildHashEnterprise} </Text> </View> </View> @@ -329,7 +328,7 @@ const ConnectedAbout = ({config, license}: ConnectedAboutProps) => { style={style.footerText} testID='about.build_date.value' > - {config.value.BuildDate} + {config.BuildDate} </Text> </View> </View> @@ -339,8 +338,8 @@ const ConnectedAbout = ({config, license}: ConnectedAboutProps) => { }; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({ - config: database.collections.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG), - license: database.collections.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.LICENSE), + config: observeConfig(database), + license: observeLicense(database), })); export default withDatabase(enhanced(ConnectedAbout)); diff --git a/app/screens/about/learn_more.tsx b/app/screens/about/learn_more.tsx index 31632f6b1a..ee6b9175cd 100644 --- a/app/screens/about/learn_more.tsx +++ b/app/screens/about/learn_more.tsx @@ -10,10 +10,8 @@ import {useTheme} from '@context/theme'; import {t} from '@i18n'; import {makeStyleSheetFromTheme} from '@utils/theme'; -import type SystemModel from '@typings/database/models/servers/system'; - type LearnMoreProps = { - config: SystemModel; + config: ClientConfig; onHandleAboutEnterprise: () => void; onHandleAboutTeam: () => void; }; @@ -27,7 +25,7 @@ const LearnMore = ({config, onHandleAboutEnterprise, onHandleAboutTeam}: LearnMo let onPress = onHandleAboutTeam; let url = Config.TeamEditionLearnURL; - if (config.value?.BuildEnterpriseReady === 'true') { + if (config.BuildEnterpriseReady === 'true') { id = t('about.enterpriseEditionLearn'); defaultMessage = 'Learn more about Enterprise Edition at '; onPress = onHandleAboutEnterprise; diff --git a/app/screens/about/server_version.tsx b/app/screens/about/server_version.tsx index b27d4f3d9e..faf0e67290 100644 --- a/app/screens/about/server_version.tsx +++ b/app/screens/about/server_version.tsx @@ -8,21 +8,19 @@ import {useTheme} from '@context/theme'; import {t} from '@i18n'; import {makeStyleSheetFromTheme} from '@utils/theme'; -import type SystemModel from '@typings/database/models/servers/system'; - type ServerVersionProps = { - config: SystemModel; + config: ClientConfig; } const ServerVersion = ({config}: ServerVersionProps) => { - const buildNumber = config.value.BuildNumber; - const version = config.value.Version; + const buildNumber = config.BuildNumber; + const version = config.Version; const theme = useTheme(); const style = getStyleSheet(theme); let id = t('mobile.about.serverVersion'); let defaultMessage = 'Server Version: {version} (Build {number})'; - let values = { + let values: {version: string; number?: string} = { version, number: buildNumber, }; diff --git a/app/screens/about/subtitle.tsx b/app/screens/about/subtitle.tsx index 7d02f7267c..a6cfa00a38 100644 --- a/app/screens/about/subtitle.tsx +++ b/app/screens/about/subtitle.tsx @@ -8,10 +8,8 @@ import {useTheme} from '@context/theme'; import {t} from '@i18n'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; -import type SystemModel from '@typings/database/models/servers/system'; - type SubtitleProps = { - config: SystemModel; + config: ClientConfig; } const Subtitle = ({config}: SubtitleProps) => { @@ -21,7 +19,7 @@ const Subtitle = ({config}: SubtitleProps) => { let id = t('about.teamEditionSt'); let defaultMessage = 'All your team communication in one place, instantly searchable and accessible anywhere.'; - if (config.value.BuildEnterpriseReady === 'true') { + if (config.BuildEnterpriseReady === 'true') { id = t('about.enterpriseEditionSt'); defaultMessage = 'Modern communication from behind your firewall.'; } diff --git a/app/screens/about/title.tsx b/app/screens/about/title.tsx index acb2860c8c..9b5771010b 100644 --- a/app/screens/about/title.tsx +++ b/app/screens/about/title.tsx @@ -8,11 +8,9 @@ import {useTheme} from '@context/theme'; import {t} from '@i18n'; import {makeStyleSheetFromTheme} from '@utils/theme'; -import type SystemModel from '@typings/database/models/servers/system'; - type TitleProps = { - config: SystemModel; - license: SystemModel; + config: ClientConfig; + license: ClientLicense; } const Title = ({config, license}: TitleProps) => { @@ -22,11 +20,11 @@ const Title = ({config, license}: TitleProps) => { let id = t('about.teamEditiont0'); let defaultMessage = 'Team Edition'; - if (config.value.BuildEnterpriseReady === 'true') { + if (config.BuildEnterpriseReady === 'true') { id = t('about.teamEditiont1'); defaultMessage = 'Enterprise Edition'; - if (license.value.IsLicensed === 'true') { + if (license.IsLicensed === 'true') { id = t('about.enterpriseEditione1'); defaultMessage = 'Enterprise Edition'; } diff --git a/app/screens/about/tos_privacy.tsx b/app/screens/about/tos_privacy.tsx index 567ea87390..69110ec624 100644 --- a/app/screens/about/tos_privacy.tsx +++ b/app/screens/about/tos_privacy.tsx @@ -9,17 +9,15 @@ import {useTheme} from '@context/theme'; import {t} from '@i18n'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; -import type SystemModel from '@typings/database/models/servers/system'; - type TosPrivacyContainerProps = { - config: SystemModel; + config: ClientConfig; onPressTOS: () => void; onPressPrivacyPolicy: () => void; } const TosPrivacyContainer = ({config, onPressTOS, onPressPrivacyPolicy}: TosPrivacyContainerProps) => { - const hasTermsOfServiceLink = config.value?.TermsOfServiceLink; - const hasPrivacyPolicyLink = config.value?.PrivacyPolicyLink; + const hasTermsOfServiceLink = config.TermsOfServiceLink; + const hasPrivacyPolicyLink = config.PrivacyPolicyLink; const theme = useTheme(); const style = getStyleSheet(theme); return ( diff --git a/app/screens/browse_channels/index.ts b/app/screens/browse_channels/index.ts index b23b1a8367..a3006cd2f5 100644 --- a/app/screens/browse_channels/index.ts +++ b/app/screens/browse_channels/index.ts @@ -1,56 +1,42 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {Permissions} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; -import {MyChannelModel} from '@database/models/server'; +import {queryAllMyChannel} from '@queries/servers/channel'; +import {queryRolesByNames} from '@queries/servers/role'; +import {observeConfig, observeCurrentTeamId, observeCurrentUserId} from '@queries/servers/system'; +import {observeUser} from '@queries/servers/user'; import {hasPermission} from '@utils/role'; import SearchHandler from './search_handler'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type RoleModel from '@typings/database/models/servers/role'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {SYSTEM, USER, ROLE, MY_CHANNEL}} = MM_TABLES; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { - const config = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap(({value}) => of$(value as ClientConfig)), - ); + const config = observeConfig(database); const sharedChannelsEnabled = config.pipe( - switchMap((v) => of$(v.ExperimentalSharedChannels === 'true')), + switchMap((v) => of$(v?.ExperimentalSharedChannels === 'true')), ); const canShowArchivedChannels = config.pipe( - switchMap((v) => of$(v.ExperimentalViewArchivedChannels === 'true')), + switchMap((v) => of$(v?.ExperimentalViewArchivedChannels === 'true')), ); - const currentTeamId = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe( - switchMap(({value}) => of$(value)), - ); - const currentUserId = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => of$(value)), - ); + const currentTeamId = observeCurrentTeamId(database); + const currentUserId = observeCurrentUserId(database); - const joinedChannels = database.get<MyChannelModel>(MY_CHANNEL).query().observe(); + const joinedChannels = queryAllMyChannel(database).observe(); - const currentUser = currentUserId.pipe( - switchMap((id) => database.get<UserModel>(USER).findAndObserve(id)), - ); - const rolesArray = currentUser.pipe( - switchMap((u) => of$(u.roles.split(' '))), - ); - const roles = rolesArray.pipe( - switchMap((values) => database.get<RoleModel>(ROLE).query(Q.where('name', Q.oneOf(values))).observe()), + const roles = currentUserId.pipe( + switchMap((id) => observeUser(database, id)), + switchMap((u) => (u ? of$(u.roles.split(' ')) : of$([]))), + switchMap((values) => queryRolesByNames(database, values).observe()), ); const canCreateChannels = roles.pipe(switchMap((r) => of$(hasPermission(r, Permissions.CREATE_PUBLIC_CHANNEL, false)))); diff --git a/app/screens/channel/channel_post_list/index.ts b/app/screens/channel/channel_post_list/index.ts index a94dd6da48..240eb660ac 100644 --- a/app/screens/channel/channel_post_list/index.ts +++ b/app/screens/channel/channel_post_list/index.ts @@ -9,59 +9,39 @@ import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {getPreferenceAsBool} from '@helpers/api/preference'; +import {observeMyChannel} from '@queries/servers/channel'; +import {queryPostsBetween, queryPostsInChannel} from '@queries/servers/post'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {observeConfigBooleanValue} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {getTimezone} from '@utils/user'; import ChannelPostList from './channel_post_list'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type MyChannelModel from '@typings/database/models/servers/my_channel'; -import type PostModel from '@typings/database/models/servers/post'; -import type PostsInChannelModel from '@typings/database/models/servers/posts_in_channel'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {MY_CHANNEL, POST, POSTS_IN_CHANNEL, PREFERENCE, SYSTEM, USER}} = MM_TABLES; const enhanced = withObservables(['channelId', 'forceQueryAfterAppState'], ({database, channelId}: {channelId: string; forceQueryAfterAppState: AppStateStatus} & WithDatabaseArgs) => { - const currentUser = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap((currentUserId) => database.get<UserModel>(USER).findAndObserve(currentUserId.value)), - ); + const currentUser = observeCurrentUser(database); return { - currentTimezone: currentUser.pipe((switchMap((user) => of$(getTimezone(user.timezone))))), - currentUsername: currentUser.pipe((switchMap((user) => of$(user.username)))), - isTimezoneEnabled: database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap((config) => of$(config.value.ExperimentalTimezone === 'true')), + currentTimezone: currentUser.pipe((switchMap((user) => of$(getTimezone(user?.timezone || null))))), + currentUsername: currentUser.pipe((switchMap((user) => of$(user?.username)))), + isTimezoneEnabled: observeConfigBooleanValue(database, 'ExperimentalTimezone'), + lastViewedAt: observeMyChannel(database, channelId).pipe( + switchMap((myChannel) => of$(myChannel?.viewedAt)), ), - lastViewedAt: database.get<MyChannelModel>(MY_CHANNEL).findAndObserve(channelId).pipe( - switchMap((myChannel) => of$(myChannel.viewedAt)), - ), - posts: database.get<PostsInChannelModel>(POSTS_IN_CHANNEL).query( - Q.where('channel_id', channelId), - Q.sortBy('latest', Q.desc), - ).observeWithColumns(['earliest', 'latest']).pipe( + posts: queryPostsInChannel(database, channelId).observeWithColumns(['earliest', 'latest']).pipe( switchMap((postsInChannel) => { if (!postsInChannel.length) { return of$([]); } const {earliest, latest} = postsInChannel[0]; - return database.get<PostModel>(POST).query( - Q.and( - Q.where('channel_id', channelId), - Q.where('create_at', Q.between(earliest, latest)), - ), - Q.sortBy('create_at', Q.desc), - ).observe(); + return queryPostsBetween(database, earliest, latest, Q.desc, '', channelId).observe(); }), ), - shouldShowJoinLeaveMessages: database.get<PreferenceModel>(PREFERENCE).query( - Q.where('category', Preferences.CATEGORY_ADVANCED_SETTINGS), - Q.where('name', Preferences.ADVANCED_FILTER_JOIN_LEAVE), - ).observe().pipe( + shouldShowJoinLeaveMessages: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_ADVANCED_SETTINGS, Preferences.ADVANCED_FILTER_JOIN_LEAVE).observe().pipe( switchMap((preferences) => of$(getPreferenceAsBool(preferences, Preferences.CATEGORY_ADVANCED_SETTINGS, Preferences.ADVANCED_FILTER_JOIN_LEAVE, true))), ), }; diff --git a/app/screens/channel/channel_post_list/intro/direct_channel/group/index.ts b/app/screens/channel/channel_post_list/intro/direct_channel/group/index.ts index 920750bfde..dcb4f124a0 100644 --- a/app/screens/channel/channel_post_list/intro/direct_channel/group/index.ts +++ b/app/screens/channel/channel_post_list/intro/direct_channel/group/index.ts @@ -1,21 +1,17 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {MM_TABLES} from '@constants/database'; +import {queryUsersById} from '@queries/servers/user'; import Group from './group'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {USER}} = MM_TABLES; const enhanced = withObservables([], ({userIds, database}: {userIds: string[]} & WithDatabaseArgs) => ({ - users: database.get<UserModel>(USER).query(Q.where('id', Q.oneOf(userIds))).observeWithColumns(['last_picture_update']), + users: queryUsersById(database, userIds).observeWithColumns(['last_picture_update']), })); export default withDatabase(enhanced(Group)); 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 70df012e0c..8a7e8402c9 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 @@ -4,21 +4,23 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; -import {catchError, switchMap} from 'rxjs/operators'; +import {switchMap} from 'rxjs/operators'; import {General} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeCurrentUserId} from '@queries/servers/system'; +import {observeUser} from '@queries/servers/user'; import {getUserIdFromChannelName} from '@utils/user'; import DirectChannel from './direct_channel'; import type {WithDatabaseArgs} from '@typings/database/database'; import type ChannelModel from '@typings/database/models/servers/channel'; -import type SystemModel from '@typings/database/models/servers/system'; import type UserModel from '@typings/database/models/servers/user'; +const observeIsBot = (user: UserModel | undefined) => of$(Boolean(user?.isBot)); + const enhanced = withObservables([], ({channel, database}: {channel: ChannelModel} & WithDatabaseArgs) => { - const currentUserId = database.get<SystemModel>(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe(switchMap(({value}) => of$(value))); + const currentUserId = observeCurrentUserId(database); const members = channel.members.observe(); let isBot = of$(false); @@ -26,10 +28,8 @@ const enhanced = withObservables([], ({channel, database}: {channel: ChannelMode isBot = currentUserId.pipe( switchMap((userId: string) => { const otherUserId = getUserIdFromChannelName(userId, channel.name); - return database.get<UserModel>(MM_TABLES.SERVER.USER).findAndObserve(otherUserId).pipe( - // eslint-disable-next-line max-nested-callbacks - switchMap((user) => of$(user.isBot)), // eslint-disable-next-line max-nested-callbacks - catchError(() => of$(false)), + return observeUser(database, otherUserId).pipe( + switchMap(observeIsBot), ); }), ); diff --git a/app/screens/channel/channel_post_list/intro/index.ts b/app/screens/channel/channel_post_list/intro/index.ts index 8fce3966ec..1a81754c00 100644 --- a/app/screens/channel/channel_post_list/intro/index.ts +++ b/app/screens/channel/channel_post_list/intro/index.ts @@ -1,36 +1,36 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {combineLatest} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeChannel, observeMyChannel} from '@queries/servers/channel'; +import {queryRolesByNames} from '@queries/servers/role'; +import {observeCurrentUser} from '@queries/servers/user'; import Intro from './intro'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type ChannelModel from '@typings/database/models/servers/channel'; -import type MyChannelModel from '@typings/database/models/servers/my_channel'; -import type RoleModel from '@typings/database/models/servers/role'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {CHANNEL, MY_CHANNEL, ROLE, SYSTEM, USER}} = MM_TABLES; const enhanced = withObservables(['channelId'], ({channelId, database}: {channelId: string} & WithDatabaseArgs) => { - const channel = database.get<ChannelModel>(CHANNEL).findAndObserve(channelId); - const myChannel = database.get<MyChannelModel>(MY_CHANNEL).findAndObserve(channelId); - const me = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => database.get<UserModel>(USER).findAndObserve(value)), - ); + const channel = observeChannel(database, channelId); + const myChannel = observeMyChannel(database, channelId); + const me = observeCurrentUser(database); const roles = combineLatest([me, myChannel]).pipe( - switchMap(([{roles: userRoles}, {roles: memberRoles}]) => { - const combinedRoles = userRoles.split(' ').concat(memberRoles.split(' ')); - return database.get<RoleModel>(ROLE).query(Q.where('name', Q.oneOf(combinedRoles))).observe(); + switchMap(([user, member]) => { + const userRoles = user?.roles.split(' '); + const memberRoles = member?.roles.split(' '); + const combinedRoles = []; + if (userRoles) { + combinedRoles.push(...userRoles); + } + if (memberRoles) { + combinedRoles.push(...memberRoles); + } + return queryRolesByNames(database, combinedRoles).observe(); }), ); diff --git a/app/screens/channel/channel_post_list/intro/options/favorite/index.ts b/app/screens/channel/channel_post_list/intro/options/favorite/index.ts index 5db9164aa7..6b54aaacb3 100644 --- a/app/screens/channel/channel_post_list/intro/options/favorite/index.ts +++ b/app/screens/channel/channel_post_list/intro/options/favorite/index.ts @@ -1,25 +1,20 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {Preferences} from '@constants'; -import {MM_TABLES} from '@constants/database'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import FavoriteItem from './favorite'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type PreferenceModel from '@typings/database/models/servers/preference'; const enhanced = withObservables([], ({channelId, database}: {channelId: string} & WithDatabaseArgs) => ({ - isFavorite: database.get<PreferenceModel>(MM_TABLES.SERVER.PREFERENCE).query( - Q.where('category', Preferences.CATEGORY_FAVORITE_CHANNEL), - Q.where('name', channelId), - ).observeWithColumns(['value']).pipe( + isFavorite: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_FAVORITE_CHANNEL, channelId).observeWithColumns(['value']).pipe( switchMap((prefs) => { return prefs.length ? of$(prefs[0].value === 'true') : of$(false); }), diff --git a/app/screens/channel/channel_post_list/intro/public_or_private_channel/index.ts b/app/screens/channel/channel_post_list/intro/public_or_private_channel/index.ts index 39c657301c..afe46e31e9 100644 --- a/app/screens/channel/channel_post_list/intro/public_or_private_channel/index.ts +++ b/app/screens/channel/channel_post_list/intro/public_or_private_channel/index.ts @@ -1,43 +1,38 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {combineLatest, of as of$} from 'rxjs'; -import {map, switchMap} from 'rxjs/operators'; +import {map} from 'rxjs/operators'; import {Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {observeConfig, observeLicense} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {displayUsername} from '@utils/user'; import PublicOrPrivateChannel from './public_or_private_channel'; import type {WithDatabaseArgs} from '@typings/database/database'; import type ChannelModel from '@typings/database/models/servers/channel'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; import type UserModel from '@typings/database/models/servers/user'; -const {SERVER: {PREFERENCE, SYSTEM, USER}} = MM_TABLES; - const enhanced = withObservables([], ({channel, database}: {channel: ChannelModel} & WithDatabaseArgs) => { let creator; if (channel.creatorId) { - const config = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe(switchMap(({value}) => of$(value as ClientConfig))); - const license = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.LICENSE).pipe(switchMap(({value}) => of$(value as ClientLicense))); - const preferences = database.get<PreferenceModel>(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS)).observe(); - const me = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => database.get<UserModel>(USER).findAndObserve(value)), - ); + const config = observeConfig(database); + const license = observeLicense(database); + const preferences = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS).observe(); + const me = observeCurrentUser(database); const profile = channel.creator.observe(); const teammateNameDisplay = combineLatest([preferences, config, license]).pipe( map(([prefs, cfg, lcs]) => getTeammateNameDisplaySetting(prefs, cfg, lcs)), ); creator = combineLatest([profile, teammateNameDisplay, me]).pipe( - map(([user, displaySetting, currentUser]) => (user ? displayUsername(user as UserModel, currentUser.locale, displaySetting, true) : '')), + map(([user, displaySetting, currentUser]) => (user ? displayUsername(user as UserModel, currentUser?.locale, displaySetting, true) : '')), ); } else { creator = of$(undefined); diff --git a/app/screens/channel/index.tsx b/app/screens/channel/index.tsx index 3c73ca4001..984f4de1c5 100644 --- a/app/screens/channel/index.tsx +++ b/app/screens/channel/index.tsx @@ -1,62 +1,32 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {combineLatest, of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {Database, General} from '@constants'; +import {General} from '@constants'; +import {observeChannel, observeChannelInfo} from '@queries/servers/channel'; +import {observeCurrentChannelId, observeCurrentTeamId, observeCurrentUserId} from '@queries/servers/system'; +import {observeUser} from '@queries/servers/user'; import {getUserIdFromChannelName} from '@utils/user'; import Channel from './channel'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type ChannelModel from '@typings/database/models/servers/channel'; -import type ChannelInfoModel from '@typings/database/models/servers/channel_info'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; - -const {MM_TABLES, SYSTEM_IDENTIFIERS} = Database; -const {SERVER: {CHANNEL, CHANNEL_INFO, SYSTEM, USER}} = MM_TABLES; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { - const currentUserId = database.collections.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}: {value: string}) => of$(value)), - ); - - const channelId = database.collections.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID).pipe( - switchMap(({value}: {value: string}) => of$(value)), - ); - const teamId = database.collections.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe( - switchMap(({value}: {value: string}) => of$(value)), - ); + const currentUserId = observeCurrentUserId(database); + const channelId = observeCurrentChannelId(database); + const teamId = observeCurrentTeamId(database); const channel = channelId.pipe( - switchMap((id) => database.get<ChannelModel>(CHANNEL).query(Q.where('id', id)).observe().pipe( - // eslint-disable-next-line max-nested-callbacks - switchMap((channels) => { - if (channels.length) { - return channels[0].observe(); - } - - return of$(null); - }), - )), + switchMap((id) => observeChannel(database, id)), ); const channelInfo = channelId.pipe( - switchMap((id) => database.get<ChannelInfoModel>(CHANNEL_INFO).query(Q.where('id', id)).observe().pipe( - // eslint-disable-next-line max-nested-callbacks - switchMap((infos) => { - if (infos.length) { - return infos[0].observe(); - } - - return of$(null); - }), - )), + switchMap((id) => observeChannelInfo(database, id)), ); const isOwnDirectMessage = combineLatest([currentUserId, channel]).pipe( @@ -74,9 +44,9 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { const name = combineLatest([currentUserId, channel]).pipe(switchMap(([userId, c]) => { if (c?.type === General.DM_CHANNEL) { const teammateId = getUserIdFromChannelName(userId, c.name); - return database.get<UserModel>(USER).findAndObserve(teammateId).pipe( + return observeUser(database, teammateId).pipe( // eslint-disable-next-line max-nested-callbacks - switchMap((u) => of$(`@${u.username}`)), + switchMap((u) => (u ? of$(`@${u.username}`) : of$('Someone'))), ); } else if (c?.type === General.GM_CHANNEL) { return of$(`@${c.name}`); diff --git a/app/screens/channel/intro/direct_channel/group/index.ts b/app/screens/channel/intro/direct_channel/group/index.ts index 920750bfde..dcb4f124a0 100644 --- a/app/screens/channel/intro/direct_channel/group/index.ts +++ b/app/screens/channel/intro/direct_channel/group/index.ts @@ -1,21 +1,17 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {MM_TABLES} from '@constants/database'; +import {queryUsersById} from '@queries/servers/user'; import Group from './group'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {USER}} = MM_TABLES; const enhanced = withObservables([], ({userIds, database}: {userIds: string[]} & WithDatabaseArgs) => ({ - users: database.get<UserModel>(USER).query(Q.where('id', Q.oneOf(userIds))).observeWithColumns(['last_picture_update']), + users: queryUsersById(database, userIds).observeWithColumns(['last_picture_update']), })); export default withDatabase(enhanced(Group)); diff --git a/app/screens/channel/intro/direct_channel/index.ts b/app/screens/channel/intro/direct_channel/index.ts index 70df012e0c..6c30d0fe4b 100644 --- a/app/screens/channel/intro/direct_channel/index.ts +++ b/app/screens/channel/intro/direct_channel/index.ts @@ -4,32 +4,32 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; -import {catchError, switchMap} from 'rxjs/operators'; +import {switchMap} from 'rxjs/operators'; import {General} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeCurrentUserId} from '@queries/servers/system'; +import {observeUser} from '@queries/servers/user'; import {getUserIdFromChannelName} from '@utils/user'; import DirectChannel from './direct_channel'; import type {WithDatabaseArgs} from '@typings/database/database'; import type ChannelModel from '@typings/database/models/servers/channel'; -import type SystemModel from '@typings/database/models/servers/system'; import type UserModel from '@typings/database/models/servers/user'; +const observeIsBot = (user: UserModel | undefined) => of$(Boolean(user?.isBot)); + const enhanced = withObservables([], ({channel, database}: {channel: ChannelModel} & WithDatabaseArgs) => { - const currentUserId = database.get<SystemModel>(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe(switchMap(({value}) => of$(value))); + const currentUserId = observeCurrentUserId(database); const members = channel.members.observe(); let isBot = of$(false); if (channel.type === General.DM_CHANNEL) { isBot = currentUserId.pipe( - switchMap((userId: string) => { + switchMap((userId) => { const otherUserId = getUserIdFromChannelName(userId, channel.name); - return database.get<UserModel>(MM_TABLES.SERVER.USER).findAndObserve(otherUserId).pipe( - // eslint-disable-next-line max-nested-callbacks - switchMap((user) => of$(user.isBot)), // eslint-disable-next-line max-nested-callbacks - catchError(() => of$(false)), + return observeUser(database, otherUserId).pipe( + switchMap(observeIsBot), ); }), ); diff --git a/app/screens/channel/intro/index.ts b/app/screens/channel/intro/index.ts index 8fce3966ec..1a81754c00 100644 --- a/app/screens/channel/intro/index.ts +++ b/app/screens/channel/intro/index.ts @@ -1,36 +1,36 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {combineLatest} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeChannel, observeMyChannel} from '@queries/servers/channel'; +import {queryRolesByNames} from '@queries/servers/role'; +import {observeCurrentUser} from '@queries/servers/user'; import Intro from './intro'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type ChannelModel from '@typings/database/models/servers/channel'; -import type MyChannelModel from '@typings/database/models/servers/my_channel'; -import type RoleModel from '@typings/database/models/servers/role'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {CHANNEL, MY_CHANNEL, ROLE, SYSTEM, USER}} = MM_TABLES; const enhanced = withObservables(['channelId'], ({channelId, database}: {channelId: string} & WithDatabaseArgs) => { - const channel = database.get<ChannelModel>(CHANNEL).findAndObserve(channelId); - const myChannel = database.get<MyChannelModel>(MY_CHANNEL).findAndObserve(channelId); - const me = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => database.get<UserModel>(USER).findAndObserve(value)), - ); + const channel = observeChannel(database, channelId); + const myChannel = observeMyChannel(database, channelId); + const me = observeCurrentUser(database); const roles = combineLatest([me, myChannel]).pipe( - switchMap(([{roles: userRoles}, {roles: memberRoles}]) => { - const combinedRoles = userRoles.split(' ').concat(memberRoles.split(' ')); - return database.get<RoleModel>(ROLE).query(Q.where('name', Q.oneOf(combinedRoles))).observe(); + switchMap(([user, member]) => { + const userRoles = user?.roles.split(' '); + const memberRoles = member?.roles.split(' '); + const combinedRoles = []; + if (userRoles) { + combinedRoles.push(...userRoles); + } + if (memberRoles) { + combinedRoles.push(...memberRoles); + } + return queryRolesByNames(database, combinedRoles).observe(); }), ); diff --git a/app/screens/channel/intro/options/favorite/index.ts b/app/screens/channel/intro/options/favorite/index.ts index 5db9164aa7..6b54aaacb3 100644 --- a/app/screens/channel/intro/options/favorite/index.ts +++ b/app/screens/channel/intro/options/favorite/index.ts @@ -1,25 +1,20 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {Preferences} from '@constants'; -import {MM_TABLES} from '@constants/database'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import FavoriteItem from './favorite'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type PreferenceModel from '@typings/database/models/servers/preference'; const enhanced = withObservables([], ({channelId, database}: {channelId: string} & WithDatabaseArgs) => ({ - isFavorite: database.get<PreferenceModel>(MM_TABLES.SERVER.PREFERENCE).query( - Q.where('category', Preferences.CATEGORY_FAVORITE_CHANNEL), - Q.where('name', channelId), - ).observeWithColumns(['value']).pipe( + isFavorite: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_FAVORITE_CHANNEL, channelId).observeWithColumns(['value']).pipe( switchMap((prefs) => { return prefs.length ? of$(prefs[0].value === 'true') : of$(false); }), diff --git a/app/screens/channel/intro/public_or_private_channel/index.ts b/app/screens/channel/intro/public_or_private_channel/index.ts index 39c657301c..afe46e31e9 100644 --- a/app/screens/channel/intro/public_or_private_channel/index.ts +++ b/app/screens/channel/intro/public_or_private_channel/index.ts @@ -1,43 +1,38 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {combineLatest, of as of$} from 'rxjs'; -import {map, switchMap} from 'rxjs/operators'; +import {map} from 'rxjs/operators'; import {Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {observeConfig, observeLicense} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {displayUsername} from '@utils/user'; import PublicOrPrivateChannel from './public_or_private_channel'; import type {WithDatabaseArgs} from '@typings/database/database'; import type ChannelModel from '@typings/database/models/servers/channel'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; import type UserModel from '@typings/database/models/servers/user'; -const {SERVER: {PREFERENCE, SYSTEM, USER}} = MM_TABLES; - const enhanced = withObservables([], ({channel, database}: {channel: ChannelModel} & WithDatabaseArgs) => { let creator; if (channel.creatorId) { - const config = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe(switchMap(({value}) => of$(value as ClientConfig))); - const license = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.LICENSE).pipe(switchMap(({value}) => of$(value as ClientLicense))); - const preferences = database.get<PreferenceModel>(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS)).observe(); - const me = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => database.get<UserModel>(USER).findAndObserve(value)), - ); + const config = observeConfig(database); + const license = observeLicense(database); + const preferences = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS).observe(); + const me = observeCurrentUser(database); const profile = channel.creator.observe(); const teammateNameDisplay = combineLatest([preferences, config, license]).pipe( map(([prefs, cfg, lcs]) => getTeammateNameDisplaySetting(prefs, cfg, lcs)), ); creator = combineLatest([profile, teammateNameDisplay, me]).pipe( - map(([user, displaySetting, currentUser]) => (user ? displayUsername(user as UserModel, currentUser.locale, displaySetting, true) : '')), + map(([user, displaySetting, currentUser]) => (user ? displayUsername(user as UserModel, currentUser?.locale, displaySetting, true) : '')), ); } else { creator = of$(undefined); diff --git a/app/screens/create_direct_message/index.ts b/app/screens/create_direct_message/index.ts index b725183f6f..b2ff6bcffd 100644 --- a/app/screens/create_direct_message/index.ts +++ b/app/screens/create_direct_message/index.ts @@ -1,50 +1,29 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {combineLatest, of as of$} from 'rxjs'; -import {map, switchMap} from 'rxjs/operators'; +import {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; -import {General, Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; -import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; +import {General} from '@constants'; +import {observeConfig, observeCurrentTeamId, observeCurrentUserId} from '@queries/servers/system'; +import {observeTeammateNameDisplay} from '@queries/servers/user'; import CreateDirectMessage from './create_direct_message'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; - -const {SERVER: {SYSTEM, PREFERENCE}} = MM_TABLES; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { - const config = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG); - const license = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.LICENSE); - const preferences = database.get<PreferenceModel>(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS)).observe(); - const currentUserId = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => of$(value)), - ); - const currentTeamId = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe( - switchMap(({value}) => of$(value)), - ); - - const teammateNameDisplay = combineLatest([config, license, preferences]).pipe( - map( - ([{value: cfg}, {value: lcs}, prefs]) => getTeammateNameDisplaySetting(prefs, cfg, lcs), - ), - ); - - const restrictDirectMessage = config.pipe( - switchMap(({value: cfg}) => of$(cfg.RestrictDirectMessage !== General.RESTRICT_DIRECT_MESSAGE_ANY)), + const restrictDirectMessage = observeConfig(database).pipe( + switchMap((cfg) => of$(cfg?.RestrictDirectMessage !== General.RESTRICT_DIRECT_MESSAGE_ANY)), ); return { - teammateNameDisplay, - currentUserId, + teammateNameDisplay: observeTeammateNameDisplay(database), + currentUserId: observeCurrentUserId(database), + currentTeamId: observeCurrentTeamId(database), restrictDirectMessage, - currentTeamId, }; }); diff --git a/app/screens/create_direct_message/selected_users/selected_user.tsx b/app/screens/create_direct_message/selected_users/selected_user.tsx index 03b1c7459f..d126d3166e 100644 --- a/app/screens/create_direct_message/selected_users/selected_user.tsx +++ b/app/screens/create_direct_message/selected_users/selected_user.tsx @@ -9,10 +9,10 @@ import { View, } from 'react-native'; -import {typography} from '@app/utils/typography'; import CompassIcon from '@components/compass_icon'; import {useTheme} from '@context/theme'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; +import {typography} from '@utils/typography'; import {displayUsername} from '@utils/user'; type Props = { diff --git a/app/screens/create_direct_message/user_list.tsx b/app/screens/create_direct_message/user_list.tsx index f8192912ce..ede4b799ba 100644 --- a/app/screens/create_direct_message/user_list.tsx +++ b/app/screens/create_direct_message/user_list.tsx @@ -4,7 +4,6 @@ import React, {useCallback, useMemo} from 'react'; import {FlatList, Keyboard, ListRenderItemInfo, Platform, SectionList, SectionListData, Text, View} from 'react-native'; -import {typography} from '@app/utils/typography'; import FormattedText from '@components/formatted_text'; import UserListRow from '@components/user_list_row'; import {General} from '@constants'; @@ -13,6 +12,7 @@ import { changeOpacity, makeStyleSheetFromTheme, } from '@utils/theme'; +import {typography} from '@utils/typography'; const INITIAL_BATCH_TO_RENDER = 15; const SCROLL_EVENT_THROTTLE = 60; diff --git a/app/screens/custom_status/index.tsx b/app/screens/custom_status/index.tsx index d306f86cda..ff78cc5f5a 100644 --- a/app/screens/custom_status/index.tsx +++ b/app/screens/custom_status/index.tsx @@ -10,7 +10,7 @@ import {BackHandler, DeviceEventEmitter, Keyboard, KeyboardAvoidingView, Platfor import {EventSubscription, Navigation, NavigationButtonPressedEvent, NavigationComponent, NavigationComponentProps} from 'react-native-navigation'; import {Edge, SafeAreaView} from 'react-native-safe-area-context'; import {of as of$} from 'rxjs'; -import {switchMap, catchError} from 'rxjs/operators'; +import {switchMap} from 'rxjs/operators'; import {updateLocalCustomStatus} from '@actions/local/user'; import {removeRecentCustomStatus, updateCustomStatus, unsetCustomStatus} from '@actions/remote/user'; @@ -18,11 +18,12 @@ import CompassIcon from '@components/compass_icon'; import TabletTitle from '@components/tablet_title'; import {CustomStatusDuration, Events, Screens} from '@constants'; import {SET_CUSTOM_STATUS_FAILURE} from '@constants/custom_status'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {withServerUrl} from '@context/server'; import {withTheme} from '@context/theme'; +import {observeConfig, observeRecentCustomStatus} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {dismissModal, goToScreen, mergeNavigationOptions, showModal} from '@screens/navigation'; -import {getCurrentMomentForTimezone, getRoundedTime, isCustomStatusExpirySupported, safeParseJSON} from '@utils/helpers'; +import {getCurrentMomentForTimezone, getRoundedTime, isCustomStatusExpirySupported} from '@utils/helpers'; import {preventDoubleTap} from '@utils/tap'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import { @@ -37,7 +38,6 @@ import CustomStatusSuggestions from './components/custom_status_suggestions'; import RecentCustomStatuses from './components/recent_custom_statuses'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; import type UserModel from '@typings/database/models/servers/user'; interface Props extends NavigationComponentProps { @@ -58,7 +58,6 @@ type State = { expires_at: Moment; }; -const {SERVER: {SYSTEM, USER}} = MM_TABLES; const {DONT_CLEAR, THIRTY_MINUTES, ONE_HOUR, FOUR_HOURS, TODAY, THIS_WEEK, DATE_AND_TIME} = CustomStatusDuration; const DEFAULT_DURATION: CustomStatusDuration = TODAY; const BTN_UPDATE_STATUS = 'update-custom-status'; @@ -394,29 +393,13 @@ class CustomStatusModal extends NavigationComponent<Props, State> { const augmentCSM = injectIntl(withTheme(withServerUrl(CustomStatusModal))); const enhancedCSM = withObservables([], ({database}: WithDatabaseArgs) => { - const config = database. - get<SystemModel>(SYSTEM). - findAndObserve(SYSTEM_IDENTIFIERS.CONFIG); + const config = observeConfig(database); return { - currentUser: database. - get<SystemModel>(SYSTEM). - findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID). - pipe( - switchMap((id) => database.get<UserModel>(USER).findAndObserve(id.value)), - ), + currentUser: observeCurrentUser(database), customStatusExpirySupported: config.pipe( - switchMap((cfg) => of$(isCustomStatusExpirySupported((cfg.value as ClientConfig).Version))), + switchMap((cfg) => of$(isCustomStatusExpirySupported(cfg?.Version || ''))), ), - recentCustomStatuses: database. - get<SystemModel>(SYSTEM). - findAndObserve(SYSTEM_IDENTIFIERS.RECENT_CUSTOM_STATUS).pipe( - switchMap( - (recentStatuses) => of$( - safeParseJSON(recentStatuses.value) as unknown as UserCustomStatus[], - ), - ), - catchError(() => of$([])), - ), + recentCustomStatuses: observeRecentCustomStatus(database), }; }); diff --git a/app/screens/custom_status_clear_after/components/date_time_selector.tsx b/app/screens/custom_status_clear_after/components/date_time_selector.tsx index 1d6327b1d4..04ed89ef8f 100644 --- a/app/screens/custom_status_clear_after/components/date_time_selector.tsx +++ b/app/screens/custom_status_clear_after/components/date_time_selector.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import DateTimePicker from '@react-native-community/datetimepicker'; @@ -13,13 +12,12 @@ import {switchMap} from 'rxjs/operators'; import {Preferences} from '@constants'; import {CUSTOM_STATUS_TIME_PICKER_INTERVALS_IN_MINUTES} from '@constants/custom_status'; -import {MM_TABLES} from '@constants/database'; import {getPreferenceAsBool} from '@helpers/api/preference'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import {getCurrentMomentForTimezone, getRoundedTime, getUtcOffsetForTimeZone} from '@utils/helpers'; import {makeStyleSheetFromTheme} from '@utils/theme'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type PreferenceModel from '@typings/database/models/servers/preference'; type Props = { timezone: string | null; @@ -115,14 +113,11 @@ const DateTimeSelector = ({timezone, handleChange, isMilitaryTime, theme}: Props }; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({ - isMilitaryTime: database.get<PreferenceModel>(MM_TABLES.SERVER.PREFERENCE). - query( - Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS), - ).observe().pipe( - switchMap( - (preferences) => of$(getPreferenceAsBool(preferences, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', false)), - ), + isMilitaryTime: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS).observe().pipe( + switchMap( + (preferences) => of$(getPreferenceAsBool(preferences, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', false)), ), + ), })); export default withDatabase(enhanced(DateTimeSelector)); diff --git a/app/screens/custom_status_clear_after/index.tsx b/app/screens/custom_status_clear_after/index.tsx index e18811c591..954b381efa 100644 --- a/app/screens/custom_status_clear_after/index.tsx +++ b/app/screens/custom_status_clear_after/index.tsx @@ -14,17 +14,15 @@ import { NavigationComponentProps, Options, } from 'react-native-navigation'; -import {switchMap} from 'rxjs/operators'; import {CustomStatusDuration} from '@constants/custom_status'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeCurrentUser} from '@queries/servers/user'; import {dismissModal, mergeNavigationOptions, popTopScreen} from '@screens/navigation'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import ClearAfterMenuItem from './components/clear_after_menu_item'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; import type UserModel from '@typings/database/models/servers/user'; interface Props extends NavigationComponentProps { @@ -42,7 +40,6 @@ type State = { showExpiryTime: boolean; } -const {SERVER: {SYSTEM, USER}} = MM_TABLES; const CLEAR_AFTER = 'update-custom-status-clear-after'; const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { @@ -208,11 +205,7 @@ class ClearAfterModal extends NavigationComponent<Props, State> { } const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({ - currentUser: database.get<SystemModel>(SYSTEM). - findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID). - pipe( - switchMap((id) => database.get<UserModel>(USER).findAndObserve(id.value)), - ), + currentUser: observeCurrentUser(database), })); export default withDatabase(enhanced(injectIntl(ClearAfterModal))); diff --git a/app/screens/edit_post/index.tsx b/app/screens/edit_post/index.tsx index 341ce27a6a..a740d04514 100644 --- a/app/screens/edit_post/index.tsx +++ b/app/screens/edit_post/index.tsx @@ -6,20 +6,16 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {MAX_MESSAGE_LENGTH_FALLBACK} from '@constants/post_draft'; +import {observeConfigIntValue} from '@queries/servers/system'; import EditPost from './edit_post'; import type {WithDatabaseArgs} from '@typings/database/database'; import type PostModel from '@typings/database/models/servers/post'; -import type SystemModel from '@typings/database/models/servers/system'; const enhance = withObservables([], ({database, post}: WithDatabaseArgs & { post: PostModel}) => { - const maxPostSize = database.get<SystemModel>(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap(({value}) => of$(parseInt(value.MaxPostSize || '0', 10) || MAX_MESSAGE_LENGTH_FALLBACK)), - - ); + const maxPostSize = observeConfigIntValue(database, 'MaxPostSize', MAX_MESSAGE_LENGTH_FALLBACK); const hasFilesAttached = post.files.observe().pipe(switchMap((files) => of$(files?.length > 0))); diff --git a/app/screens/edit_profile/index.ts b/app/screens/edit_profile/index.ts index 3b03d13cfb..271595a122 100644 --- a/app/screens/edit_profile/index.ts +++ b/app/screens/edit_profile/index.ts @@ -6,47 +6,40 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeConfig} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {WithDatabaseArgs} from '@typings/database/database'; -import SystemModel from '@typings/database/models/servers/system'; -import UserModel from '@typings/database/models/servers/user'; import EditProfile from './edit_profile'; -const {SERVER: {SYSTEM, USER}} = MM_TABLES; - const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { - const config = database.get<SystemModel>(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG); + const config = observeConfig(database); return { - currentUser: database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap( - (id) => database.get<UserModel>(USER).findAndObserve(id.value), - ), - ), + currentUser: observeCurrentUser(database), lockedFirstName: config.pipe( switchMap( - ({value}: {value: ClientConfig}) => of$(value.LdapFirstNameAttributeSet === 'true' || value.SamlFirstNameAttributeSet === 'true'), + (cfg) => of$(cfg?.LdapFirstNameAttributeSet === 'true' || cfg?.SamlFirstNameAttributeSet === 'true'), ), ), lockedLastName: config.pipe( switchMap( - ({value}: {value: ClientConfig}) => of$(value.LdapLastNameAttributeSet === 'true' || value.SamlLastNameAttributeSet === 'true'), + (cfg) => of$(cfg?.LdapLastNameAttributeSet === 'true' || cfg?.SamlLastNameAttributeSet === 'true'), ), ), lockedNickname: config.pipe( switchMap( - ({value}: {value: ClientConfig}) => of$(value.LdapNicknameAttributeSet === 'true' || value.SamlNicknameAttributeSet === 'true'), + (cfg) => of$(cfg?.LdapNicknameAttributeSet === 'true' || cfg?.SamlNicknameAttributeSet === 'true'), ), ), lockedPosition: config.pipe( switchMap( - ({value}: {value: ClientConfig}) => of$(value.LdapPositionAttributeSet === 'true' || value.SamlPositionAttributeSet === 'true'), + (cfg) => of$(cfg?.LdapPositionAttributeSet === 'true' || cfg?.SamlPositionAttributeSet === 'true'), ), ), lockedPicture: config.pipe( switchMap( - ({value}: {value: ClientConfig}) => of$(value.LdapPictureAttributeSet === 'true'), + (cfg) => of$(cfg?.LdapPictureAttributeSet === 'true'), ), ), }; diff --git a/app/screens/gallery/document_renderer/index.ts b/app/screens/gallery/document_renderer/index.ts index b41bcf2485..df8ffefa02 100644 --- a/app/screens/gallery/document_renderer/index.ts +++ b/app/screens/gallery/document_renderer/index.ts @@ -6,22 +6,16 @@ import withObservables from '@nozbe/with-observables'; import {combineLatest, of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeConfigBooleanValue, observeLicense} from '@queries/servers/system'; import DocumentRenderer from './document_renderer'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; - -const {CONFIG, LICENSE} = SYSTEM_IDENTIFIERS; -const {SERVER: {SYSTEM}} = MM_TABLES; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { - const enableMobileFileDownload = database.get<SystemModel>(SYSTEM).findAndObserve(CONFIG).pipe( - switchMap(({value}) => of$(value.EnableMobileFileDownload !== 'false')), - ); - const complianceDisabled = database.get<SystemModel>(SYSTEM).findAndObserve(LICENSE).pipe( - switchMap(({value}) => of$(value.IsLicensed === 'false' || value.Compliance === 'false')), + const enableMobileFileDownload = observeConfigBooleanValue(database, 'EnableMobileFileDownload'); + const complianceDisabled = observeLicense(database).pipe( + switchMap((lcs) => of$(lcs?.IsLicensed === 'false' || lcs?.Compliance === 'false')), ); const canDownloadFiles = combineLatest([enableMobileFileDownload, complianceDisabled]).pipe( switchMap(([download, compliance]) => of$(compliance || download)), diff --git a/app/screens/gallery/footer/download_with_action/index.tsx b/app/screens/gallery/footer/download_with_action/index.tsx index b10f5da36e..a150e6c75c 100644 --- a/app/screens/gallery/footer/download_with_action/index.tsx +++ b/app/screens/gallery/footer/download_with_action/index.tsx @@ -13,7 +13,6 @@ import {useAnimatedStyle, withTiming} from 'react-native-reanimated'; import Share from 'react-native-share'; import {downloadFile} from '@actions/remote/file'; -import {typography} from '@app/utils/typography'; import CompassIcon from '@components/compass_icon'; import ProgressBar from '@components/progress_bar'; import Toast from '@components/toast'; @@ -22,6 +21,7 @@ import {useServerUrl} from '@context/server'; import {alertFailedToOpenDocument} from '@utils/document'; import {fileExists, getLocalFilePathFromFile, hasWriteStoragePermission} from '@utils/file'; import {galleryItemToFileInfo} from '@utils/gallery'; +import {typography} from '@utils/typography'; import type {ClientResponse, ProgressPromise} from '@mattermost/react-native-network-client'; diff --git a/app/screens/gallery/footer/index.ts b/app/screens/gallery/footer/index.ts index c2f36c9f46..4afd4b6545 100644 --- a/app/screens/gallery/footer/index.ts +++ b/app/screens/gallery/footer/index.ts @@ -1,60 +1,40 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {combineLatest, of as of$, Observable} from 'rxjs'; +import {combineLatest, of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {General, Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; -import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; +import {General} from '@constants'; +import {observeChannel} from '@queries/servers/channel'; +import {observePost} from '@queries/servers/post'; +import {observeConfig, observeCurrentChannelId, observeCurrentUserId, observeLicense} from '@queries/servers/system'; +import {observeTeammateNameDisplay, observeUser} from '@queries/servers/user'; import Footer from './footer'; 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'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; type FooterProps = WithDatabaseArgs & { item: GalleryItemType; } -const {CONFIG, CURRENT_CHANNEL_ID, CURRENT_USER_ID, LICENSE} = SYSTEM_IDENTIFIERS; -const {SERVER: {CHANNEL, POST, PREFERENCE, SYSTEM, USER}} = MM_TABLES; - const enhanced = withObservables(['item'], ({database, item}: FooterProps) => { - const post: Observable<PostModel|undefined> = - item.postId ? database.get<PostModel>(POST).findAndObserve(item.postId) : of$(undefined); + const post = item.postId ? observePost(database, item.postId) : of$(undefined); + const currentChannelId = observeCurrentChannelId(database); + const currentUserId = observeCurrentUserId(database); - const currentChannelId = database.get<SystemModel>(SYSTEM).findAndObserve(CURRENT_CHANNEL_ID).pipe( - switchMap(({value}) => of$(value)), - ); - - const currentUserId = database.get<SystemModel>(SYSTEM).findAndObserve(CURRENT_USER_ID).pipe( - switchMap(({value}) => of$(value)), - ); - - const config = database.get<SystemModel>(SYSTEM).findAndObserve(CONFIG).pipe( - switchMap(({value}) => of$(value as ClientConfig)), - ); - const license = database.get<SystemModel>(SYSTEM).findAndObserve(LICENSE).pipe( - switchMap(({value}) => of$(value as ClientLicense)), - ); - const preferences = database.get<PreferenceModel>(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS)).observe(); - const teammateNameDisplay = combineLatest([preferences, config, license]).pipe( - switchMap(([prefs, cfg, lcs]) => of$(getTeammateNameDisplaySetting(prefs, cfg, lcs))), - ); + const config = observeConfig(database); + const license = observeLicense(database); + const teammateNameDisplay = observeTeammateNameDisplay(database); const author = post.pipe( switchMap((p) => { const id = p?.userId || item.authorId; if (id) { - return database.get<UserModel>(USER).findAndObserve(id); + return observeUser(database, id); } return of$(undefined); @@ -63,14 +43,14 @@ const enhanced = withObservables(['item'], ({database, item}: FooterProps) => { const channel = combineLatest([currentChannelId, post]).pipe( switchMap(([cId, p]) => { - return p?.channel.observe() || database.get<ChannelModel>(CHANNEL).findAndObserve(cId); + return p?.channel.observe() || observeChannel(database, cId); }), ); - const enablePostUsernameOverride = config.pipe(switchMap((c) => of$(c.EnablePostUsernameOverride === 'true'))); - const enablePostIconOverride = config.pipe(switchMap((c) => of$(c.EnablePostIconOverride === 'true'))); - const enablePublicLink = config.pipe(switchMap((c) => of$(c.EnablePublicLink === 'true'))); - const enableMobileFileDownload = config.pipe(switchMap((c) => of$(c.EnableMobileFileDownload !== 'false'))); - const complianceDisabled = license.pipe(switchMap((l) => of$(l.IsLicensed === 'false' || l.Compliance === 'false'))); + const enablePostUsernameOverride = config.pipe(switchMap((c) => of$(c?.EnablePostUsernameOverride === 'true'))); + const enablePostIconOverride = config.pipe(switchMap((c) => of$(c?.EnablePostIconOverride === 'true'))); + const enablePublicLink = config.pipe(switchMap((c) => of$(c?.EnablePublicLink === 'true'))); + const enableMobileFileDownload = config.pipe(switchMap((c) => of$(c?.EnableMobileFileDownload !== 'false'))); + const complianceDisabled = license.pipe(switchMap((l) => of$(l?.IsLicensed === 'false' || l?.Compliance === 'false'))); const canDownloadFiles = combineLatest([enableMobileFileDownload, complianceDisabled]).pipe( switchMap(([download, compliance]) => of$(compliance || download)), ); diff --git a/app/screens/home/account/index.tsx b/app/screens/home/account/index.tsx index 4576878a7c..c4d16027d6 100644 --- a/app/screens/home/account/index.tsx +++ b/app/screens/home/account/index.tsx @@ -12,9 +12,10 @@ import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {View as ViewConstants} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {useTheme} from '@context/theme'; import {useIsTablet} from '@hooks/device'; +import {observeConfig} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {isCustomStatusExpirySupported, isMinimumServerVersion} from '@utils/helpers'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; @@ -23,7 +24,6 @@ import AccountTabletView from './components/tablet_view'; import AccountUserInfo from './components/user_info'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; import type UserModel from '@typings/database/models/servers/user'; type AccountScreenProps = { @@ -33,7 +33,6 @@ type AccountScreenProps = { showFullName: boolean; }; -const {SERVER: {SYSTEM, USER}} = MM_TABLES; const edges: Edge[] = ['left', 'right']; const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { @@ -144,30 +143,20 @@ const AccountScreen = ({currentUser, enableCustomUserStatuses, customStatusExpir }; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { - const config = database. - get<SystemModel>(SYSTEM). - findAndObserve(SYSTEM_IDENTIFIERS.CONFIG); - const showFullName = config.pipe((switchMap((cfg) => of$((cfg.value as ClientConfig).ShowFullName === 'true')))); + const config = observeConfig(database); + const showFullName = config.pipe((switchMap((cfg) => of$(cfg?.ShowFullName === 'true')))); const enableCustomUserStatuses = config.pipe((switchMap((cfg) => { - const ClientConfig = cfg.value as ClientConfig; - return of$(ClientConfig.EnableCustomUserStatuses === 'true' && isMinimumServerVersion(ClientConfig.Version, 5, 36)); + return of$(cfg?.EnableCustomUserStatuses === 'true' && isMinimumServerVersion(cfg?.Version || '', 5, 36)); }))); const version = config.pipe( - switchMap((cfg) => of$((cfg.value as ClientConfig).Version)), + switchMap((cfg) => of$(cfg?.Version || '')), ); const customStatusExpirySupported = config.pipe( - switchMap((cfg) => of$(isCustomStatusExpirySupported((cfg.value as ClientConfig).Version))), + switchMap((cfg) => of$(isCustomStatusExpirySupported(cfg?.Version || ''))), ); return { - currentUser: database. - get(SYSTEM). - findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID). - pipe( - switchMap((id: SystemModel) => - database.get(USER).findAndObserve(id.value), - ), - ), + currentUser: observeCurrentUser(database), enableCustomUserStatuses, customStatusExpirySupported, showFullName, diff --git a/app/screens/home/channel_list/index.ts b/app/screens/home/channel_list/index.ts index d2a42724a6..5757249f91 100644 --- a/app/screens/home/channel_list/index.ts +++ b/app/screens/home/channel_list/index.ts @@ -3,27 +3,19 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {catchError, of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {queryAllMyChannel} from '@queries/servers/channel'; +import {observeCurrentTeamId} from '@queries/servers/system'; +import {queryMyTeams} from '@queries/servers/team'; import ChannelsList from './channel_list'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type MyChannelModel from '@typings/database/models/servers/my_channel'; -import type MyTeamModel from '@typings/database/models/servers/my_team'; -import type SystemModel from '@typings/database/models/servers/system'; - -const {SERVER: {MY_CHANNEL, MY_TEAM, SYSTEM}} = MM_TABLES; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({ - currentTeamId: database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID).pipe( - switchMap((id) => of$(id.value)), - catchError(() => of$(undefined)), - ), - teamsCount: database.get<MyTeamModel>(MY_TEAM).query().observeCount(), - channelsCount: database.get<MyChannelModel>(MY_CHANNEL).query().observeCount(), + currentTeamId: observeCurrentTeamId(database), + teamsCount: queryMyTeams(database).observeCount(), + channelsCount: queryAllMyChannel(database).observeCount(), })); export default withDatabase(enhanced(ChannelsList)); diff --git a/app/screens/home/channel_list/servers/servers_list/server_item/websocket/index.ts b/app/screens/home/channel_list/servers/servers_list/server_item/websocket/index.ts index 193c2cfb9d..8356dcf2b3 100644 --- a/app/screens/home/channel_list/servers/servers_list/server_item/websocket/index.ts +++ b/app/screens/home/channel_list/servers/servers_list/server_item/websocket/index.ts @@ -5,21 +5,16 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeWebsocket} from '@queries/servers/system'; import WebSocket from './websocket'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; - -const {SERVER: {SYSTEM}} = MM_TABLES; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({ - isConnected: database.get<SystemModel>(SYSTEM). - findAndObserve(SYSTEM_IDENTIFIERS.WEBSOCKET). - pipe( - switchMap(({value}) => of$(parseInt(value || 0, 10) > 0)), - ), + isConnected: observeWebsocket(database).pipe( + switchMap((value) => of$(value > 0)), + ), })); export default enhanced(WebSocket); diff --git a/app/screens/home/recent_mentions/index.ts b/app/screens/home/recent_mentions/index.ts index 4450b0672c..a87961806e 100644 --- a/app/screens/home/recent_mentions/index.ts +++ b/app/screens/home/recent_mentions/index.ts @@ -8,41 +8,30 @@ import compose from 'lodash/fp/compose'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {SYSTEM_IDENTIFIERS, MM_TABLES} from '@constants/database'; -import {SystemModel, UserModel} from '@database/models/server'; +import {queryPostsById} from '@queries/servers/post'; +import {observeConfigBooleanValue, observeRecentMentions} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {getTimezone} from '@utils/user'; import RecentMentionsScreen from './recent_mentions'; import type {WithDatabaseArgs} from '@typings/database/database'; -const {USER, SYSTEM, POST} = MM_TABLES.SERVER; - const enhance = withObservables([], ({database}: WithDatabaseArgs) => { - const currentUser = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap((currentUserId) => database.get<UserModel>(USER).findAndObserve(currentUserId.value)), - ); + const currentUser = observeCurrentUser(database); return { - mentions: database.get<SystemModel>(SYSTEM).query( - Q.where('id', SYSTEM_IDENTIFIERS.RECENT_MENTIONS), - Q.take(1), - ).observeWithColumns(['value']).pipe( - switchMap((rows) => { - if (!rows.length || !rows[0].value.length) { + mentions: observeRecentMentions(database).pipe( + switchMap((recentMentions) => { + if (!recentMentions.length) { return of$([]); } - const row = rows[0]; - return database.get(POST).query( - Q.where('id', Q.oneOf(row.value)), - Q.sortBy('create_at', Q.asc), - ).observe(); + return queryPostsById(database, recentMentions, Q.asc).observe(); }), ), - currentTimezone: currentUser.pipe((switchMap((user) => of$(getTimezone(user.timezone))))), - isTimezoneEnabled: database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap((config) => of$(config.value.ExperimentalTimezone === 'true')), - ), + currentUser, + currentTimezone: currentUser.pipe((switchMap((user) => of$(getTimezone(user?.timezone || null))))), + isTimezoneEnabled: observeConfigBooleanValue(database, 'ExperimentalTimezone'), }; }); diff --git a/app/screens/home/recent_mentions/recent_mentions.tsx b/app/screens/home/recent_mentions/recent_mentions.tsx index 460dcdcf90..7b08fb6200 100644 --- a/app/screens/home/recent_mentions/recent_mentions.tsx +++ b/app/screens/home/recent_mentions/recent_mentions.tsx @@ -9,9 +9,9 @@ import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-nati import {SafeAreaView, Edge} from 'react-native-safe-area-context'; import {fetchRecentMentions} from '@actions/remote/search'; -import PostWithChannelInfo from '@app/components/post_with_channel_info'; import NavigationHeader from '@components/navigation_header'; import DateSeparator from '@components/post_list/date_separator'; +import PostWithChannelInfo from '@components/post_with_channel_info'; import {Events, Screens} from '@constants'; import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; diff --git a/app/screens/home/saved_posts/index.ts b/app/screens/home/saved_posts/index.ts index 825e65551f..7562b9f4c9 100644 --- a/app/screens/home/saved_posts/index.ts +++ b/app/screens/home/saved_posts/index.ts @@ -7,31 +7,27 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {Preferences} from '@app/constants'; -import {SYSTEM_IDENTIFIERS, MM_TABLES} from '@constants/database'; -import {SystemModel, UserModel, PreferenceModel} from '@database/models/server'; +import {Preferences} from '@constants'; +import {PreferenceModel} from '@database/models/server'; +import {queryPostsById} from '@queries/servers/post'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {observeConfigBooleanValue} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {getTimezone} from '@utils/user'; import SavedMessagesScreen from './saved_posts'; import type {WithDatabaseArgs} from '@typings/database/database'; -const {USER, SYSTEM, POST, PREFERENCE} = MM_TABLES.SERVER; - function getPostIDs(preferences: PreferenceModel[]) { return preferences.map((preference) => preference.name); } const enhance = withObservables([], ({database}: WithDatabaseArgs) => { - const currentUser = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap((currentUserId) => database.get<UserModel>(USER).findAndObserve(currentUserId.value)), - ); + const currentUser = observeCurrentUser(database); return { - posts: database.get<PreferenceModel>(PREFERENCE).query( - Q.where('category', Preferences.CATEGORY_SAVED_POST), - Q.where('value', 'true'), - ).observeWithColumns(['name']).pipe( + posts: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SAVED_POST, undefined, 'true').observeWithColumns(['name']).pipe( switchMap((rows) => { if (!rows.length) { return of$([]); @@ -39,16 +35,11 @@ const enhance = withObservables([], ({database}: WithDatabaseArgs) => { return of$(getPostIDs(rows)); }), switchMap((ids) => { - return database.get(POST).query( - Q.where('id', Q.oneOf(ids)), - Q.sortBy('create_at', Q.asc), - ).observe(); + return queryPostsById(database, ids, Q.asc).observe(); }), ), - currentTimezone: currentUser.pipe((switchMap((user) => of$(getTimezone(user.timezone))))), - isTimezoneEnabled: database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap((config) => of$(config.value.ExperimentalTimezone === 'true')), - ), + currentTimezone: currentUser.pipe((switchMap((user) => of$(getTimezone(user?.timezone || null))))), + isTimezoneEnabled: observeConfigBooleanValue(database, 'ExperimentalTimezone'), }; }); diff --git a/app/screens/home/tab_bar/account.tsx b/app/screens/home/tab_bar/account.tsx index 92be8c6804..b9bd4fb9ce 100644 --- a/app/screens/home/tab_bar/account.tsx +++ b/app/screens/home/tab_bar/account.tsx @@ -5,14 +5,12 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import React from 'react'; import {View} from 'react-native'; -import {switchMap} from 'rxjs/operators'; import ProfilePicture from '@components/profile_picture'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeCurrentUser} from '@queries/servers/user'; import {makeStyleSheetFromTheme} from '@utils/theme'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; import type UserModel from '@typings/database/models/servers/user'; type Props = { @@ -21,8 +19,6 @@ type Props = { theme: Theme; } -const {SERVER: {SYSTEM, USER}} = MM_TABLES; - const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ selected: { borderWidth: 2, @@ -46,9 +42,7 @@ const Account = ({currentUser, isFocused, theme}: Props) => { }; const withCurrentUser = withObservables([], ({database}: WithDatabaseArgs) => ({ - currentUser: database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap((id) => database.get(USER).findAndObserve(id.value)), - ), + currentUser: observeCurrentUser(database), })); export default withDatabase(withCurrentUser(Account)); diff --git a/app/screens/in_app_notification/icon.tsx b/app/screens/in_app_notification/icon.tsx index d4a677010f..d5c61e1a36 100644 --- a/app/screens/in_app_notification/icon.tsx +++ b/app/screens/in_app_notification/icon.tsx @@ -6,13 +6,13 @@ import React from 'react'; import {StyleSheet, View} from 'react-native'; import FastImage, {Source} from 'react-native-fast-image'; import {of as of$} from 'rxjs'; -import {catchError, switchMap} from 'rxjs/operators'; +import {switchMap} from 'rxjs/operators'; import CompassIcon from '@components/compass_icon'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import NetworkManager from '@init/network_manager'; +import {observeConfig} from '@queries/servers/system'; +import {observeUser} from '@queries/servers/user'; import {WithDatabaseArgs} from '@typings/database/database'; -import SystemModel from '@typings/database/models/servers/system'; import type {Client} from '@client/rest'; import type UserModel from '@typings/database/models/servers/user'; @@ -26,7 +26,6 @@ interface NotificationIconProps { useUserIcon: boolean; } -const {SERVER: {SYSTEM, USER}} = MM_TABLES; const IMAGE_SIZE = 36; const logo = require('@assets/images/icon.png'); @@ -90,15 +89,13 @@ const NotificationIcon = ({author, enablePostIconOverride, fromWebhook, override }; const enhanced = withObservables([], ({database, senderId}: WithDatabaseArgs & {senderId: string}) => { - const config = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG); - const author = database.get<UserModel>(USER).findAndObserve(senderId).pipe( - catchError(() => of$(undefined)), - ); + const config = observeConfig(database); + const author = observeUser(database, senderId); return { author, enablePostIconOverride: config.pipe( - switchMap(({value}) => of$((value as ClientConfig).EnablePostIconOverride === 'true')), + switchMap((cfg) => of$(cfg?.EnablePostIconOverride === 'true')), ), }; }); diff --git a/app/screens/permalink/index.ts b/app/screens/permalink/index.ts index d87231ced0..29ff8a4f12 100644 --- a/app/screens/permalink/index.ts +++ b/app/screens/permalink/index.ts @@ -1,30 +1,21 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES} from '@app/constants/database'; +import {observePost} from '@queries/servers/post'; import {WithDatabaseArgs} from '@typings/database/database'; import PostModel from '@typings/database/models/servers/post'; -const {POST} = MM_TABLES.SERVER; - import Permalink from './permalink'; type OwnProps = {postId: PostModel['id']} & WithDatabaseArgs; const enhance = withObservables([], ({database, postId}: OwnProps) => { - const post = database.get<PostModel>(POST).query( - Q.where('id', postId), - ).observe().pipe( - switchMap((p) => { - return p.length ? p[0].observe() : of$(undefined); - }), - ); + const post = observePost(database, postId); return { channel: post.pipe( diff --git a/app/screens/permalink/permalink.tsx b/app/screens/permalink/permalink.tsx index cec3ffb557..225f1c6414 100644 --- a/app/screens/permalink/permalink.tsx +++ b/app/screens/permalink/permalink.tsx @@ -8,12 +8,11 @@ import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context'; import {switchToChannelById} from '@actions/remote/channel'; import {fetchPostsAround} from '@actions/remote/post'; -import {Screens} from '@app/constants'; -import {changeOpacity, makeStyleSheetFromTheme} from '@app/utils/theme'; import CompassIcon from '@components/compass_icon'; import FormattedText from '@components/formatted_text'; import Loading from '@components/loading'; import PostList from '@components/post_list'; +import {Screens} from '@constants'; import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; import {dismissModal} from '@screens/navigation'; @@ -21,6 +20,7 @@ import ChannelModel from '@typings/database/models/servers/channel'; import PostModel from '@typings/database/models/servers/post'; import {closePermalink} from '@utils/permalink'; import {preventDoubleTap} from '@utils/tap'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; type Props = { currentUsername: UserProfile['username']; diff --git a/app/screens/post_options/index.ts b/app/screens/post_options/index.ts index 0e7c2fe13c..cc01fd8f3e 100644 --- a/app/screens/post_options/index.ts +++ b/app/screens/post_options/index.ts @@ -1,15 +1,17 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {combineLatest, from as from$, of as of$, Observable} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {General, Permissions, Post, Preferences, Screens} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {MAX_ALLOWED_REACTIONS} from '@constants/emoji'; +import {observePost} from '@queries/servers/post'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {observeConfig, observeLicense} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {isMinimumServerVersion} from '@utils/helpers'; import {isSystemMessage} from '@utils/post'; import {getPostIdsForCombinedUserActivityPost} from '@utils/post_list'; @@ -21,9 +23,7 @@ import PostOptions from './post_options'; 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'; -import type PreferenceModel from '@typings/database/models/servers/preference'; import type ReactionModel from '@typings/database/models/servers/reaction'; -import type SystemModel from '@typings/database/models/servers/system'; import type UserModel from '@typings/database/models/servers/user'; type EnhancedProps = WithDatabaseArgs & { @@ -33,9 +33,6 @@ type EnhancedProps = WithDatabaseArgs & { location: string; } -const {POST, USER, SYSTEM, PREFERENCE} = MM_TABLES.SERVER; -const {CURRENT_USER_ID, LICENSE, CONFIG} = SYSTEM_IDENTIFIERS; - const canEditPost = (isOwner: boolean, post: PostModel, postEditTimeLimit: number, isLicensed: boolean, channel: ChannelModel, user: UserModel): boolean => { if (!post || isSystemMessage(post)) { return false; @@ -67,7 +64,7 @@ const withPost = withObservables([], ({post, database}: {post: Post | PostModel} id = systemPostIds?.pop(); combinedPost = of$(post as Post); } - const thePost = id ? database.get<PostModel>(POST).findAndObserve(id) : post; + const thePost = id ? observePost(database, id) : post; return { combinedPost, post: thePost, @@ -77,23 +74,23 @@ const withPost = withObservables([], ({post, database}: {post: Post | PostModel} const enhanced = withObservables([], ({combinedPost, post, showAddReaction, location, database}: EnhancedProps) => { const channel = post.channel.observe(); const channelIsArchived = channel.pipe(switchMap((ch: ChannelModel) => of$(ch.deleteAt !== 0))); - const currentUser = database.get<SystemModel>(SYSTEM).findAndObserve(CURRENT_USER_ID).pipe(switchMap(({value}) => database.get<UserModel>(USER).findAndObserve(value))); - const config = database.get<SystemModel>(SYSTEM).findAndObserve(CONFIG).pipe(switchMap(({value}) => of$(value as ClientConfig))); - const isLicensed = database.get<SystemModel>(SYSTEM).findAndObserve(LICENSE).pipe(switchMap(({value}) => of$(value.IsLicensed === 'true'))); - const allowEditPost = config.pipe(switchMap((cfg) => of$(cfg.AllowEditPost))); - const serverVersion = config.pipe(switchMap((cfg) => cfg.Version)); - const postEditTimeLimit = config.pipe(switchMap((cfg) => of$(parseInt(cfg.PostEditTimeLimit || '-1', 10)))); + const currentUser = observeCurrentUser(database); + const config = observeConfig(database); + const isLicensed = observeLicense(database).pipe(switchMap((lcs) => of$(lcs?.IsLicensed === 'true'))); + const allowEditPost = config.pipe(switchMap((cfg) => of$(cfg?.AllowEditPost))); + 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]) => from$(hasPermissionForChannel(c, u, Permissions.CREATE_POST, false)))); - const hasAddReactionPermission = currentUser.pipe(switchMap((u) => from$(hasPermissionForPost(post, u, Permissions.ADD_REACTION, true)))); + const canPostPermission = combineLatest([channel, currentUser]).pipe(switchMap(([c, u]) => ((c && u) ? from$(hasPermissionForChannel(c, u, Permissions.CREATE_POST, false)) : of$(false)))); + const hasAddReactionPermission = currentUser.pipe(switchMap((u) => (u ? from$(hasPermissionForPost(post, u, Permissions.ADD_REACTION, true)) : of$(false)))); const canDeletePostPermission = currentUser.pipe(switchMap((u) => { - const isOwner = post.userId === u.id; - return from$(hasPermissionForPost(post, u, isOwner ? Permissions.DELETE_POST : Permissions.DELETE_OTHERS_POSTS, false)); + const isOwner = post.userId === u?.id; + return u ? from$(hasPermissionForPost(post, u, isOwner ? Permissions.DELETE_POST : Permissions.DELETE_OTHERS_POSTS, false)) : of$(false); })); - const experimentalTownSquareIsReadOnly = config.pipe(switchMap((value) => of$(value.ExperimentalTownSquareIsReadOnly === 'true'))); + const experimentalTownSquareIsReadOnly = config.pipe(switchMap((value) => of$(value?.ExperimentalTownSquareIsReadOnly === 'true'))); const channelIsReadOnly = combineLatest([currentUser, channel, experimentalTownSquareIsReadOnly]).pipe(switchMap(([u, c, readOnly]) => { - return of$(c?.name === General.DEFAULT_CHANNEL && !isSystemAdmin(u.roles) && readOnly); + return of$(c?.name === General.DEFAULT_CHANNEL && (u && !isSystemAdmin(u.roles)) && readOnly); })); const isUnderMaxAllowedReactions = post.reactions.observe().pipe( @@ -118,17 +115,17 @@ const enhanced = withObservables([], ({combinedPost, post, showAddReaction, loca return of$(!isSystemMessage(post) && !isArchived && !isReadOnly); })); - const isSaved = database.get<PreferenceModel>(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_SAVED_POST), Q.where('name', post.id)).observe().pipe(switchMap((pref) => of$(Boolean(pref[0]?.value === 'true')))); + const isSaved = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SAVED_POST, post.id).observe().pipe(switchMap((pref) => of$(Boolean(pref[0]?.value === 'true')))); 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 = canEditPost(isOwner, post, lt, ls, c, u); + const isOwner = u?.id === post.userId; + const canEditPostPermission = (c && u) ? canEditPost(isOwner, post, lt, ls, c, u) : false; const timeNotReached = (until === -1) || (until > Date.now()); return of$(canEditPostPermission && !isArchived && !isReadOnly && timeNotReached && canPost); })); const canMarkAsUnread = combineLatest([currentUser, channelIsArchived]).pipe( - switchMap(([user, isArchived]) => of$(!isArchived && user.id !== post.userId && !isSystemMessage(post))), + switchMap(([user, isArchived]) => of$(!isArchived && user?.id !== post.userId && !isSystemMessage(post))), ); const canAddReaction = combineLatest([hasAddReactionPermission, channelIsReadOnly, isUnderMaxAllowedReactions, channelIsArchived]).pipe( diff --git a/app/screens/post_options/options/copy_permalink_option/index.tsx b/app/screens/post_options/options/copy_permalink_option/index.tsx index fa1109e561..afffeb18a2 100644 --- a/app/screens/post_options/options/copy_permalink_option/index.tsx +++ b/app/screens/post_options/options/copy_permalink_option/index.tsx @@ -6,28 +6,23 @@ import withObservables from '@nozbe/with-observables'; import {combineLatest, of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeCurrentTeamId} from '@queries/servers/system'; +import {observeTeam} from '@queries/servers/team'; import CopyPermalinkOption from './copy_permalink_option'; import type {WithDatabaseArgs} from '@typings/database/database'; import type PostModel from '@typings/database/models/servers/post'; -import type SystemModel from '@typings/database/models/servers/system'; import type TeamModel from '@typings/database/models/servers/team'; -const {SERVER: {SYSTEM, TEAM}} = MM_TABLES; -const {CURRENT_TEAM_ID} = SYSTEM_IDENTIFIERS; - const enhanced = withObservables(['post'], ({post, database}: WithDatabaseArgs & { post: PostModel }) => { - const currentTeamId = database.get<SystemModel>(SYSTEM).findAndObserve(CURRENT_TEAM_ID); + const currentTeamId = observeCurrentTeamId(database); const channel = post.channel.observe(); const teamName = combineLatest([channel, currentTeamId]).pipe( switchMap(([c, tid]) => { - const teamId = c.teamId || tid.value; - return database. - get<TeamModel>(TEAM). - findAndObserve(teamId). + const teamId = c?.teamId || tid; + return observeTeam(database, teamId). pipe( // eslint-disable-next-line max-nested-callbacks switchMap((team: TeamModel) => of$(team.name)), diff --git a/app/screens/post_options/reaction_bar/index.ts b/app/screens/post_options/reaction_bar/index.ts index 8bc576604f..9b5b75b910 100644 --- a/app/screens/post_options/reaction_bar/index.ts +++ b/app/screens/post_options/reaction_bar/index.ts @@ -4,15 +4,13 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; -import {catchError, switchMap} from 'rxjs/operators'; +import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; -import {safeParseJSON} from '@utils/helpers'; +import {observeRecentReactions} from '@queries/servers/system'; import ReactionBar from './reaction_bar'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; const DEFAULT_EMOJIS = [ 'thumbsup', @@ -29,12 +27,9 @@ const mergeRecentWithDefault = (recentEmojis: string[]) => { }; const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({ - recentEmojis: database. - get<SystemModel>(MM_TABLES.SERVER.SYSTEM). - findAndObserve(SYSTEM_IDENTIFIERS.RECENT_REACTIONS). + recentEmojis: observeRecentReactions(database). pipe( - switchMap((recent) => of$(mergeRecentWithDefault(safeParseJSON(recent.value) as string[]))), - catchError(() => of$(mergeRecentWithDefault([]))), + switchMap((recent) => of$(mergeRecentWithDefault(recent))), ), })); diff --git a/app/screens/thread/index.tsx b/app/screens/thread/index.tsx index 5952f1ddd9..cbc5016de1 100644 --- a/app/screens/thread/index.tsx +++ b/app/screens/thread/index.tsx @@ -1,29 +1,18 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; -import {Database} from '@constants'; +import {observePost} from '@queries/servers/post'; import Thread from './thread'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type PostModel from '@typings/database/models/servers/post'; - -const {MM_TABLES} = Database; -const {SERVER: {POST}} = MM_TABLES; const enhanced = withObservables(['rootId'], ({database, rootId}: WithDatabaseArgs & {rootId: string}) => { return { - rootPost: database.get<PostModel>(POST).query( - Q.where('id', rootId), - ).observe().pipe( - switchMap((posts) => posts[0]?.observe() || of$(undefined)), - ), + rootPost: observePost(database, rootId), }; }); diff --git a/app/screens/thread/thread_post_list/index.ts b/app/screens/thread/thread_post_list/index.ts index 54adb9853f..f634311410 100644 --- a/app/screens/thread/thread_post_list/index.ts +++ b/app/screens/thread/thread_post_list/index.ts @@ -1,26 +1,22 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {AppStateStatus} from 'react-native'; import {of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {observeMyChannel} from '@queries/servers/channel'; +import {queryPostsChunk, queryPostsInThread} from '@queries/servers/post'; +import {observeConfigBooleanValue} from '@queries/servers/system'; +import {observeCurrentUser} from '@queries/servers/user'; import {getTimezone} from '@utils/user'; import ThreadPostList from './thread_post_list'; import type {WithDatabaseArgs} from '@typings/database/database'; -import type MyChannelModel from '@typings/database/models/servers/my_channel'; import type PostModel from '@typings/database/models/servers/post'; -import type PostsInThreadModel from '@typings/database/models/servers/posts_in_thread'; -import type SystemModel from '@typings/database/models/servers/system'; -import type UserModel from '@typings/database/models/servers/user'; - -const {SERVER: {MY_CHANNEL, POST, POSTS_IN_THREAD, SYSTEM, USER}} = MM_TABLES; type Props = WithDatabaseArgs & { channelId: string; @@ -29,34 +25,23 @@ type Props = WithDatabaseArgs & { }; const enhanced = withObservables(['channelId', 'forceQueryAfterAppState', 'rootPost'], ({channelId, database, rootPost}: Props) => { - const currentUser = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap((currentUserId) => database.get<UserModel>(USER).findAndObserve(currentUserId.value)), - ); + const currentUser = observeCurrentUser(database); return { - currentTimezone: currentUser.pipe((switchMap((user) => of$(getTimezone(user.timezone))))), - currentUsername: currentUser.pipe((switchMap((user) => of$(user.username)))), - isTimezoneEnabled: database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap((config) => of$(config.value.ExperimentalTimezone === 'true')), + currentTimezone: currentUser.pipe((switchMap((user) => of$(getTimezone(user?.timezone || null))))), + currentUsername: currentUser.pipe((switchMap((user) => of$(user?.username || '')))), + isTimezoneEnabled: observeConfigBooleanValue(database, 'ExperimentalTimezone'), + lastViewedAt: observeMyChannel(database, channelId).pipe( + switchMap((myChannel) => of$(myChannel?.viewedAt)), ), - lastViewedAt: database.get<MyChannelModel>(MY_CHANNEL).findAndObserve(channelId).pipe( - switchMap((myChannel) => of$(myChannel.viewedAt)), - ), - posts: database.get<PostsInThreadModel>(POSTS_IN_THREAD).query( - Q.where('root_id', rootPost.id), - Q.sortBy('latest', Q.desc), - ).observeWithColumns(['earliest', 'latest']).pipe( + posts: queryPostsInThread(database, rootPost.id, true, true).observeWithColumns(['earliest', 'latest']).pipe( switchMap((postsInThread) => { if (!postsInThread.length) { return of$([]); } const {earliest, latest} = postsInThread[0]; - return database.get<PostModel>(POST).query( - Q.where('root_id', rootPost.id), - Q.where('create_at', Q.between(earliest, latest)), - Q.sortBy('create_at', Q.desc), - ).observe(); + return queryPostsChunk(database, rootPost.id, earliest, latest, true).observe(); }), ), }; diff --git a/app/utils/apps.ts b/app/utils/apps.ts index f9d65ee85a..fc4de5196e 100644 --- a/app/utils/apps.ts +++ b/app/utils/apps.ts @@ -2,11 +2,6 @@ // See LICENSE.txt for license information. import {AppBindingLocations, AppCallResponseTypes} from '@constants/apps'; -export function appsEnabled(config: Partial<ClientConfig>) { // eslint-disable-line @typescript-eslint/no-unused-vars - const enabled = config['FeatureFlagAppsEnabled' as keyof Partial<ClientConfig>]; - return enabled === 'true'; -} - export function copyAndFillBindings(binding?: AppBinding): AppBinding | undefined { if (!binding) { return undefined; diff --git a/app/utils/role/index.ts b/app/utils/role/index.ts index 8061afbb6a..21a9feaca4 100644 --- a/app/utils/role/index.ts +++ b/app/utils/role/index.ts @@ -1,10 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; - import {General, Permissions} from '@constants'; -import {MM_TABLES} from '@constants/database'; +import {queryRolesByNames} from '@queries/servers/role'; import type ChannelModel from '@typings/database/models/servers/channel'; import type MyChannelModel from '@typings/database/models/servers/my_channel'; @@ -41,7 +39,7 @@ export async function hasPermissionForChannel(channel: ChannelModel, user: UserM } if (rolesArray.length) { - const roles = await user.collections.get(MM_TABLES.SERVER.ROLE).query(Q.where('name', Q.oneOf(rolesArray))).fetch() as RoleModel[]; + const roles = await queryRolesByNames(user.database, rolesArray).fetch(); return hasPermission(roles, permission, defaultValue); } @@ -57,7 +55,7 @@ export async function hasPermissionForTeam(team: TeamModel, user: UserModel, per } if (rolesArray.length) { - const roles = await user.collections.get(MM_TABLES.SERVER.ROLE).query(Q.where('name', Q.oneOf(rolesArray))).fetch() as RoleModel[]; + const roles = await queryRolesByNames(user.database, rolesArray).fetch(); return hasPermission(roles, permission, defaultValue); } @@ -77,7 +75,7 @@ export async function canManageChannelMembers(post: PostModel, user: UserModel) const rolesArray = [...user.roles.split(' ')]; const channel = await post.channel.fetch() as ChannelModel | undefined; - if (!channel || channel.deleteAt !== 0 || [General.DM_CHANNEL, General.GM_CHANNEL].includes(channel.type) || channel.name === General.DEFAULT_CHANNEL) { + if (!channel || channel.deleteAt !== 0 || [General.DM_CHANNEL, General.GM_CHANNEL].includes(channel.type as any) || channel.name === General.DEFAULT_CHANNEL) { return false; } @@ -95,7 +93,7 @@ export async function canManageChannelMembers(post: PostModel, user: UserModel) } if (rolesArray.length) { - const roles = await post.collections.get(MM_TABLES.SERVER.ROLE).query(Q.where('name', Q.oneOf(rolesArray))).fetch() as RoleModel[]; + const roles = await queryRolesByNames(post.database, rolesArray).fetch() as RoleModel[]; const permission = channel.type === General.OPEN_CHANNEL ? Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS : Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS; return hasPermission(roles, permission, true); } diff --git a/types/database/models/servers/category.d.ts b/types/database/models/servers/category.d.ts index 5aac68eff7..7a9477d647 100644 --- a/types/database/models/servers/category.d.ts +++ b/types/database/models/servers/category.d.ts @@ -6,6 +6,11 @@ import {lazy} from '@nozbe/watermelondb/decorators'; import Model, {Associations} from '@nozbe/watermelondb/Model'; import {Observable} from 'rxjs'; +import type CategoryChannelModel from './category_channel'; +import type ChannelModel from './channel'; +import type MyChannelModel from './my_channel'; +import type TeamModel from './team'; + /** * A Category groups together channels for a user in a team. */ diff --git a/types/database/models/servers/category_channel.d.ts b/types/database/models/servers/category_channel.d.ts index 00284134fe..4d9ff8f663 100644 --- a/types/database/models/servers/category_channel.d.ts +++ b/types/database/models/servers/category_channel.d.ts @@ -4,6 +4,10 @@ import {Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type CategoryModel from './category'; +import type ChannelModel from './channel'; +import type MyChannelModel from './my_channel'; + /** * The CategoryChannel model represents the 'association table' where many categories have channels and many channels are in * categories (relationship type N:N) diff --git a/types/database/models/servers/channel.d.ts b/types/database/models/servers/channel.d.ts index 47e673de9f..6a3dca5ec3 100644 --- a/types/database/models/servers/channel.d.ts +++ b/types/database/models/servers/channel.d.ts @@ -4,6 +4,17 @@ import {Query, Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type CategoryChannelModel from './category_channel'; +import type ChannelInfoModel from './channel_info'; +import type ChannelMembershipModel from './channel_membership'; +import type DraftModel from './draft'; +import type MyChannelModel from './my_channel'; +import type MyChannelSettingsModel from './my_channel_settings'; +import type PostModel from './post'; +import type PostsInChannelModel from './posts_in_channel'; +import type TeamModel from './team'; +import type UserModel from './user'; + /** * The Channel model represents a channel in the Mattermost app. */ @@ -72,7 +83,7 @@ export default class ChannelModel extends Model { settings: Relation<MyChannelSettingsModel>; /** categoryChannel: category of this channel */ - categoryChannel: Relation<CategoryChanelModel>; + categoryChannel: Relation<CategoryChannelModel>; toApi = () => Channel; } diff --git a/types/database/models/servers/channel_info.d.ts b/types/database/models/servers/channel_info.d.ts index f59ac5ecb1..a679cccc39 100644 --- a/types/database/models/servers/channel_info.d.ts +++ b/types/database/models/servers/channel_info.d.ts @@ -4,6 +4,8 @@ import {Relation} from '@nozbe/watermelondb'; import Model from '@nozbe/watermelondb/Model'; +import type ChannelModel from './channel'; + /** * ChannelInfo is an extension of the information contained in the Channel table. * In a Separation of Concerns approach, ChannelInfo will provide additional information about a channel but on a more diff --git a/types/database/models/servers/channel_membership.d.ts b/types/database/models/servers/channel_membership.d.ts index 516e6395c2..f90b0285a2 100644 --- a/types/database/models/servers/channel_membership.d.ts +++ b/types/database/models/servers/channel_membership.d.ts @@ -4,6 +4,9 @@ import {Query, Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type ChannelModel from './channel'; +import type UserModel from './user'; + /** * The ChannelMembership model represents the 'association table' where many channels have users and many users are on * channels ( N:N relationship between model Users and model Channel) @@ -25,7 +28,7 @@ export default class ChannelMembershipModel extends Model { memberChannel: Relation<ChannelModel>; /** memberUser : The related member belonging to the channel */ - memberUser: Relation<User>; + memberUser: Relation<UserModel>; /** * getAllChannelsForUser - Retrieves all the channels that the user is part of diff --git a/types/database/models/servers/file.d.ts b/types/database/models/servers/file.d.ts index ca2d2b16a0..68e56d9444 100644 --- a/types/database/models/servers/file.d.ts +++ b/types/database/models/servers/file.d.ts @@ -4,6 +4,8 @@ import {Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type PostModel from './post'; + /** * The File model works in pair with the Post model. It hosts information about the files shared in a Post */ diff --git a/types/database/models/servers/my_channel.d.ts b/types/database/models/servers/my_channel.d.ts index fd3270e6ae..8d6cb6c34c 100644 --- a/types/database/models/servers/my_channel.d.ts +++ b/types/database/models/servers/my_channel.d.ts @@ -4,6 +4,8 @@ import {Relation} from '@nozbe/watermelondb'; import Model from '@nozbe/watermelondb/Model'; +import type ChannelModel from './channel'; + /** * MyChannel is an extension of the Channel model but it lists only the Channels the app's user belongs to */ diff --git a/types/database/models/servers/my_channel_settings.d.ts b/types/database/models/servers/my_channel_settings.d.ts index 59af275008..e933b8f962 100644 --- a/types/database/models/servers/my_channel_settings.d.ts +++ b/types/database/models/servers/my_channel_settings.d.ts @@ -4,6 +4,8 @@ import {Relation} from '@nozbe/watermelondb'; import Model from '@nozbe/watermelondb/Model'; +import type ChannelModel from './channel'; + /** * The MyChannelSettings model represents the specific user's configuration to * the channel this user belongs to. diff --git a/types/database/models/servers/my_team.d.ts b/types/database/models/servers/my_team.d.ts index 12882f52eb..6233d8c20d 100644 --- a/types/database/models/servers/my_team.d.ts +++ b/types/database/models/servers/my_team.d.ts @@ -4,6 +4,8 @@ import {Relation} from '@nozbe/watermelondb'; import Model from '@nozbe/watermelondb/Model'; +import type TeamModel from './team'; + /** * MyTeam represents only the teams that the current user belongs to */ diff --git a/types/database/models/servers/post.d.ts b/types/database/models/servers/post.d.ts index 7bf2e7563e..aad0773f4a 100644 --- a/types/database/models/servers/post.d.ts +++ b/types/database/models/servers/post.d.ts @@ -4,6 +4,14 @@ import {Query, Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type ChannelModel from './channel'; +import type DraftModel from './draft'; +import type FileModel from './file'; +import type PostsInThreadModel from './posts_in_thread'; +import type ReactionModel from './reaction'; +import type ThreadModel from './thread'; +import type UserModel from './user'; + /** * The Post model is the building block of communication in the Mattermost app. */ @@ -68,7 +76,7 @@ export default class PostModel extends Model { files: Query<FileModel>; /** postsInThread: Every posts associated to a thread */ - postsInThread: Query<PostInThreadModel>; + postsInThread: Query<PostsInThreadModel>; /** reactions: All the reactions associated with this Post */ reactions: Query<ReactionModel>; diff --git a/types/database/models/servers/posts_in_channel.d.ts b/types/database/models/servers/posts_in_channel.d.ts index 6f71f566a4..63e70b75cb 100644 --- a/types/database/models/servers/posts_in_channel.d.ts +++ b/types/database/models/servers/posts_in_channel.d.ts @@ -4,6 +4,8 @@ import {Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type ChannelModel from './channel'; + /** * PostsInChannel model helps us to combine adjacent posts together without leaving * gaps in between for an efficient user reading experience of posts. diff --git a/types/database/models/servers/posts_in_thread.d.ts b/types/database/models/servers/posts_in_thread.d.ts index 57fdfe7b7e..9a07c46f96 100644 --- a/types/database/models/servers/posts_in_thread.d.ts +++ b/types/database/models/servers/posts_in_thread.d.ts @@ -4,6 +4,8 @@ import {Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type PostModel from './post'; + /** * PostsInThread model helps us to combine adjacent threads together without leaving * gaps in between for an efficient user reading experience for threads. diff --git a/types/database/models/servers/preference.d.ts b/types/database/models/servers/preference.d.ts index 272aff1911..1d6c059c79 100644 --- a/types/database/models/servers/preference.d.ts +++ b/types/database/models/servers/preference.d.ts @@ -4,6 +4,8 @@ import {Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type UserModel from './user'; + /** * The Preference model hold information about the user's preference in the app. * This includes settings about the account, the themes, etc. diff --git a/types/database/models/servers/reaction.d.ts b/types/database/models/servers/reaction.d.ts index 7ebe176991..c6355d113a 100644 --- a/types/database/models/servers/reaction.d.ts +++ b/types/database/models/servers/reaction.d.ts @@ -4,6 +4,9 @@ import {Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type PostModel from './post'; +import type UserModel from './user'; + /** * The Reaction Model is used to present the reactions a user had on a particular post */ diff --git a/types/database/models/servers/slash_command.d.ts b/types/database/models/servers/slash_command.d.ts index e86551c136..31f30d153a 100644 --- a/types/database/models/servers/slash_command.d.ts +++ b/types/database/models/servers/slash_command.d.ts @@ -4,6 +4,8 @@ import {Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type TeamModel from './team'; + /** * The SlashCommand model describes the commands of the various commands available in each team. */ diff --git a/types/database/models/servers/team.d.ts b/types/database/models/servers/team.d.ts index 8080cbbe92..8a68267cef 100644 --- a/types/database/models/servers/team.d.ts +++ b/types/database/models/servers/team.d.ts @@ -1,6 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import type CategoryModel from './category'; +import type ChannelModel from './channel'; +import type MyTeamModel from './my_team'; +import type SlashCommandModel from './slash_command'; +import type TeamChannelHistoryModel from './team_channel_history'; +import type TeamMembershipModel from './team_membership'; +import type TeamSearchHistoryModel from './team_search_history'; import type {Query, Relation} from '@nozbe/watermelondb'; import type Model, {Associations} from '@nozbe/watermelondb/Model'; diff --git a/types/database/models/servers/team_channel_history.d.ts b/types/database/models/servers/team_channel_history.d.ts index bc06bdb95b..69583ffe33 100644 --- a/types/database/models/servers/team_channel_history.d.ts +++ b/types/database/models/servers/team_channel_history.d.ts @@ -4,6 +4,8 @@ import {Relation} from '@nozbe/watermelondb'; import Model from '@nozbe/watermelondb/Model'; +import type TeamModel from './team'; + /** * The TeamChannelHistory model helps keeping track of the last channel visited * by the user. diff --git a/types/database/models/servers/team_membership.d.ts b/types/database/models/servers/team_membership.d.ts index 335d6d9911..d063285aa7 100644 --- a/types/database/models/servers/team_membership.d.ts +++ b/types/database/models/servers/team_membership.d.ts @@ -4,6 +4,9 @@ import {Query, Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type TeamModel from './team'; +import type UserModel from './user'; + /** * The TeamMembership model represents the 'association table' where many teams have users and many users are in * teams (relationship type N:N) diff --git a/types/database/models/servers/team_search_history.d.ts b/types/database/models/servers/team_search_history.d.ts index 46cecbb3aa..808134f3ab 100644 --- a/types/database/models/servers/team_search_history.d.ts +++ b/types/database/models/servers/team_search_history.d.ts @@ -4,6 +4,8 @@ import {Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type TeamModel from './team'; + /** * The TeamSearchHistory model holds the term searched within a team. The searches are performed * at team level in the app. diff --git a/types/database/models/servers/thread.d.ts b/types/database/models/servers/thread.d.ts index 08154e7186..a3612bfd26 100644 --- a/types/database/models/servers/thread.d.ts +++ b/types/database/models/servers/thread.d.ts @@ -4,6 +4,9 @@ import {Query, Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type PostModel from './post'; +import type ThreadParticipantsModel from './thread_participant'; + /** * The Thread model contains thread information of a post. */ diff --git a/types/database/models/servers/thread_participant.d.ts b/types/database/models/servers/thread_participant.d.ts index 1724b67229..d4a7ce03ac 100644 --- a/types/database/models/servers/thread_participant.d.ts +++ b/types/database/models/servers/thread_participant.d.ts @@ -4,6 +4,9 @@ import {Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type ThreadModel from './thread'; +import type UserModel from './user'; + /** * The Thread Participants Model is used to show the participants of a thread */ diff --git a/types/database/models/servers/user.d.ts b/types/database/models/servers/user.d.ts index 392ef57c6e..fe7bb8a2fe 100644 --- a/types/database/models/servers/user.d.ts +++ b/types/database/models/servers/user.d.ts @@ -3,6 +3,15 @@ import Model, {Associations} from '@nozbe/watermelondb/Model'; +import type ChannelModel from './channel'; +import type ChannelMembershipModel from './channel_membership'; +import type PostModel from './post'; +import type PreferenceModel from './preference'; +import type ReactionModel from './reaction'; +import type TeamMembershipModel from './team_membership'; +import type ThreadParticipantsModel from './thread_participant'; +import type {UserMentionKey} from '@typings/global/markdown'; + /** * The User model represents the 'USER' table and its relationship to other * shareholders in the app.