Remove duplicated directs (#6516)

This commit is contained in:
Kristina 2024-09-11 05:58:41 +04:00 committed by GitHub
parent 1daec4def4
commit 42abc31ab4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 106 additions and 25 deletions

View File

@ -74,7 +74,10 @@ export { chunterOperation } from './migration'
export const DOMAIN_CHUNTER = 'chunter' as Domain
@Model(chunter.class.ChunterSpace, core.class.Space)
export class TChunterSpace extends TSpace implements ChunterSpace {}
export class TChunterSpace extends TSpace implements ChunterSpace {
@Prop(PropCollection(activity.class.ActivityMessage), chunter.string.Messages)
messages?: number
}
@Model(chunter.class.Channel, chunter.class.ChunterSpace)
@UX(chunter.string.Channel, chunter.icon.Hashtag, undefined, undefined, undefined, chunter.string.Channels)

View File

@ -13,7 +13,7 @@
// limitations under the License.
//
import { chunterId, type ThreadMessage } from '@hcengineering/chunter'
import { chunterId, type DirectMessage, type ThreadMessage } from '@hcengineering/chunter'
import core, {
type Account,
TxOperations,
@ -33,12 +33,13 @@ import {
} from '@hcengineering/model'
import activity, { migrateMessagesSpace, DOMAIN_ACTIVITY } from '@hcengineering/model-activity'
import notification from '@hcengineering/notification'
import contactPlugin, { type PersonAccount } from '@hcengineering/contact'
import { DOMAIN_NOTIFICATION } from '@hcengineering/model-notification'
import contactPlugin, { type Person, type PersonAccount } from '@hcengineering/contact'
import { DOMAIN_DOC_NOTIFY, DOMAIN_NOTIFICATION } from '@hcengineering/model-notification'
import { type DocUpdateMessage } from '@hcengineering/activity'
import { DOMAIN_SPACE } from '@hcengineering/model-core'
import chunter from './plugin'
import { DOMAIN_CHUNTER } from './index'
import { type DocUpdateMessage } from '@hcengineering/activity'
export const DOMAIN_COMMENT = 'comment' as Domain
@ -239,6 +240,53 @@ async function removeWrongActivity (client: MigrationClient): Promise<void> {
})
}
async function removeDuplicatedDirects (client: MigrationClient): Promise<void> {
const directs = await client.find<DirectMessage>(DOMAIN_SPACE, { _class: chunter.class.DirectMessage })
const personAccounts = await client.model.findAll<PersonAccount>(contactPlugin.class.PersonAccount, {})
const personByAccount = new Map(personAccounts.map((it) => [it._id, it.person]))
const accountsToPersons = (members: Ref<Account>[]): Ref<Person>[] => {
const personsSet = new Set(
members
.map((it) => personByAccount.get(it as Ref<PersonAccount>))
.filter((it): it is Ref<Person> => it !== undefined)
)
return Array.from(personsSet)
}
const map: Map<string, DirectMessage[]> = new Map<string, DirectMessage[]>()
const toRemove: Ref<DirectMessage>[] = []
for (const direct of directs) {
const persons = accountsToPersons(direct.members)
if (persons.length === 0) {
toRemove.push(direct._id)
continue
}
const key = persons.sort().join(',')
if (!map.has(key)) {
map.set(key, [direct])
} else {
map.get(key)?.push(direct)
}
}
for (const [, directs] of map) {
if (directs.length === 1) continue
const toSave = directs.reduce((acc, it) => ((it.messages ?? 0) > (acc.messages ?? 0) ? it : acc), directs[0])
const rest = directs.filter((it) => it._id !== toSave._id)
toRemove.push(...rest.map((it) => it._id))
}
await client.deleteMany(DOMAIN_SPACE, { _id: { $in: toRemove } })
await client.deleteMany(DOMAIN_ACTIVITY, { attachedTo: { $in: toRemove } })
await client.deleteMany(DOMAIN_ACTIVITY, { objectId: { $in: toRemove } })
await client.deleteMany(DOMAIN_DOC_NOTIFY, { objectId: { $in: toRemove } })
}
export const chunterOperation: MigrateOperation = {
async migrate (client: MigrationClient): Promise<void> {
await tryMigrate(client, chunterId, [
@ -283,6 +331,12 @@ export const chunterOperation: MigrateOperation = {
func: async (client) => {
await removeWrongActivity(client)
}
},
{
state: 'remove-duplicated-directs-v1',
func: async (client) => {
await removeDuplicatedDirects(client)
}
}
])
},

View File

@ -17,9 +17,9 @@
import { deepEqual } from 'fast-equals'
import { DirectMessage } from '@hcengineering/chunter'
import contact, { Employee, PersonAccount } from '@hcengineering/contact'
import contact, { Employee, Person, PersonAccount } from '@hcengineering/contact'
import core, { getCurrentAccount, Ref } from '@hcengineering/core'
import { SelectUsersPopup } from '@hcengineering/contact-resources'
import { personIdByAccountId, SelectUsersPopup } from '@hcengineering/contact-resources'
import notification from '@hcengineering/notification'
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
import { Modal, showPopup } from '@hcengineering/ui'
@ -55,15 +55,31 @@
const accIds = [myAcc._id, ...employeeAccounts.filter(({ _id }) => _id !== myAcc._id).map(({ _id }) => _id)].sort()
const existingDms = await client.findAll(chunter.class.DirectMessage, {})
const newDirectPersons = Array.from(new Set([...employeeIds, myAcc.person])).sort()
let direct: DirectMessage | undefined
for (const dm of existingDms) {
if (deepEqual(dm.members.sort(), accIds)) {
const existDirectPersons = Array.from(
new Set(dm.members.map((id) => $personIdByAccountId.get(id as Ref<PersonAccount>)))
)
.filter((person): person is Ref<Person> => person !== undefined)
.sort()
if (deepEqual(existDirectPersons, newDirectPersons)) {
direct = dm
break
}
}
const existingMembers = direct?.members
const missingAccounts = existingMembers !== undefined ? accIds.filter((id) => !existingMembers.includes(id)) : []
if (direct !== undefined && missingAccounts.length > 0) {
await client.updateDoc(chunter.class.DirectMessage, direct.space, direct._id, {
members: [...direct.members, ...missingAccounts]
})
}
const dmId =
direct?._id ??
(await client.createDoc(chunter.class.DirectMessage, core.space.Space, {
@ -75,12 +91,13 @@
}))
const context = await client.findOne(notification.class.DocNotifyContext, {
person: myAcc.person,
user: myAcc._id,
objectId: dmId,
objectClass: chunter.class.DirectMessage
})
if (context !== undefined) {
dispatch('close')
openChannel(dmId, chunter.class.DirectMessage)
return
@ -97,6 +114,7 @@
})
openChannel(dmId, chunter.class.DirectMessage)
dispatch('close')
}
function handleCancel (): void {

View File

@ -25,7 +25,9 @@ import { Person, ChannelProvider as SocialChannelProvider } from '@hcengineering
/**
* @public
*/
export interface ChunterSpace extends Space {}
export interface ChunterSpace extends Space {
messages?: number
}
/**
* @public

View File

@ -16,38 +16,38 @@
import {
AvatarType,
type Channel,
type ChannelProvider,
type Contact,
contactId,
type Employee,
formatName,
getFirstName,
getLastName,
getName,
type Channel,
type ChannelProvider,
type Contact,
type Employee,
type Person,
type PersonAccount
} from '@hcengineering/contact'
import core, {
getCurrentAccount,
toIdMap,
type Account,
AggregateValue,
AggregateValueData,
type Class,
type Client,
type Doc,
type DocumentQuery,
getCurrentAccount,
type Hierarchy,
type IdMap,
matchQuery,
type ObjQueryType,
type Ref,
type Space,
type Timestamp,
toIdMap,
type TxOperations,
type UserStatus,
type WithLookup,
AggregateValue,
type Space,
type Hierarchy,
type DocumentQuery,
AggregateValueData,
matchQuery
type WithLookup
} from '@hcengineering/core'
import notification, { type DocNotifyContext, type InboxNotification } from '@hcengineering/notification'
import { getEmbeddedLabel, getResource, translate } from '@hcengineering/platform'
@ -61,8 +61,8 @@ import {
type ResolvedLocation,
type TabItem
} from '@hcengineering/ui'
import view, { type GrouppingManager, type Filter } from '@hcengineering/view'
import { FilterQuery, accessDeniedStore } from '@hcengineering/view-resources'
import view, { type Filter, type GrouppingManager } from '@hcengineering/view'
import { accessDeniedStore, FilterQuery } from '@hcengineering/view-resources'
import { derived, get, writable } from 'svelte/store'
import contact from './plugin'
@ -305,6 +305,10 @@ export const channelProviders = writable<ChannelProvider[]>([])
export const personAccountPersonByIdStore = writable<IdMap<WithLookup<Person>>>(new Map())
export const personIdByAccountId = derived(personAccountByIdStore, (vals) => {
return new Map<Ref<PersonAccount>, Ref<Person>>(Array.from(vals.values()).map((it) => [it._id, it.person]))
})
export const statusByUserStore = writable<Map<Ref<Account>, UserStatus>>(new Map())
export const personByIdStore = derived([personAccountPersonByIdStore, employeeByIdStore], (vals) => {