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 export const DOMAIN_CHUNTER = 'chunter' as Domain
@Model(chunter.class.ChunterSpace, core.class.Space) @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) @Model(chunter.class.Channel, chunter.class.ChunterSpace)
@UX(chunter.string.Channel, chunter.icon.Hashtag, undefined, undefined, undefined, chunter.string.Channels) @UX(chunter.string.Channel, chunter.icon.Hashtag, undefined, undefined, undefined, chunter.string.Channels)

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
// //
import { chunterId, type ThreadMessage } from '@hcengineering/chunter' import { chunterId, type DirectMessage, type ThreadMessage } from '@hcengineering/chunter'
import core, { import core, {
type Account, type Account,
TxOperations, TxOperations,
@ -33,12 +33,13 @@ import {
} from '@hcengineering/model' } from '@hcengineering/model'
import activity, { migrateMessagesSpace, DOMAIN_ACTIVITY } from '@hcengineering/model-activity' import activity, { migrateMessagesSpace, DOMAIN_ACTIVITY } from '@hcengineering/model-activity'
import notification from '@hcengineering/notification' import notification from '@hcengineering/notification'
import contactPlugin, { type PersonAccount } from '@hcengineering/contact' import contactPlugin, { type Person, type PersonAccount } from '@hcengineering/contact'
import { DOMAIN_NOTIFICATION } from '@hcengineering/model-notification' 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 chunter from './plugin'
import { DOMAIN_CHUNTER } from './index' import { DOMAIN_CHUNTER } from './index'
import { type DocUpdateMessage } from '@hcengineering/activity'
export const DOMAIN_COMMENT = 'comment' as Domain 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 = { export const chunterOperation: MigrateOperation = {
async migrate (client: MigrationClient): Promise<void> { async migrate (client: MigrationClient): Promise<void> {
await tryMigrate(client, chunterId, [ await tryMigrate(client, chunterId, [
@ -283,6 +331,12 @@ export const chunterOperation: MigrateOperation = {
func: async (client) => { func: async (client) => {
await removeWrongActivity(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 { deepEqual } from 'fast-equals'
import { DirectMessage } from '@hcengineering/chunter' 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 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 notification from '@hcengineering/notification'
import presentation, { createQuery, getClient } from '@hcengineering/presentation' import presentation, { createQuery, getClient } from '@hcengineering/presentation'
import { Modal, showPopup } from '@hcengineering/ui' 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 accIds = [myAcc._id, ...employeeAccounts.filter(({ _id }) => _id !== myAcc._id).map(({ _id }) => _id)].sort()
const existingDms = await client.findAll(chunter.class.DirectMessage, {}) const existingDms = await client.findAll(chunter.class.DirectMessage, {})
const newDirectPersons = Array.from(new Set([...employeeIds, myAcc.person])).sort()
let direct: DirectMessage | undefined let direct: DirectMessage | undefined
for (const dm of existingDms) { 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 direct = dm
break 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 = const dmId =
direct?._id ?? direct?._id ??
(await client.createDoc(chunter.class.DirectMessage, core.space.Space, { (await client.createDoc(chunter.class.DirectMessage, core.space.Space, {
@ -75,12 +91,13 @@
})) }))
const context = await client.findOne(notification.class.DocNotifyContext, { const context = await client.findOne(notification.class.DocNotifyContext, {
person: myAcc.person, user: myAcc._id,
objectId: dmId, objectId: dmId,
objectClass: chunter.class.DirectMessage objectClass: chunter.class.DirectMessage
}) })
if (context !== undefined) { if (context !== undefined) {
dispatch('close')
openChannel(dmId, chunter.class.DirectMessage) openChannel(dmId, chunter.class.DirectMessage)
return return
@ -97,6 +114,7 @@
}) })
openChannel(dmId, chunter.class.DirectMessage) openChannel(dmId, chunter.class.DirectMessage)
dispatch('close')
} }
function handleCancel (): void { function handleCancel (): void {

View File

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

View File

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