Files
huly-platform/plugins/inbox-resources/src/location.ts
T
2025-10-22 09:14:35 +07:00

250 lines
7.2 KiB
TypeScript

//
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import type { Class, Doc, Ref, WithLookup } from '@hcengineering/core'
import {
navigate,
type Location,
getCurrentResolvedLocation,
getLocation,
locationStorageKeyId,
getCurrentLocation
} from '@hcengineering/ui'
import { inboxId } from '@hcengineering/inbox'
import { type MessageID, type Notification } from '@hcengineering/communication-types'
import { decodeObjectURI, encodeObjectURI } from '@hcengineering/view'
import activity, { type ActivityMessage } from '@hcengineering/activity'
import {
type ActivityInboxNotification,
type DocNotifyContext,
type InboxNotification
} from '@hcengineering/notification'
import { getResource } from '@hcengineering/platform'
import chunter, { type ThreadMessage } from '@hcengineering/chunter'
import { isActivityMessageClass, messageInFocus } from '@hcengineering/activity-resources'
import { getClient } from '@hcengineering/presentation'
import { isMentionNotification, isReactionNotification } from '@hcengineering/notification-resources'
import { type NavigationItem } from './type'
// Url: /inbox/{_class}&{_id}/{thread}?message={messageId}
export function getDocInfoFromLocation (loc: Location): Pick<Doc, '_id' | '_class'> | undefined {
if (loc.path[2] !== inboxId) {
return undefined
}
const [_id, _class] = decodeObjectURI(loc.path[3])
if (_id == null || _class == null) return undefined
if (_id === '' || _class === '') return undefined
return {
_id,
_class
}
}
export function getMessageInfoFromLocation (loc: Location):
| {
id: Ref<ActivityMessage> | MessageID
date?: Date
}
| undefined {
if (loc.path[2] !== inboxId) return undefined
if (loc.query?.message == null || loc.query.message === '') return undefined
const [id, date] = decodeURIComponent(loc.query.message).split(':')
if (id == null || id === '') return undefined
return {
id: id as any,
date: date != null ? new Date(date) : undefined
}
}
export function navigateToDoc (
navItem: NavigationItem,
doc: Doc,
notification?: InboxNotification | Notification
): void {
if (navItem.type === 'legacy') {
void selectInboxContext(navItem.context, doc, notification as InboxNotification)
return
}
const loc = getCurrentResolvedLocation()
loc.path[2] = inboxId
loc.path[3] = encodeObjectURI(navItem._id, navItem._class)
loc.path[4] = ''
loc.path.length = 4
delete loc.fragment
const nn = notification as Notification | undefined
if (nn?.message != null) {
loc.query = {
...loc.query,
message: encodeURIComponent(`${nn.messageId}:${nn.message.created.toISOString()}`)
}
} else {
delete loc.query?.message
}
navigate(loc)
}
export function closeDoc (): void {
const loc = getCurrentResolvedLocation()
loc.path[2] = inboxId
loc.path[3] = ''
loc.path[4] = ''
loc.path.length = 3
delete loc.query?.message
delete loc.fragment
localStorage.setItem(`${locationStorageKeyId}_${inboxId}`, JSON.stringify(loc))
navigate(loc)
}
async function navigateToInboxDoc (
context: Ref<DocNotifyContext>,
_id: Ref<Doc>,
_class: Ref<Class<Doc>>,
thread?: Ref<ActivityMessage>,
message?: Ref<ActivityMessage>
): Promise<void> {
const loc = getLocation()
if (loc.path[2] !== inboxId) {
return
}
loc.path[3] = encodeObjectURI(_id, _class)
if (thread !== undefined) {
loc.path[4] = thread
loc.path.length = 5
const fn = await getResource(chunter.function.OpenThreadInSidebar)
void fn(thread, undefined, undefined, message, { autofocus: false })
} else {
loc.path[4] = ''
loc.path.length = 4
}
loc.query = { ...loc.query, context, message: message ?? null }
messageInFocus.set(message)
navigate(loc)
}
export async function selectInboxContext (
context: DocNotifyContext,
doc?: Doc,
notification?: WithLookup<InboxNotification>
): Promise<void> {
const client = getClient()
const hierarchy = client.getHierarchy()
const { objectId, objectClass } = context
const loc = getCurrentLocation()
if (isMentionNotification(notification) && isActivityMessageClass(notification.mentionedInClass)) {
const selectedMsg = notification.mentionedIn as Ref<ActivityMessage>
void navigateToInboxDoc(
context._id,
objectId,
objectClass,
isActivityMessageClass(objectClass) ? (objectId as Ref<ActivityMessage>) : undefined,
selectedMsg
)
return
}
if (isReactionNotification(notification)) {
const thread = hierarchy.isDerived(context.objectClass, activity.class.ActivityMessage)
? context.objectId
: undefined
const reactedTo = await client.findOne(activity.class.ActivityMessage, { _id: notification.attachedTo })
const isThread = reactedTo != null && hierarchy.isDerived(reactedTo._class, chunter.class.ThreadMessage)
const channelId = isThread ? (reactedTo as ThreadMessage)?.objectId : (reactedTo?.attachedTo ?? objectId)
const channelClass = isThread
? (reactedTo as ThreadMessage)?.objectClass
: (reactedTo?.attachedToClass ?? objectClass)
void navigateToInboxDoc(
context._id,
channelId,
channelClass,
thread as Ref<ActivityMessage>,
notification.attachedTo
)
return
}
if (hierarchy.isDerived(objectClass, activity.class.ActivityMessage)) {
const message = (notification as WithLookup<ActivityInboxNotification>)?.$lookup?.attachedTo
if (objectClass === chunter.class.ThreadMessage) {
const thread =
doc?._id === objectId
? (doc as ThreadMessage)
: await client.findOne(
chunter.class.ThreadMessage,
{
_id: objectId as Ref<ThreadMessage>
},
{ projection: { _id: 1, attachedTo: 1 } }
)
void navigateToInboxDoc(
context._id,
thread?.objectId ?? objectId,
thread?.objectClass ?? objectClass,
thread?.attachedTo,
thread?._id
)
return
}
const selectedMsg = (notification as ActivityInboxNotification)?.attachedTo
const thread = selectedMsg !== objectId ? objectId : loc.path[4] === objectId ? objectId : undefined
const channelId = (doc as ActivityMessage)?.attachedTo ?? message?.attachedTo ?? objectId
const channelClass = (doc as ActivityMessage)?.attachedToClass ?? message?.attachedToClass ?? objectClass
void navigateToInboxDoc(
context._id,
channelId,
channelClass,
thread as Ref<ActivityMessage>,
selectedMsg ?? (objectId as Ref<ActivityMessage>)
)
return
}
void navigateToInboxDoc(
context._id,
objectId,
objectClass,
undefined,
(notification as ActivityInboxNotification)?.attachedTo
)
}