diff --git a/app/actions/remote/entry.ts b/app/actions/remote/entry.ts index d2ddbd612b..d704cd9134 100644 --- a/app/actions/remote/entry.ts +++ b/app/actions/remote/entry.ts @@ -16,6 +16,7 @@ import {addChannelToTeamHistory, prepareMyTeams} from '@queries/servers/team'; import {selectDefaultChannelForTeam} from '@utils/channel'; import {fetchMissingSidebarInfo, fetchMyChannelsForTeam, MyChannelsRequest} from './channel'; +import {fetchGroupsForTeam} from './group'; import {fetchPostsForChannel, fetchPostsForUnreadChannels} from './post'; import {MyPreferencesRequest, fetchMyPreferences} from './preference'; import {fetchRolesIfNeeded} from './role'; @@ -203,9 +204,9 @@ const deferredLoginActions = async ( } // defer groups for team - // if (initialTeam) { - // await fetchGroupsForTeam(serverUrl, initialTeam.id); - // } + if (initialTeam) { + await fetchGroupsForTeam(serverUrl, initialTeam.id); + } // defer fetch channels and unread posts for other teams if (teamData.teams?.length && teamData.memberships?.length) { diff --git a/app/actions/remote/group.ts b/app/actions/remote/group.ts new file mode 100644 index 0000000000..43c7962beb --- /dev/null +++ b/app/actions/remote/group.ts @@ -0,0 +1,133 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Model} from '@nozbe/watermelondb'; + +import DatabaseManager from '@database/manager'; +import NetworkManager from '@init/network_manager'; +import {queryCommonSystemValues, queryWebSocketLastDisconnected} from '@queries/servers/system'; +import {queryTeamById} from '@queries/servers/team'; + +import {forceLogoutIfNecessary} from './session'; + +export const fetchGroupsForTeam = async (serverUrl: string, teamId: string) => { + const database = DatabaseManager.serverDatabases[serverUrl]?.database; + if (!database) { + return {error: `${serverUrl} database not found`}; + } + + let client; + try { + client = NetworkManager.getClient(serverUrl); + } catch (error) { + return {error}; + } + + try { + const system = await queryCommonSystemValues(database); + const team = await queryTeamById(database, teamId); + const hasLicense = system.license.IsLicensed === 'true' && system.license.LDAPGroups === 'true'; + + if (hasLicense && team) { + const groups: Group[] = []; + const groupsChannels: GroupChannelRelation[] = []; + const groupsTeams: GroupTeamRelation[] = []; + const groupMemberships: GroupMembership[] = []; + if (team.isGroupConstrained) { + const [groupsAssociatedToChannelsInTeam, groupsAssociatedToTeam] = await Promise.all<{groups: Record}, {groups: Group[]; total_group_count: number}>([ + client.getAllGroupsAssociatedToChannelsInTeam(teamId, true), + client.getAllGroupsAssociatedToTeam(teamId, true), + ]); + + if (groupsAssociatedToChannelsInTeam.groups) { + const keys = Object.keys(groupsAssociatedToChannelsInTeam.groups); + for (const key of keys) { + for (const group of groupsAssociatedToChannelsInTeam.groups[key]) { + groups.push(group); + groupsChannels.push({ + channel_id: key, + group_id: group.id, + }); + } + } + } + + if (groupsAssociatedToTeam.groups) { + for (const group of groupsAssociatedToTeam.groups) { + groups.push(group); + groupsTeams.push({group_id: group.id, team_id: teamId}); + } + } + } else { + const since = await queryWebSocketLastDisconnected(database); + const [groupsAssociatedToChannelsInTeam, allGroups] = await Promise.all<{groups: Record}, Group[]>([ + client.getAllGroupsAssociatedToChannelsInTeam(teamId, true), + client.getGroups(true, 0, 0, since), + ]); + + if (groupsAssociatedToChannelsInTeam.groups) { + const keys = Object.keys(groupsAssociatedToChannelsInTeam.groups); + for (const key of keys) { + for (const group of groupsAssociatedToChannelsInTeam.groups[key]) { + groups.push(group); + groupsChannels.push({ + channel_id: key, + group_id: group.id, + }); + } + } + } + + if (allGroups?.length) { + groups.push(...allGroups); + } + } + + const userGroups = await client.getGroupsByUserId(system.currentUserId); + if (userGroups) { + for (const mg of userGroups) { + groupMemberships.push({group_id: mg.id, user_id: system.currentUserId}); + groups.push(mg); + } + } + + const models: Model[] = []; + const {operator} = DatabaseManager.serverDatabases[serverUrl]; + if (groups.length) { + const gModels = await operator.handleGroup({groups, prepareRecordsOnly: true}); + if (gModels.length) { + models.push(...gModels); + } + } + + if (groupsChannels.length) { + const gcModels = await operator.handleGroupsChannel({groupsChannels, prepareRecordsOnly: true}); + if (gcModels.length) { + models.push(...gcModels); + } + } + + if (groupsTeams.length) { + const gtModels = await operator.handleGroupsTeam({groupsTeams, prepareRecordsOnly: true}); + if (gtModels.length) { + models.push(...gtModels); + } + } + + if (groupMemberships.length) { + const gmModels = await operator.handleGroupMembership({groupMemberships, prepareRecordsOnly: true}); + if (gmModels.length) { + models.push(...gmModels); + } + } + + if (models.length) { + await operator.batchRecords(models.flat()); + } + } + return null; + } catch (error) { + forceLogoutIfNecessary(serverUrl, error); + return {error}; + } +}; diff --git a/app/client/rest/groups.ts b/app/client/rest/groups.ts index 491723c5cd..0d99cc8db4 100644 --- a/app/client/rest/groups.ts +++ b/app/client/rest/groups.ts @@ -8,7 +8,7 @@ import {PER_PAGE_DEFAULT} from './constants'; export interface ClientGroupsMix { getGroups: (filterAllowReference?: boolean, page?: number, perPage?: number, since?: number) => Promise; getGroupsByUserId: (userID: string) => Promise; - getAllGroupsAssociatedToTeam: (teamID: string, filterAllowReference?: boolean) => Promise<{groups: Group[]; tota_group_count: number}>; + getAllGroupsAssociatedToTeam: (teamID: string, filterAllowReference?: boolean) => Promise<{groups: Group[]; total_group_count: number}>; getAllGroupsAssociatedToChannelsInTeam: (teamID: string, filterAllowReference?: boolean) => Promise<{groups: Record}>; getAllGroupsAssociatedToChannel: (channelID: string, filterAllowReference?: boolean) => Promise; } diff --git a/app/components/markdown/at_mention/index.tsx b/app/components/markdown/at_mention/index.tsx index 0b268b1426..ee2ffbc58b 100644 --- a/app/components/markdown/at_mention/index.tsx +++ b/app/components/markdown/at_mention/index.tsx @@ -22,17 +22,21 @@ import {showModal, showModalOverCurrentContext} from '@screens/navigation'; import {displayUsername, getUserMentionKeys, getUsersByUsername} from '@utils/user'; import type {WithDatabaseArgs} from '@typings/database/database'; +import type GroupModel from '@typings/database/models/servers/group'; +import type GroupMembershipModel from '@typings/database/models/servers/group_membership'; import type PreferenceModel from '@typings/database/models/servers/preference'; import type SystemModel from '@typings/database/models/servers/system'; import type UserModelType from '@typings/database/models/servers/user'; type AtMentionProps = { + currentUserId: SystemModel; database: Database; - groupsByName?: Record; + groups: GroupModel[]; isSearchResult?: boolean; mentionKeys: Array<{key: string }>; mentionName: string; mentionStyle: TextStyle; + myGroups: GroupMembershipModel[]; onPostPress?: (e: GestureResponderEvent) => void; teammateNameDisplay: string; textStyle: StyleProp; @@ -46,7 +50,7 @@ type AtMentionArgs = { mentionName: string; } -const {SERVER: {PREFERENCE, SYSTEM, USER}} = MM_TABLES; +const {SERVER: {GROUP, GROUP_MEMBERSHIP, PREFERENCE, SYSTEM, USER}} = MM_TABLES; const style = StyleSheet.create({ bottomSheet: { @@ -55,12 +59,14 @@ const style = StyleSheet.create({ }); const AtMention = ({ + currentUserId, database, - groupsByName, + groups, isSearchResult, mentionName, mentionKeys, mentionStyle, + myGroups, onPostPress, teammateNameDisplay, textStyle, @@ -90,15 +96,19 @@ const AtMention = ({ return new UserModel(database.get(USER), {username: ''}); }, [users, mentionName]); const userMentionKeys = useMemo(() => { + if (user.id !== currentUserId.value) { + return []; + } + if (mentionKeys) { return mentionKeys; } - return getUserMentionKeys(user); - }, [user, mentionKeys]); + return getUserMentionKeys(user, groups, myGroups); + }, [currentUserId.value, groups, mentionKeys, myGroups, user]); const getGroupFromMentionName = () => { const mentionNameTrimmed = mentionName.toLowerCase().replace(/[._-]*$/, ''); - return groupsByName?.[mentionNameTrimmed]; + return groups.find((g) => g.name === mentionNameTrimmed); }; const goToUserProfile = useCallback(() => { @@ -190,9 +200,8 @@ const AtMention = ({ isMention = true; canPress = true; } else { - // TODO: Add group functionality const group = getGroupFromMentionName(); - if (group?.allow_reference) { + if (group?.allowReference) { highlighted = userMentionKeys.some((item) => item.key === `@${group.name}`); isMention = true; mention = group.name; @@ -249,9 +258,10 @@ const AtMention = ({ }; const withPreferences = withObservables([], ({database}: WithDatabaseArgs) => ({ - preferences: database.get(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS)).observe(), config: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG), + currentUserId: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID), license: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.LICENSE), + preferences: database.get(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS)).observe(), })); const withAtMention = withObservables(['mentionName', 'preferences', 'config', 'license'], ({database, mentionName, preferences, config, license}: WithDatabaseArgs & AtMentionArgs) => { @@ -263,6 +273,8 @@ const withAtMention = withObservables(['mentionName', 'preferences', 'config', ' const teammateNameDisplay = of(getTeammateNameDisplaySetting(preferences, config.value, license.value)); return { + groups: database.get(GROUP).query(Q.where('delete_at', Q.eq(0))).observe(), + myGroups: database.get(GROUP_MEMBERSHIP).query().observe(), teammateNameDisplay, users: database.get(USER).query( Q.where('username', Q.like( diff --git a/app/constants/database.ts b/app/constants/database.ts index b35a38f15d..cde429a5ff 100644 --- a/app/constants/database.ts +++ b/app/constants/database.ts @@ -17,8 +17,8 @@ export const MM_TABLES = { DRAFT: 'Draft', FILE: 'File', GROUP: 'Group', - GROUPS_IN_CHANNEL: 'GroupsInChannel', - GROUPS_IN_TEAM: 'GroupsInTeam', + GROUPS_CHANNEL: 'GroupsChannel', + GROUPS_TEAM: 'GroupsTeam', GROUP_MEMBERSHIP: 'GroupMembership', MY_CHANNEL: 'MyChannel', MY_CHANNEL_SETTINGS: 'MyChannelSettings', diff --git a/app/database/manager/__mocks__/index.ts b/app/database/manager/__mocks__/index.ts index 72283f0556..4ebdf56779 100644 --- a/app/database/manager/__mocks__/index.ts +++ b/app/database/manager/__mocks__/index.ts @@ -16,7 +16,7 @@ import {InfoModel, GlobalModel, ServersModel} from '@database/models/app'; import {schema as appSchema} from '@app/database/schema/app'; import ServerDatabaseMigrations from '@database/migration/server'; import {ChannelModel, ChannelInfoModel, ChannelMembershipModel, CustomEmojiModel, DraftModel, FileModel, - GroupModel, GroupMembershipModel, GroupsInChannelModel, GroupsInTeamModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel, + GroupModel, GroupMembershipModel, GroupsChannelModel, GroupsTeamModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel, PostModel, PostMetadataModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel, SlashCommandModel, SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel, TermsOfServiceModel, UserModel, @@ -51,7 +51,7 @@ class DatabaseManager { this.appModels = [InfoModel, GlobalModel, ServersModel]; this.serverModels = [ ChannelModel, ChannelInfoModel, ChannelMembershipModel, CustomEmojiModel, DraftModel, FileModel, - GroupModel, GroupMembershipModel, GroupsInChannelModel, GroupsInTeamModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel, + GroupModel, GroupMembershipModel, GroupsChannelModel, GroupsTeamModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel, PostModel, PostMetadataModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel, SlashCommandModel, SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel, TermsOfServiceModel, UserModel, diff --git a/app/database/manager/index.ts b/app/database/manager/index.ts index 7b125acea3..aa8446434e 100644 --- a/app/database/manager/index.ts +++ b/app/database/manager/index.ts @@ -16,7 +16,7 @@ import {InfoModel, GlobalModel, ServersModel} from '@database/models/app'; import {schema as appSchema} from '@database/schema/app'; import ServerDatabaseMigrations from '@database/migration/server'; import {ChannelModel, ChannelInfoModel, ChannelMembershipModel, CustomEmojiModel, DraftModel, FileModel, - GroupModel, GroupMembershipModel, GroupsInChannelModel, GroupsInTeamModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel, + GroupModel, GroupMembershipModel, GroupsChannelModel, GroupsTeamModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel, PostModel, PostMetadataModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel, SlashCommandModel, SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel, TermsOfServiceModel, UserModel, @@ -45,7 +45,7 @@ class DatabaseManager { this.appModels = [InfoModel, GlobalModel, ServersModel]; this.serverModels = [ ChannelModel, ChannelInfoModel, ChannelMembershipModel, CustomEmojiModel, DraftModel, FileModel, - GroupModel, GroupMembershipModel, GroupsInChannelModel, GroupsInTeamModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel, + GroupModel, GroupMembershipModel, GroupsChannelModel, GroupsTeamModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel, PostModel, PostMetadataModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel, SlashCommandModel, SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel, TermsOfServiceModel, UserModel, diff --git a/app/database/models/server/channel.ts b/app/database/models/server/channel.ts index 456b74a8af..77b6ee86f5 100644 --- a/app/database/models/server/channel.ts +++ b/app/database/models/server/channel.ts @@ -10,7 +10,7 @@ import {MM_TABLES} from '@constants/database'; import type ChannelInfoModel from '@typings/database/models/servers/channel_info'; import type ChannelMembershipModel from '@typings/database/models/servers/channel_membership'; import type DraftModel from '@typings/database/models/servers/draft'; -import type GroupsInChannelModel from '@typings/database/models/servers/groups_in_channel'; +import type GroupsChannelModel from '@typings/database/models/servers/groups_channel'; import type MyChannelModel from '@typings/database/models/servers/my_channel'; import type MyChannelSettingsModel from '@typings/database/models/servers/my_channel_settings'; import type PostModel from '@typings/database/models/servers/post'; @@ -23,7 +23,7 @@ const { CHANNEL_INFO, CHANNEL_MEMBERSHIP, DRAFT, - GROUPS_IN_CHANNEL, + GROUPS_CHANNEL, MY_CHANNEL, MY_CHANNEL_SETTINGS, POSTS_IN_CHANNEL, @@ -48,8 +48,8 @@ export default class ChannelModel extends Model { /** A CHANNEL can be associated with multiple DRAFT (relationship is 1:N) */ [DRAFT]: {type: 'has_many', foreignKey: 'channel_id'}, - /** A CHANNEL can be associated with multiple GROUPS_IN_CHANNEL (relationship is 1:N) */ - [GROUPS_IN_CHANNEL]: {type: 'has_many', foreignKey: 'channel_id'}, + /** A CHANNEL can be associated with multiple GROUPS_CHANNEL (relationship is 1:N) */ + [GROUPS_CHANNEL]: {type: 'has_many', foreignKey: 'channel_id'}, /** A CHANNEL can be associated with multiple POSTS_IN_CHANNEL (relationship is 1:N) */ [POSTS_IN_CHANNEL]: {type: 'has_many', foreignKey: 'channel_id'}, @@ -100,8 +100,8 @@ export default class ChannelModel extends Model { /** drafts : All drafts for this channel */ @children(DRAFT) drafts!: DraftModel[]; - /** groupsInChannel : Every group contained in this channel */ - @children(GROUPS_IN_CHANNEL) groupsInChannel!: GroupsInChannelModel[]; + /** groupsChannel : Every group contained in this channel */ + @children(GROUPS_CHANNEL) groupsChannel!: GroupsChannelModel[]; /** posts : All posts made in that channel */ @children(POST) posts!: PostModel[]; diff --git a/app/database/models/server/group.ts b/app/database/models/server/group.ts index 7cbbcd6dee..dd51e70796 100644 --- a/app/database/models/server/group.ts +++ b/app/database/models/server/group.ts @@ -7,10 +7,10 @@ import {children, field} from '@nozbe/watermelondb/decorators'; import {MM_TABLES} from '@constants/database'; import type GroupMembershipModel from '@typings/database/models/servers/group_membership'; -import type GroupsInChannelModel from '@typings/database/models/servers/groups_in_channel'; -import type GroupsInTeamModel from '@typings/database/models/servers/groups_in_team'; +import type GroupsChannelModel from '@typings/database/models/servers/groups_channel'; +import type GroupsTeamModel from '@typings/database/models/servers/groups_team'; -const {GROUP, GROUPS_IN_CHANNEL, GROUPS_IN_TEAM, GROUP_MEMBERSHIP} = MM_TABLES.SERVER; +const {GROUP, GROUPS_CHANNEL, GROUPS_TEAM, GROUP_MEMBERSHIP} = MM_TABLES.SERVER; /** * The Group model unifies/assembles users, teams and channels based on a common ground. For example, a group can be @@ -24,27 +24,33 @@ export default class GroupModel extends Model { /** associations : Describes every relationship to this table. */ static associations: Associations = { - /** A GROUP has a 1:N relationship with GROUPS_IN_CHANNEL */ - [GROUPS_IN_CHANNEL]: {type: 'has_many', foreignKey: 'group_id'}, + /** A GROUP has a 1:N relationship with GROUPS_CHANNEL */ + [GROUPS_CHANNEL]: {type: 'has_many', foreignKey: 'group_id'}, - /** A GROUP has a 1:N relationship with GROUPS_IN_TEAM */ - [GROUPS_IN_TEAM]: {type: 'has_many', foreignKey: 'group_id'}, + /** A GROUP has a 1:N relationship with GROUPS_TEAM */ + [GROUPS_TEAM]: {type: 'has_many', foreignKey: 'group_id'}, /** A GROUP has a 1:N relationship with GROUP_MEMBERSHIP */ [GROUP_MEMBERSHIP]: {type: 'has_many', foreignKey: 'group_id'}, }; + /** allow_reference : Determins if the group can be referenced in mentions */ + @field('allow_reference') allowReference!: boolean; + + /** delete_at : When the group was deleted */ + @field('delete_at') deleteAt!: number; + /** display_name : The display name for the group */ @field('display_name') displayName!: string; /** name : The name of the group */ @field('name') name!: string; - /** groupsInChannel : All the related children records from GroupsInChannel */ - @children(GROUPS_IN_CHANNEL) groupsInChannel!: GroupsInChannelModel[]; + /** groupsChannel : All the related children records from GroupsChannel */ + @children(GROUPS_CHANNEL) groupsChannel!: GroupsChannelModel[]; - /** groupsInTeam : All the related children records from GroupsInTeam */ - @children(GROUPS_IN_TEAM) groupsInTeam!: GroupsInTeamModel[]; + /** groupsTeam : All the related children records from GroupsTeam */ + @children(GROUPS_TEAM) groupsTeam!: GroupsTeamModel[]; /** groupMemberships : All the related children records from GroupMembership */ @children(GROUP_MEMBERSHIP) groupMemberships!: GroupMembershipModel[]; diff --git a/app/database/models/server/groups_in_channel.ts b/app/database/models/server/groups_channel.ts similarity index 64% rename from app/database/models/server/groups_in_channel.ts rename to app/database/models/server/groups_channel.ts index 0b09489d04..e297256495 100644 --- a/app/database/models/server/groups_in_channel.ts +++ b/app/database/models/server/groups_channel.ts @@ -10,22 +10,22 @@ import {MM_TABLES} from '@constants/database'; import type ChannelModel from '@typings/database/models/servers/channel'; import type GroupModel from '@typings/database/models/servers/group'; -const {GROUP, GROUPS_IN_CHANNEL, CHANNEL} = MM_TABLES.SERVER; +const {GROUP, GROUPS_CHANNEL, CHANNEL} = MM_TABLES.SERVER; /** - * The GroupsInChannel links the Channel model with the Group model + * The GroupsChannel links the Channel model with the Group model */ -export default class GroupsInChannelModel extends Model { - /** table (name) : GroupsInChannel */ - static table = GROUPS_IN_CHANNEL; +export default class GroupsChannelModel extends Model { + /** table (name) : GroupsChannel */ + static table = GROUPS_CHANNEL; /** associations : Describes every relationship to this table. */ static associations: Associations = { - /** A GROUP can be associated with multiple GROUPS_IN_CHANNEL (relationship is 1:N) */ + /** A GROUP can be associated with multiple GROUPS_CHANNEL (relationship is 1:N) */ [GROUP]: {type: 'belongs_to', key: 'group_id'}, - /** A CHANNEL can be associated with multiple GROUPS_IN_CHANNEL (relationship is 1:N) */ + /** A CHANNEL can be associated with multiple GROUPS_CHANNEL (relationship is 1:N) */ [CHANNEL]: {type: 'belongs_to', key: 'channel_id'}, }; @@ -35,12 +35,6 @@ export default class GroupsInChannelModel extends Model { /** group_id : The foreign key of the related GROUP model */ @field('group_id') groupId!: string; - /** member_count : The number of members in that group */ - @field('member_count') memberCount!: number; - - /** timezone_count : The number of timezones in that group */ - @field('timezone_count') timezoneCount!: number; - /** channel : The related record to the parent Channel model */ @immutableRelation(CHANNEL, 'channel_id') channel!: Relation; diff --git a/app/database/models/server/groups_in_team.ts b/app/database/models/server/groups_team.ts similarity index 76% rename from app/database/models/server/groups_in_team.ts rename to app/database/models/server/groups_team.ts index b15825dee8..5416e2b6b9 100644 --- a/app/database/models/server/groups_in_team.ts +++ b/app/database/models/server/groups_team.ts @@ -10,22 +10,22 @@ import {MM_TABLES} from '@constants/database'; import type GroupModel from '@typings/database/models/servers/group'; import type TeamModel from '@typings/database/models/servers/team'; -const {GROUP, GROUPS_IN_TEAM, TEAM} = MM_TABLES.SERVER; +const {GROUP, GROUPS_TEAM, TEAM} = MM_TABLES.SERVER; /** - * The GroupsInTeam links the Team model with the Group model + * The GroupsTeam links the Team model with the Group model */ -export default class GroupsInTeamModel extends Model { - /** table (name) : GroupsInTeam */ - static table = GROUPS_IN_TEAM; +export default class GroupsTeamModel extends Model { + /** table (name) : GroupsTeam */ + static table = GROUPS_TEAM; /** associations : Describes every relationship to this table. */ static associations: Associations = { - /** GroupsInTeam can belong to only one Group */ + /** GroupsTeam can belong to only one Group */ [GROUP]: {type: 'belongs_to', key: 'group_id'}, - /** GroupsInTeam can belong to only one Team */ + /** GroupsTeam can belong to only one Team */ [TEAM]: {type: 'belongs_to', key: 'team_id'}, }; diff --git a/app/database/models/server/index.ts b/app/database/models/server/index.ts index 36cdf8357d..a39839d6fa 100644 --- a/app/database/models/server/index.ts +++ b/app/database/models/server/index.ts @@ -8,8 +8,8 @@ export {default as CustomEmojiModel} from './custom_emoji'; export {default as DraftModel} from './draft'; export {default as FileModel} from './file'; export {default as GroupMembershipModel} from './group_membership'; -export {default as GroupsInChannelModel} from './groups_in_channel'; -export {default as GroupsInTeamModel} from './groups_in_team'; +export {default as GroupsChannelModel} from './groups_channel'; +export {default as GroupsTeamModel} from './groups_team'; export {default as GroupModel} from './group'; export {default as MyChannelSettingsModel} from './my_channel_settings'; export {default as MyChannelModel} from './my_channel'; diff --git a/app/database/models/server/team.ts b/app/database/models/server/team.ts index c577d9d7d3..65e3efa700 100644 --- a/app/database/models/server/team.ts +++ b/app/database/models/server/team.ts @@ -8,7 +8,7 @@ import Model, {Associations} from '@nozbe/watermelondb/Model'; import {MM_TABLES} from '@constants/database'; import type ChannelModel from '@typings/database/models/servers/channel'; -import type GroupsInTeamModel from '@typings/database/models/servers/groups_in_team'; +import type GroupsTeamModel from '@typings/database/models/servers/groups_team'; import type MyTeamModel from '@typings/database/models/servers/my_team'; import type SlashCommandModel from '@typings/database/models/servers/slash_command'; import type TeamChannelHistoryModel from '@typings/database/models/servers/team_channel_history'; @@ -17,7 +17,7 @@ import type TeamSearchHistoryModel from '@typings/database/models/servers/team_s const { CHANNEL, - GROUPS_IN_TEAM, + GROUPS_TEAM, TEAM, MY_TEAM, SLASH_COMMAND, @@ -39,8 +39,8 @@ export default class TeamModel extends Model { /** A TEAM has a 1:N relationship with CHANNEL. A TEAM can possess multiple channels */ [CHANNEL]: {type: 'has_many', foreignKey: 'team_id'}, - /** A TEAM has a 1:N relationship with GROUPS_IN_TEAM. A TEAM can possess multiple groups */ - [GROUPS_IN_TEAM]: {type: 'has_many', foreignKey: 'team_id'}, + /** A TEAM has a 1:N relationship with GROUPS_TEAM. A TEAM can possess multiple groups */ + [GROUPS_TEAM]: {type: 'has_many', foreignKey: 'team_id'}, /** A TEAM has a 1:N relationship with SLASH_COMMAND. A TEAM can possess multiple slash commands */ [SLASH_COMMAND]: {type: 'has_many', foreignKey: 'team_id'}, @@ -82,8 +82,8 @@ export default class TeamModel extends Model { /** channels : All the channels associated with this team */ @children(CHANNEL) channels!: ChannelModel[]; - /** groupsInTeam : All the groups associated with this team */ - @children(GROUPS_IN_TEAM) groupsInTeam!: GroupsInTeamModel[]; + /** groupsTeam : All the groups associated with this team */ + @children(GROUPS_TEAM) groupsTeam!: GroupsTeamModel[]; /** myTeam : Retrieves additional information about the team that this user is possibly part of. */ @immutableRelation(MY_TEAM, 'id') myTeam!: Relation; diff --git a/app/database/operator/server_data_operator/comparators/index.ts b/app/database/operator/server_data_operator/comparators/index.ts index a7734821cb..3c9525d959 100644 --- a/app/database/operator/server_data_operator/comparators/index.ts +++ b/app/database/operator/server_data_operator/comparators/index.ts @@ -9,8 +9,8 @@ import type DraftModel from '@typings/database/models/servers/draft'; import type FileModel from '@typings/database/models/servers/file'; import type GroupModel from '@typings/database/models/servers/group'; import type GroupMembershipModel from '@typings/database/models/servers/group_membership'; -import type GroupsInChannelModel from '@typings/database/models/servers/groups_in_channel'; -import type GroupsInTeamModel from '@typings/database/models/servers/groups_in_team'; +import type GroupsChannelModel from '@typings/database/models/servers/groups_channel'; +import type GroupsTeamModel from '@typings/database/models/servers/groups_team'; import type MyChannelModel from '@typings/database/models/servers/my_channel'; import type MyChannelSettingsModel from '@typings/database/models/servers/my_channel_settings'; import type MyTeamModel from '@typings/database/models/servers/my_team'; @@ -86,11 +86,11 @@ export const isRecordGroupEqualToRaw = (record: GroupModel, raw: Group) => { return raw.id === record.id; }; -export const isRecordGroupsInTeamEqualToRaw = (record: GroupsInTeamModel, raw: GroupTeam) => { +export const isRecordGroupsTeamEqualToRaw = (record: GroupsTeamModel, raw: GroupTeam) => { return raw.team_id === record.teamId && raw.group_id === record.groupId; }; -export const isRecordGroupsInChannelEqualToRaw = (record: GroupsInChannelModel, raw: GroupChannel) => { +export const isRecordGroupsChannelEqualToRaw = (record: GroupsChannelModel, raw: GroupChannel) => { return raw.channel_id === record.channelId && raw.group_id === record.groupId; }; diff --git a/app/database/operator/server_data_operator/handlers/group.test.ts b/app/database/operator/server_data_operator/handlers/group.test.ts index 2fb6ae772e..64471723d3 100644 --- a/app/database/operator/server_data_operator/handlers/group.test.ts +++ b/app/database/operator/server_data_operator/handlers/group.test.ts @@ -5,14 +5,14 @@ import DatabaseManager from '@database/manager'; import { isRecordGroupEqualToRaw, isRecordGroupMembershipEqualToRaw, - isRecordGroupsInChannelEqualToRaw, - isRecordGroupsInTeamEqualToRaw, + isRecordGroupsChannelEqualToRaw, + isRecordGroupsTeamEqualToRaw, } from '@database/operator/server_data_operator/comparators'; import { transformGroupMembershipRecord, transformGroupRecord, - transformGroupsInChannelRecord, - transformGroupsInTeamRecord, + transformGroupsChannelRecord, + transformGroupsTeamRecord, } from '@database/operator/server_data_operator/transformers/group'; import ServerDataOperator from '..'; @@ -41,7 +41,7 @@ describe('*** Operator: Group Handlers tests ***', () => { has_syncables: true, type: '', member_count: 1, - allow_reference: false, + allow_reference: true, }, ]; @@ -52,7 +52,7 @@ describe('*** Operator: Group Handlers tests ***', () => { expect(spyOnHandleRecords).toHaveBeenCalledTimes(1); expect(spyOnHandleRecords).toHaveBeenCalledWith({ - fieldName: 'name', + fieldName: 'id', createOrUpdateRawValues: groups, tableName: 'Group', prepareRecordsOnly: false, @@ -61,11 +61,11 @@ describe('*** Operator: Group Handlers tests ***', () => { }); }); - it('=> HandleGroupsInTeam: should write to the GROUPS_IN_TEAM table', async () => { + it('=> HandleGroupsTeam: should write to the GROUPS_TEAM table', async () => { expect.assertions(2); const spyOnHandleRecords = jest.spyOn(operator, 'handleRecords'); - const groupsInTeams = [ + const groupsTeams = [ { team_id: 'team_899', team_display_name: '', @@ -78,27 +78,27 @@ describe('*** Operator: Group Handlers tests ***', () => { }, ]; - await operator.handleGroupsInTeam({ - groupsInTeams, + await operator.handleGroupsTeam({ + groupsTeams, prepareRecordsOnly: false, }); expect(spyOnHandleRecords).toHaveBeenCalledTimes(1); expect(spyOnHandleRecords).toHaveBeenCalledWith({ fieldName: 'group_id', - createOrUpdateRawValues: groupsInTeams, - tableName: 'GroupsInTeam', + createOrUpdateRawValues: groupsTeams, + tableName: 'GroupsTeam', prepareRecordsOnly: false, - findMatchingRecordBy: isRecordGroupsInTeamEqualToRaw, - transformer: transformGroupsInTeamRecord, + findMatchingRecordBy: isRecordGroupsTeamEqualToRaw, + transformer: transformGroupsTeamRecord, }); }); - it('=> HandleGroupsInChannel: should write to the GROUPS_IN_CHANNEL table', async () => { + it('=> HandleGroupsChannel: should write to the GROUPS_CHANNEL table', async () => { expect.assertions(2); const spyOnHandleRecords = jest.spyOn(operator, 'handleRecords'); - const groupsInChannels = [ + const groupsChannels = [ { auto_add: true, channel_display_name: '', @@ -111,24 +111,22 @@ describe('*** Operator: Group Handlers tests ***', () => { team_id: '', team_type: '', update_at: 0, - member_count: 0, - timezone_count: 0, }, ]; - await operator.handleGroupsInChannel({ - groupsInChannels, + await operator.handleGroupsChannel({ + groupsChannels, prepareRecordsOnly: false, }); expect(spyOnHandleRecords).toHaveBeenCalledTimes(1); expect(spyOnHandleRecords).toHaveBeenCalledWith({ fieldName: 'group_id', - createOrUpdateRawValues: groupsInChannels, - tableName: 'GroupsInChannel', + createOrUpdateRawValues: groupsChannels, + tableName: 'GroupsChannel', prepareRecordsOnly: false, - findMatchingRecordBy: isRecordGroupsInChannelEqualToRaw, - transformer: transformGroupsInChannelRecord, + findMatchingRecordBy: isRecordGroupsChannelEqualToRaw, + transformer: transformGroupsChannelRecord, }); }); @@ -150,7 +148,7 @@ describe('*** Operator: Group Handlers tests ***', () => { expect(spyOnHandleRecords).toHaveBeenCalledTimes(1); expect(spyOnHandleRecords).toHaveBeenCalledWith({ - fieldName: 'user_id', + fieldName: 'group_id', createOrUpdateRawValues: groupMemberships, tableName: 'GroupMembership', prepareRecordsOnly: false, diff --git a/app/database/operator/server_data_operator/handlers/group.ts b/app/database/operator/server_data_operator/handlers/group.ts index 99d96e036f..9b60f89a43 100644 --- a/app/database/operator/server_data_operator/handlers/group.ts +++ b/app/database/operator/server_data_operator/handlers/group.ts @@ -6,35 +6,35 @@ import DataOperatorException from '@database/exceptions/data_operator_exception' import { isRecordGroupEqualToRaw, isRecordGroupMembershipEqualToRaw, - isRecordGroupsInChannelEqualToRaw, - isRecordGroupsInTeamEqualToRaw, + isRecordGroupsChannelEqualToRaw, + isRecordGroupsTeamEqualToRaw, } from '@database/operator/server_data_operator/comparators'; import { transformGroupMembershipRecord, transformGroupRecord, - transformGroupsInChannelRecord, - transformGroupsInTeamRecord, + transformGroupsChannelRecord, + transformGroupsTeamRecord, } from '@database/operator/server_data_operator/transformers/group'; import {getUniqueRawsBy} from '@database/operator/utils/general'; -import type {HandleGroupArgs, HandleGroupMembershipArgs, HandleGroupsInChannelArgs, HandleGroupsInTeamArgs} from '@typings/database/database'; +import type {HandleGroupArgs, HandleGroupMembershipArgs, HandleGroupsChannelArgs, HandleGroupsTeamArgs} from '@typings/database/database'; import type GroupModel from '@typings/database/models/servers/group'; import type GroupMembershipModel from '@typings/database/models/servers/group_membership'; -import type GroupsInChannelModel from '@typings/database/models/servers/groups_in_channel'; -import type GroupsInTeamModel from '@typings/database/models/servers/groups_in_team'; +import type GroupsChannelModel from '@typings/database/models/servers/groups_channel'; +import type GroupsTeamModel from '@typings/database/models/servers/groups_team'; const { GROUP, - GROUPS_IN_CHANNEL, - GROUPS_IN_TEAM, + GROUPS_CHANNEL, + GROUPS_TEAM, GROUP_MEMBERSHIP, } = MM_TABLES.SERVER; export interface GroupHandlerMix { handleGroupMembership: ({groupMemberships, prepareRecordsOnly}: HandleGroupMembershipArgs) => Promise; handleGroup: ({groups, prepareRecordsOnly}: HandleGroupArgs) => Promise; - handleGroupsInTeam: ({groupsInTeams, prepareRecordsOnly}: HandleGroupsInTeamArgs) => Promise; - handleGroupsInChannel: ({groupsInChannels, prepareRecordsOnly}: HandleGroupsInChannelArgs) => Promise; + handleGroupsTeam: ({groupsTeams, prepareRecordsOnly}: HandleGroupsTeamArgs) => Promise; + handleGroupsChannel: ({groupsChannels, prepareRecordsOnly}: HandleGroupsChannelArgs) => Promise; } const GroupHandler = (superclass: any) => class extends superclass { @@ -56,7 +56,7 @@ const GroupHandler = (superclass: any) => class extends superclass { const createOrUpdateRawValues = getUniqueRawsBy({raws: groupMemberships, key: 'group_id'}); return this.handleRecords({ - fieldName: 'user_id', + fieldName: 'group_id', findMatchingRecordBy: isRecordGroupMembershipEqualToRaw, transformer: transformGroupMembershipRecord, prepareRecordsOnly, @@ -80,10 +80,10 @@ const GroupHandler = (superclass: any) => class extends superclass { ); } - const createOrUpdateRawValues = getUniqueRawsBy({raws: groups, key: 'name'}); + const createOrUpdateRawValues = getUniqueRawsBy({raws: groups, key: 'id'}); return this.handleRecords({ - fieldName: 'name', + fieldName: 'id', findMatchingRecordBy: isRecordGroupEqualToRaw, transformer: transformGroupRecord, prepareRecordsOnly, @@ -93,56 +93,58 @@ const GroupHandler = (superclass: any) => class extends superclass { }; /** - * handleGroupsInTeam: Handler responsible for the Create/Update operations occurring on the GROUPS_IN_TEAM table from the 'Server' schema - * @param {HandleGroupsInTeamArgs} groupsInTeamsArgs - * @param {RawGroupsInTeam[]} groupsInTeamsArgs.groupsInTeams - * @param {boolean} groupsInTeamsArgs.prepareRecordsOnly + * handleGroupsTeam: Handler responsible for the Create/Update operations occurring on the GROUPS_TEAM table from the 'Server' schema + * @param {HandleGroupsTeamArgs} groupsTeamsArgs + * @param {GroupsTeam[]} groupsTeamsArgs.groupsTeams + * @param {boolean} groupsTeamsArgs.prepareRecordsOnly * @throws DataOperatorException - * @returns {Promise} + * @returns {Promise} */ - handleGroupsInTeam = ({groupsInTeams, prepareRecordsOnly = true}: HandleGroupsInTeamArgs): Promise => { - if (!groupsInTeams.length) { + handleGroupsTeam = ({groupsTeams, prepareRecordsOnly = true}: HandleGroupsTeamArgs): Promise => { + if (!groupsTeams.length) { throw new DataOperatorException( - 'An empty "groups" array has been passed to the handleGroupsInTeam method', + 'An empty "groups" array has been passed to the handleGroupsTeam method', ); } - const createOrUpdateRawValues = getUniqueRawsBy({raws: groupsInTeams, key: 'group_id'}); + const createOrUpdateRawValues = groupsTeams.filter((gt, index, self) => ( + index === self.findIndex((item) => item.team_id === gt.team_id && item.group_id === gt.group_id))); return this.handleRecords({ fieldName: 'group_id', - findMatchingRecordBy: isRecordGroupsInTeamEqualToRaw, - transformer: transformGroupsInTeamRecord, + findMatchingRecordBy: isRecordGroupsTeamEqualToRaw, + transformer: transformGroupsTeamRecord, prepareRecordsOnly, createOrUpdateRawValues, - tableName: GROUPS_IN_TEAM, + tableName: GROUPS_TEAM, }); }; /** - * handleGroupsInChannel: Handler responsible for the Create/Update operations occurring on the GROUPS_IN_CHANNEL table from the 'Server' schema - * @param {HandleGroupsInChannelArgs} groupsInChannelsArgs - * @param {RawGroupsInChannel[]} groupsInChannelsArgs.groupsInChannels - * @param {boolean} groupsInChannelsArgs.prepareRecordsOnly + * handleGroupsChannel: Handler responsible for the Create/Update operations occurring on the GROUPS_CHANNEL table from the 'Server' schema + * @param {HandleGroupsChannelArgs} groupsChannelsArgs + * @param {GroupsChannel[]} groupsChannelsArgs.groupsChannels + * @param {boolean} groupsChannelsArgs.prepareRecordsOnly * @throws DataOperatorException - * @returns {Promise} + * @returns {Promise} */ - handleGroupsInChannel = ({groupsInChannels, prepareRecordsOnly = true}: HandleGroupsInChannelArgs): Promise => { - if (!groupsInChannels.length) { + handleGroupsChannel = ({groupsChannels, prepareRecordsOnly = true}: HandleGroupsChannelArgs): Promise => { + if (!groupsChannels.length) { throw new DataOperatorException( - 'An empty "groups" array has been passed to the handleGroupsInTeam method', + 'An empty "groups" array has been passed to the handleGroupsTeam method', ); } - const createOrUpdateRawValues = getUniqueRawsBy({raws: groupsInChannels, key: 'channel_id'}); + const createOrUpdateRawValues = groupsChannels.filter((gc, index, self) => ( + index === self.findIndex((item) => item.channel_id === gc.channel_id && item.group_id === gc.group_id))); return this.handleRecords({ fieldName: 'group_id', - findMatchingRecordBy: isRecordGroupsInChannelEqualToRaw, - transformer: transformGroupsInChannelRecord, + findMatchingRecordBy: isRecordGroupsChannelEqualToRaw, + transformer: transformGroupsChannelRecord, prepareRecordsOnly, createOrUpdateRawValues, - tableName: GROUPS_IN_CHANNEL, + tableName: GROUPS_CHANNEL, }); }; }; diff --git a/app/database/operator/server_data_operator/transformers/group.test.ts b/app/database/operator/server_data_operator/transformers/group.test.ts index b58404e77a..9acd697180 100644 --- a/app/database/operator/server_data_operator/transformers/group.test.ts +++ b/app/database/operator/server_data_operator/transformers/group.test.ts @@ -4,8 +4,8 @@ import { transformGroupMembershipRecord, transformGroupRecord, - transformGroupsInChannelRecord, - transformGroupsInTeamRecord, + transformGroupsChannelRecord, + transformGroupsTeamRecord, } from '@database/operator/server_data_operator/transformers/group'; import {createTestConnection} from '@database/operator/utils/create_test_connection'; import {OperationType} from '@typings/database/enums'; @@ -43,13 +43,13 @@ describe('*** GROUP Prepare Records Test ***', () => { expect(preparedRecords!.collection.modelClass.name).toBe('GroupModel'); }); - it('=> transformGroupsInTeamRecord: should return an array of type GroupsInTeam', async () => { + it('=> transformGroupsTeamRecord: should return an array of type GroupsTeam', async () => { expect.assertions(3); const database = await createTestConnection({databaseName: 'group_prepare_records', setActive: true}); expect(database).toBeTruthy(); - const preparedRecords = await transformGroupsInTeamRecord({ + const preparedRecords = await transformGroupsTeamRecord({ action: OperationType.CREATE, database: database!, value: { @@ -68,16 +68,16 @@ describe('*** GROUP Prepare Records Test ***', () => { }); expect(preparedRecords).toBeTruthy(); - expect(preparedRecords!.collection.modelClass.name).toBe('GroupsInTeamModel'); + expect(preparedRecords!.collection.modelClass.name).toBe('GroupsTeamModel'); }); - it('=> transformGroupsInChannelRecord: should return an array of type GroupsInChannel', async () => { + it('=> transformGroupsChannelRecord: should return an array of type GroupsChannel', async () => { expect.assertions(3); const database = await createTestConnection({databaseName: 'group_prepare_records', setActive: true}); expect(database).toBeTruthy(); - const preparedRecords = await transformGroupsInChannelRecord({ + const preparedRecords = await transformGroupsChannelRecord({ action: OperationType.CREATE, database: database!, value: { @@ -99,7 +99,7 @@ describe('*** GROUP Prepare Records Test ***', () => { }); expect(preparedRecords).toBeTruthy(); - expect(preparedRecords!.collection.modelClass.name).toBe('GroupsInChannelModel'); + expect(preparedRecords!.collection.modelClass.name).toBe('GroupsChannelModel'); }); it('=> transformGroupMembershipRecord: should return an array of type GroupMembership', async () => { diff --git a/app/database/operator/server_data_operator/transformers/group.ts b/app/database/operator/server_data_operator/transformers/group.ts index 52c87b6377..359f92287d 100644 --- a/app/database/operator/server_data_operator/transformers/group.ts +++ b/app/database/operator/server_data_operator/transformers/group.ts @@ -8,13 +8,13 @@ import type {TransformerArgs} from '@typings/database/database'; import {OperationType} from '@typings/database/enums'; import type GroupModel from '@typings/database/models/servers/group'; import type GroupMembershipModel from '@typings/database/models/servers/group_membership'; -import type GroupsInChannelModel from '@typings/database/models/servers/groups_in_channel'; -import type GroupsInTeamModel from '@typings/database/models/servers/groups_in_team'; +import type GroupsChannelModel from '@typings/database/models/servers/groups_channel'; +import type GroupsTeamModel from '@typings/database/models/servers/groups_team'; const { GROUP, - GROUPS_IN_CHANNEL, - GROUPS_IN_TEAM, + GROUPS_CHANNEL, + GROUPS_TEAM, GROUP_MEMBERSHIP, } = MM_TABLES.SERVER; @@ -61,6 +61,8 @@ export const transformGroupRecord = ({action, database, value}: TransformerArgs) // If isCreateAction is true, we will use the id (API response) from the RAW, else we shall use the existing record id from the database const fieldsMapper = (group: GroupModel) => { group._raw.id = isCreateAction ? (raw?.id ?? group.id) : record.id; + group.allowReference = raw.allow_reference; + group.deleteAt = raw.delete_at; group.name = raw.name; group.displayName = raw.display_name; }; @@ -75,57 +77,55 @@ export const transformGroupRecord = ({action, database, value}: TransformerArgs) }; /** - * transformGroupsInTeamRecord: Prepares a record of the SERVER database 'GroupsInTeam' table for update or create actions. + * transformGroupsTeamRecord: Prepares a record of the SERVER database 'GroupsTeam' table for update or create actions. * @param {DataFactory} operator * @param {Database} operator.database * @param {RecordPair} operator.value - * @returns {Promise} + * @returns {Promise} */ -export const transformGroupsInTeamRecord = ({action, database, value}: TransformerArgs): Promise => { +export const transformGroupsTeamRecord = ({action, database, value}: TransformerArgs): Promise => { const raw = value.raw as GroupTeam; - const record = value.record as GroupsInTeamModel; + const record = value.record as GroupsTeamModel; const isCreateAction = action === OperationType.CREATE; - const fieldsMapper = (groupsInTeam: GroupsInTeamModel) => { - groupsInTeam._raw.id = isCreateAction ? groupsInTeam.id : record.id; - groupsInTeam.teamId = raw.team_id; - groupsInTeam.groupId = raw.group_id; + const fieldsMapper = (groupsTeam: GroupsTeamModel) => { + groupsTeam._raw.id = isCreateAction ? groupsTeam.id : record.id; + groupsTeam.teamId = raw.team_id; + groupsTeam.groupId = raw.group_id; }; return prepareBaseRecord({ action, database, - tableName: GROUPS_IN_TEAM, + tableName: GROUPS_TEAM, value, fieldsMapper, - }) as Promise; + }) as Promise; }; /** - * transformGroupsInChannelRecord: Prepares a record of the SERVER database 'GroupsInChannel' table for update or create actions. + * transformGroupsChannelRecord: Prepares a record of the SERVER database 'GroupsChannel' table for update or create actions. * @param {DataFactory} operator * @param {Database} operator.database * @param {RecordPair} operator.value - * @returns {Promise} + * @returns {Promise} */ -export const transformGroupsInChannelRecord = ({action, database, value}: TransformerArgs): Promise => { - const raw = value.raw as GroupChannel; - const record = value.record as GroupsInChannelModel; +export const transformGroupsChannelRecord = ({action, database, value}: TransformerArgs): Promise => { + const raw = value.raw as GroupChannelRelation; + const record = value.record as GroupsChannelModel; const isCreateAction = action === OperationType.CREATE; - const fieldsMapper = (groupsInChannel: GroupsInChannelModel) => { - groupsInChannel._raw.id = isCreateAction ? groupsInChannel.id : record.id; - groupsInChannel.channelId = raw.channel_id; - groupsInChannel.groupId = raw.group_id; - groupsInChannel.memberCount = raw.member_count; - groupsInChannel.timezoneCount = raw.timezone_count; + const fieldsMapper = (groupsChannel: GroupsChannelModel) => { + groupsChannel._raw.id = isCreateAction ? groupsChannel.id : record.id; + groupsChannel.channelId = raw.channel_id; + groupsChannel.groupId = raw.group_id; }; return prepareBaseRecord({ action, database, - tableName: GROUPS_IN_CHANNEL, + tableName: GROUPS_CHANNEL, value, fieldsMapper, - }) as Promise; + }) as Promise; }; diff --git a/app/database/schema/server/index.ts b/app/database/schema/server/index.ts index 9eb267af99..ed29d961a8 100644 --- a/app/database/schema/server/index.ts +++ b/app/database/schema/server/index.ts @@ -12,8 +12,8 @@ import { FileSchema, GroupMembershipSchema, GroupSchema, - GroupsInChannelSchema, - GroupsInTeamSchema, + GroupsChannelSchema, + GroupsTeamSchema, MyChannelSchema, MyChannelSettingsSchema, MyTeamSchema, @@ -45,8 +45,8 @@ export const serverSchema: AppSchema = appSchema({ FileSchema, GroupMembershipSchema, GroupSchema, - GroupsInChannelSchema, - GroupsInTeamSchema, + GroupsChannelSchema, + GroupsTeamSchema, MyChannelSchema, MyChannelSettingsSchema, MyTeamSchema, diff --git a/app/database/schema/server/table_schemas/group.ts b/app/database/schema/server/table_schemas/group.ts index 371e6993c2..baadfb6c00 100644 --- a/app/database/schema/server/table_schemas/group.ts +++ b/app/database/schema/server/table_schemas/group.ts @@ -10,6 +10,8 @@ const {GROUP} = MM_TABLES.SERVER; export default tableSchema({ name: GROUP, columns: [ + {name: 'allow_reference', type: 'boolean'}, + {name: 'delete_at', type: 'number'}, {name: 'display_name', type: 'string'}, {name: 'name', type: 'string'}, ], diff --git a/app/database/schema/server/table_schemas/groups_in_channel.ts b/app/database/schema/server/table_schemas/groups_channel.ts similarity index 86% rename from app/database/schema/server/table_schemas/groups_in_channel.ts rename to app/database/schema/server/table_schemas/groups_channel.ts index 76bce5fcdf..5d7f5ff880 100644 --- a/app/database/schema/server/table_schemas/groups_in_channel.ts +++ b/app/database/schema/server/table_schemas/groups_channel.ts @@ -5,10 +5,10 @@ import {tableSchema} from '@nozbe/watermelondb'; import {MM_TABLES} from '@constants/database'; -const {GROUPS_IN_CHANNEL} = MM_TABLES.SERVER; +const {GROUPS_CHANNEL} = MM_TABLES.SERVER; export default tableSchema({ - name: GROUPS_IN_CHANNEL, + name: GROUPS_CHANNEL, columns: [ {name: 'channel_id', type: 'string', isIndexed: true}, {name: 'group_id', type: 'string', isIndexed: true}, diff --git a/app/database/schema/server/table_schemas/groups_in_team.ts b/app/database/schema/server/table_schemas/groups_team.ts similarity index 84% rename from app/database/schema/server/table_schemas/groups_in_team.ts rename to app/database/schema/server/table_schemas/groups_team.ts index 9c48d64331..9041b402c2 100644 --- a/app/database/schema/server/table_schemas/groups_in_team.ts +++ b/app/database/schema/server/table_schemas/groups_team.ts @@ -5,10 +5,10 @@ import {tableSchema} from '@nozbe/watermelondb'; import {MM_TABLES} from '@constants/database'; -const {GROUPS_IN_TEAM} = MM_TABLES.SERVER; +const {GROUPS_TEAM} = MM_TABLES.SERVER; export default tableSchema({ - name: GROUPS_IN_TEAM, + name: GROUPS_TEAM, columns: [ {name: 'group_id', type: 'string', isIndexed: true}, {name: 'team_id', type: 'string', isIndexed: true}, diff --git a/app/database/schema/server/table_schemas/index.ts b/app/database/schema/server/table_schemas/index.ts index 1e85d1177d..dc360ef9ce 100644 --- a/app/database/schema/server/table_schemas/index.ts +++ b/app/database/schema/server/table_schemas/index.ts @@ -9,8 +9,8 @@ export {default as DraftSchema} from './draft'; export {default as FileSchema} from './file'; export {default as GroupMembershipSchema} from './group_membership'; export {default as GroupSchema} from './group'; -export {default as GroupsInChannelSchema} from './groups_in_channel'; -export {default as GroupsInTeamSchema} from './groups_in_team'; +export {default as GroupsChannelSchema} from './groups_channel'; +export {default as GroupsTeamSchema} from './groups_team'; export {default as MyChannelSchema} from './my_channel'; export {default as MyChannelSettingsSchema} from './my_channel_settings'; export {default as MyTeamSchema} from './my_team'; diff --git a/app/database/schema/server/test.ts b/app/database/schema/server/test.ts index af42316990..c8f9004271 100644 --- a/app/database/schema/server/test.ts +++ b/app/database/schema/server/test.ts @@ -15,8 +15,8 @@ const { DRAFT, FILE, GROUP, - GROUPS_IN_CHANNEL, - GROUPS_IN_TEAM, + GROUPS_CHANNEL, + GROUPS_TEAM, GROUP_MEMBERSHIP, MY_CHANNEL, MY_CHANNEL_SETTINGS, @@ -248,16 +248,20 @@ describe('*** Test schema for SERVER database ***', () => { [GROUP]: { name: GROUP, columns: { + allow_reference: {name: 'allow_reference', type: 'boolean'}, + delete_at: {name: 'delete_at', type: 'number'}, display_name: {name: 'display_name', type: 'string'}, name: {name: 'name', type: 'string'}, }, columnArray: [ + {name: 'allow_reference', type: 'boolean'}, + {name: 'delete_at', type: 'number'}, {name: 'display_name', type: 'string'}, {name: 'name', type: 'string'}, ], }, - [GROUPS_IN_CHANNEL]: { - name: GROUPS_IN_CHANNEL, + [GROUPS_CHANNEL]: { + name: GROUPS_CHANNEL, columns: { channel_id: {name: 'channel_id', type: 'string', isIndexed: true}, group_id: {name: 'group_id', type: 'string', isIndexed: true}, @@ -271,8 +275,8 @@ describe('*** Test schema for SERVER database ***', () => { {name: 'timezone_count', type: 'number'}, ], }, - [GROUPS_IN_TEAM]: { - name: GROUPS_IN_TEAM, + [GROUPS_TEAM]: { + name: GROUPS_TEAM, columns: { group_id: {name: 'group_id', type: 'string', isIndexed: true}, team_id: {name: 'team_id', type: 'string', isIndexed: true}, diff --git a/app/utils/user/index.ts b/app/utils/user/index.ts index 5f72b463bf..d8486d9bf2 100644 --- a/app/utils/user/index.ts +++ b/app/utils/user/index.ts @@ -5,6 +5,9 @@ import {General, Preferences} from '@constants'; import {UserModel} from '@database/models/server'; import {DEFAULT_LOCALE, getLocalizedMessage, t} from '@i18n'; +import type GroupModel from '@typings/database/models/servers/group'; +import type GroupMembershipModel from '@typings/database/models/servers/group_membership'; + export function displayUsername(user?: UserProfile | UserModel, locale?: string, teammateDisplayNameSetting?: string, useFallbackUsername = true) { let name = useFallbackUsername ? getLocalizedMessage(locale || DEFAULT_LOCALE, t('channel_loader.someone'), 'Someone') : ''; @@ -94,7 +97,7 @@ export const getUsersByUsername = (users: UserModel[]) => { return usersByUsername; }; -export const getUserMentionKeys = (user: UserModel) => { +export const getUserMentionKeys = (user: UserModel, groups: GroupModel[], userGroups: GroupMembershipModel[]) => { const keys: UserMentionKey[] = []; if (!user.notifyProps) { @@ -123,5 +126,17 @@ export const getUserMentionKeys = (user: UserModel) => { keys.push({key: usernameKey}); } + if (groups.length && userGroups.length) { + const groupMentions = userGroups.reduce((result: Array<{key: string}>, ug: GroupMembershipModel) => { + const group = groups.find((g) => ug.groupId === g.id); + if (group) { + result.push({key: `@${group.name}`}); + } + return result; + }, []); + + keys.push(...groupMentions); + } + return keys; }; diff --git a/types/api/groups.d.ts b/types/api/groups.d.ts index 4e1c13d954..2fa1aebd8c 100644 --- a/types/api/groups.d.ts +++ b/types/api/groups.d.ts @@ -45,8 +45,8 @@ type GroupChannel = { create_at: number; delete_at: number; update_at: number; - member_count: number; - timezone_count: number; + member_count?: number; + timezone_count?: number; }; type GroupSyncables = { teams: GroupTeam[]; diff --git a/types/database/database.d.ts b/types/database/database.d.ts index 9a5359e759..c19e02562c 100644 --- a/types/database/database.d.ts +++ b/types/database/database.d.ts @@ -213,12 +213,12 @@ export type HandleTeamArgs = PrepareOnly & { teams: Team[]; }; -export type HandleGroupsInChannelArgs = PrepareOnly & { - groupsInChannels: GroupChannel[]; +export type HandleGroupsChannelArgs = PrepareOnly & { + groupsChannels: GroupChannelRelation[]; }; -export type HandleGroupsInTeamArgs = PrepareOnly &{ - groupsInTeams: GroupTeam[]; +export type HandleGroupsTeamArgs = PrepareOnly &{ + groupsTeams: GroupTeamRelation[]; }; export type HandleGroupArgs = PrepareOnly & { diff --git a/types/database/models/servers/channel.d.ts b/types/database/models/servers/channel.d.ts index 898bc2cff0..0f075d1af7 100644 --- a/types/database/models/servers/channel.d.ts +++ b/types/database/models/servers/channel.d.ts @@ -47,8 +47,8 @@ export default class ChannelModel extends Model { /** drafts : All drafts for this channel */ drafts: Query; - /** groupsInChannel : Every group contained in this channel */ - groupsInChannel: Query; + /** groupsChannel : Every group contained in this channel */ + groupsChannel: Query; /** posts : All posts made in the channel */ posts: Query; diff --git a/types/database/models/servers/group.d.ts b/types/database/models/servers/group.d.ts index c7c0b18ffa..f67014f3c2 100644 --- a/types/database/models/servers/group.d.ts +++ b/types/database/models/servers/group.d.ts @@ -15,17 +15,23 @@ export default class GroupModel extends Model { /** associations : Describes every relationship to this table. */ static associations: Associations; + /** allow_reference : Determins if the group can be referenced in mentions */ + allowReference: boolean; + + /** delete_at : When the group was deleted */ + deleteAt: number; + /** display_name : The display name for the group */ displayName: string; /** name : The name of the group */ name: string; - /** groupsInChannel : All the related children records from GroupsInChannel */ - groupsInChannel: Query; + /** groupsChannel : All the related children records from GroupsChannel */ + groupsChannel: Query; - /** groupsInTeam : All the related children records from GroupsInTeam */ - groupsInTeam: Query; + /** groupsTeam : All the related children records from GroupsTeam */ + groupsTeam: Query; /** groupMemberships : All the related children records from GroupMembership */ groupMemberships: Query; diff --git a/types/database/models/servers/groups_in_channel.d.ts b/types/database/models/servers/groups_channel.d.ts similarity index 68% rename from types/database/models/servers/groups_in_channel.d.ts rename to types/database/models/servers/groups_channel.d.ts index 0ab8783fc0..0b501f5db9 100644 --- a/types/database/models/servers/groups_in_channel.d.ts +++ b/types/database/models/servers/groups_channel.d.ts @@ -5,10 +5,10 @@ import {Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; /** - * The GroupsInChannel links the Channel model with the Group model + * The GroupsChannel links the Channel model with the Group model */ -export default class GroupsInChannelModel extends Model { - /** table (name) : GroupsInChannel */ +export default class GroupsChannelModel extends Model { + /** table (name) : GroupsChannel */ static table: string; /** associations : Describes every relationship to this table. */ @@ -20,12 +20,6 @@ export default class GroupsInChannelModel extends Model { /** group_id : The foreign key of the related GROUP model */ groupId: string; - /** member_count : The number of members in that group */ - memberCount: number; - - /** timezone_count : The number of timezones in that group */ - timezoneCount: number; - /** channel : The related record to the parent Channel model */ channel: Relation; diff --git a/types/database/models/servers/groups_in_team.d.ts b/types/database/models/servers/groups_team.d.ts similarity index 85% rename from types/database/models/servers/groups_in_team.d.ts rename to types/database/models/servers/groups_team.d.ts index 81462bf22d..fee932f3a1 100644 --- a/types/database/models/servers/groups_in_team.d.ts +++ b/types/database/models/servers/groups_team.d.ts @@ -5,10 +5,10 @@ import {Relation} from '@nozbe/watermelondb'; import Model, {Associations} from '@nozbe/watermelondb/Model'; /** - * The GroupsInTeam links the Team model with the Group model + * The GroupsTeam links the Team model with the Group model */ -export default class GroupsInTeamModel extends Model { - /** table (name) : GroupsInTeam */ +export default class GroupsTeamModel extends Model { + /** table (name) : GroupsTeam */ static table: string; /** associations : Describes every relationship to this table. */ diff --git a/types/database/models/servers/team.d.ts b/types/database/models/servers/team.d.ts index 6b31c14a1e..6d60bbbde2 100644 --- a/types/database/models/servers/team.d.ts +++ b/types/database/models/servers/team.d.ts @@ -44,8 +44,8 @@ export default class TeamModel extends Model { /** channels : All the channels associated with this team */ channels: Query; - /** groupsInTeam : All the groups associated with this team */ - groupsInTeam: Query; + /** groupsTeam : All the groups associated with this team */ + groupsTeam: Query; /** myTeam : Retrieves additional information about the team that this user is possibly part of. This query might yield no result if the user isn't part of a team. */ myTeam: Relation; diff --git a/types/database/raw_values.d.ts b/types/database/raw_values.d.ts index 4b25428a27..eee51e286a 100644 --- a/types/database/raw_values.d.ts +++ b/types/database/raw_values.d.ts @@ -29,6 +29,16 @@ type GroupMembership = { group_id: string; }; +type GroupChannelRelation = { + channel_id: string; + group_id: string; +} + +type GroupTeamRelation = { + group_id: string; + team_id: string; +} + type MyTeam = { id: string; roles: string;