From d81f91fc3b320fff91430f1f65ed7bf5cb481575 Mon Sep 17 00:00:00 2001 From: Kristina Date: Tue, 16 Apr 2024 13:11:21 +0400 Subject: [PATCH] UBERF-5686: Fix copy link (#5368) Signed-off-by: Kristina Fefelova --- models/chunter/src/index.ts | 59 +++---- .../src/components/Channel.svelte | 12 +- .../src/components/ChannelPanel.svelte | 2 +- .../src/components/ChannelView.svelte | 21 ++- .../src/components/DmHeader.svelte | 4 +- .../src/components/Header.svelte | 2 +- .../src/components/Replies.svelte | 19 +-- .../src/components/chat/Chat.svelte | 67 ++++---- .../src/components/chat/ChatAside.svelte | 2 +- .../chat/create/CreateChannel.svelte | 6 +- .../chat/create/CreateDirectChat.svelte | 34 ++-- .../chat/navigator/ChatNavGroup.svelte | 60 ++++++- .../chat/navigator/ChatNavItem.svelte | 29 +++- .../chat/navigator/ChatNavSection.svelte | 23 +-- .../chat/navigator/ChatNavigator.svelte | 9 +- .../chat/specials/ChannelBrowser.svelte | 44 +----- .../chat/specials/MessagesBrowser.svelte | 2 +- .../chat/specials/SavedMessages.svelte | 2 +- .../src/components/chat/types.ts | 8 +- .../src/components/threads/Threads.svelte | 2 +- plugins/chunter-resources/src/index.ts | 74 +-------- plugins/chunter-resources/src/navigation.ts | 139 +++++++++++++++++ plugins/chunter-resources/src/plugin.ts | 1 - plugins/chunter-resources/src/utils.ts | 147 +----------------- plugins/chunter/src/index.ts | 2 - .../src/components/inbox/Inbox.svelte | 28 +++- plugins/notification-resources/src/utils.ts | 51 +++--- 27 files changed, 396 insertions(+), 453 deletions(-) create mode 100644 plugins/chunter-resources/src/navigation.ts diff --git a/models/chunter/src/index.ts b/models/chunter/src/index.ts index 356ef4c3cb..1e03d46582 100644 --- a/models/chunter/src/index.ts +++ b/models/chunter/src/index.ts @@ -402,29 +402,28 @@ export function createModel (builder: Builder, options = { addApplication: true encode: chunter.function.GetThreadLink }) - // Note: it is not working now, need to fix navigation by url UBERF-5686 - // createAction( - // builder, - // { - // action: view.actionImpl.CopyTextToClipboard, - // actionProps: { - // textProvider: chunter.function.GetLink - // }, - // label: chunter.string.CopyLink, - // icon: chunter.icon.Copy, - // keyBinding: [], - // input: 'none', - // category: chunter.category.Chunter, - // target: activity.class.ActivityMessage, - // visibilityTester: chunter.function.CanCopyMessageLink, - // context: { - // mode: ['context', 'browser'], - // application: chunter.app.Chunter, - // group: 'copy' - // } - // }, - // chunter.action.CopyChatMessageLink - // ) + createAction( + builder, + { + action: view.actionImpl.CopyTextToClipboard, + actionProps: { + textProvider: chunter.function.GetLink + }, + label: chunter.string.CopyLink, + icon: chunter.icon.Copy, + keyBinding: [], + input: 'none', + category: chunter.category.Chunter, + target: activity.class.ActivityMessage, + visibilityTester: chunter.function.CanCopyMessageLink, + context: { + mode: ['context', 'browser'], + application: chunter.app.Chunter, + group: 'copy' + } + }, + chunter.action.CopyChatMessageLink + ) builder.mixin(chunter.class.ChunterMessage, core.class.Class, view.mixin.ClassFilters, { filters: ['space', '_class'] @@ -602,20 +601,6 @@ export function createModel (builder: Builder, options = { addApplication: true chunter.action.LeaveChannel ) - createAction( - builder, - { - ...viewTemplates.open, - target: notification.class.DocNotifyContext, - context: { - mode: ['browser', 'context'], - group: 'create' - }, - action: chunter.actionImpl.OpenChannel - }, - chunter.action.OpenChannel - ) - createAction(builder, { ...notificationActionTemplates.pinContext, label: chunter.string.StarChannel, diff --git a/plugins/chunter-resources/src/components/Channel.svelte b/plugins/chunter-resources/src/components/Channel.svelte index 647f0437a2..defce21edc 100644 --- a/plugins/chunter-resources/src/components/Channel.svelte +++ b/plugins/chunter-resources/src/components/Channel.svelte @@ -24,8 +24,8 @@ import ChannelScrollView from './ChannelScrollView.svelte' import { ChannelDataProvider } from '../channelDataProvider' - export let context: DocNotifyContext - export let object: Doc | undefined + export let object: Doc + export let context: DocNotifyContext | undefined export let filters: Ref[] = [] export let isAsideOpened = false @@ -39,11 +39,11 @@ selectedMessageId = getMessageFromLoc(newLocation) }) - $: isDocChannel = !hierarchy.isDerived(context.attachedToClass, chunter.class.ChunterSpace) + $: isDocChannel = !hierarchy.isDerived(object._class, chunter.class.ChunterSpace) $: _class = isDocChannel ? activity.class.ActivityMessage : chunter.class.ChatMessage $: collection = isDocChannel ? 'comments' : 'messages' - $: updateDataProvider(context.attachedTo, _class, context.lastViewedTimestamp, selectedMessageId) + $: updateDataProvider(object._id, _class, context?.lastViewedTimestamp, selectedMessageId) function updateDataProvider ( attachedTo: Ref, @@ -62,8 +62,8 @@ {#if dataProvider} -{#if context} +{#if object}
diff --git a/plugins/chunter-resources/src/components/ChannelView.svelte b/plugins/chunter-resources/src/components/ChannelView.svelte index 2e2cffcc07..52a331baf8 100644 --- a/plugins/chunter-resources/src/components/ChannelView.svelte +++ b/plugins/chunter-resources/src/components/ChannelView.svelte @@ -26,8 +26,8 @@ import chunter from '../plugin' import ChannelAside from './chat/ChannelAside.svelte' - export let context: DocNotifyContext - export let object: Doc | undefined = undefined + export let object: Doc + export let context: DocNotifyContext | undefined export let allowClose = false export let embedded = false @@ -43,9 +43,8 @@ isThreadOpened = newLocation.path[4] != null }) - $: isDocChat = !hierarchy.isDerived(context.attachedToClass, chunter.class.ChunterSpace) - $: withAside = - !embedded && !isThreadOpened && !hierarchy.isDerived(context.attachedToClass, chunter.class.DirectMessage) + $: isDocChat = !hierarchy.isDerived(object._class, chunter.class.ChunterSpace) + $: withAside = !embedded && !isThreadOpened && !hierarchy.isDerived(object._class, chunter.class.DirectMessage) function toChannel (object?: Doc): Channel | undefined { return object as Channel | undefined @@ -56,8 +55,8 @@
- {#key context._id} + {#key object._id} {/key}
@@ -82,10 +81,10 @@
- {#if hierarchy.isDerived(context.attachedToClass, chunter.class.Channel)} - + {#if hierarchy.isDerived(object._class, chunter.class.Channel)} + {:else} - + {/if}
diff --git a/plugins/chunter-resources/src/components/DmHeader.svelte b/plugins/chunter-resources/src/components/DmHeader.svelte index dcc85346c6..3dbe4dda2d 100644 --- a/plugins/chunter-resources/src/components/DmHeader.svelte +++ b/plugins/chunter-resources/src/components/DmHeader.svelte @@ -21,9 +21,11 @@ import { createQuery, getClient } from '@hcengineering/presentation' import { SearchEdit } from '@hcengineering/ui' import { openDoc } from '@hcengineering/view-resources' + import { userSearch } from '../index' import chunter from '../plugin' - import { getDmName, navigateToSpecial } from '../utils' + import { getDmName } from '../utils' + import { navigateToSpecial } from '../navigation' export let spaceId: Ref | undefined export let withSearch: boolean = true diff --git a/plugins/chunter-resources/src/components/Header.svelte b/plugins/chunter-resources/src/components/Header.svelte index b7d1d660be..103dc68a06 100644 --- a/plugins/chunter-resources/src/components/Header.svelte +++ b/plugins/chunter-resources/src/components/Header.svelte @@ -32,7 +32,7 @@ import { ActivityMessagesFilter } from '@hcengineering/activity' import { userSearch } from '../index' - import { navigateToSpecial } from '../utils' + import { navigateToSpecial } from '../navigation' import ChannelMessagesFilter from './ChannelMessagesFilter.svelte' export let object: Doc | undefined = undefined diff --git a/plugins/chunter-resources/src/components/Replies.svelte b/plugins/chunter-resources/src/components/Replies.svelte index 1dd7b9a216..4e8057d280 100644 --- a/plugins/chunter-resources/src/components/Replies.svelte +++ b/plugins/chunter-resources/src/components/Replies.svelte @@ -25,9 +25,8 @@ } from '@hcengineering/notification' import { getResource } from '@hcengineering/platform' import { Label, TimeSince, getLocation, navigate } from '@hcengineering/ui' - import { get } from 'svelte/store' - import { buildThreadLink } from '../utils' + import { buildThreadLink } from '../navigation' export let object: ActivityMessage export let embedded = false @@ -71,14 +70,14 @@ .some(({ isViewed }) => !isViewed) } - function updateQuery (personIds: Set>, personById: IdMap) { + function updateQuery (personIds: Set>, personById: IdMap): void { displayPersons = Array.from(personIds) .map((id) => personById.get(id)) .filter((person): person is Person => person !== undefined) .slice(0, maxDisplayPersons - 1) } - function handleReply (e: any) { + function handleReply (e: MouseEvent): void { e.stopPropagation() e.preventDefault() @@ -87,17 +86,7 @@ return } - if (inboxClient === undefined) { - return - } - - const context = get(inboxClient.contextByDoc).get(object.attachedTo) - - if (context === undefined) { - return - } - - navigate(buildThreadLink(getLocation(), context._id, object._id)) + navigate(buildThreadLink(getLocation(), object.attachedTo, object.attachedToClass, object._id)) } diff --git a/plugins/chunter-resources/src/components/chat/Chat.svelte b/plugins/chunter-resources/src/components/chat/Chat.svelte index 5646647a67..ee4ca8b364 100644 --- a/plugins/chunter-resources/src/components/chat/Chat.svelte +++ b/plugins/chunter-resources/src/components/chat/Chat.svelte @@ -13,7 +13,7 @@ // limitations under the License. --> diff --git a/plugins/chunter-resources/src/components/chat/specials/MessagesBrowser.svelte b/plugins/chunter-resources/src/components/chat/specials/MessagesBrowser.svelte index e35a03546b..db9a5bd70d 100644 --- a/plugins/chunter-resources/src/components/chat/specials/MessagesBrowser.svelte +++ b/plugins/chunter-resources/src/components/chat/specials/MessagesBrowser.svelte @@ -21,7 +21,7 @@ import { ActivityMessagePresenter } from '@hcengineering/activity-resources' import plugin from '../../../plugin' - import { openMessageFromSpecial } from '../../../utils' + import { openMessageFromSpecial } from '../../../navigation' export let withHeader: boolean = true export let search: string = '' diff --git a/plugins/chunter-resources/src/components/chat/specials/SavedMessages.svelte b/plugins/chunter-resources/src/components/chat/specials/SavedMessages.svelte index 12cfa2227c..cad1053ca4 100644 --- a/plugins/chunter-resources/src/components/chat/specials/SavedMessages.svelte +++ b/plugins/chunter-resources/src/components/chat/specials/SavedMessages.svelte @@ -24,9 +24,9 @@ import { ActivityMessagePresenter, savedMessagesStore } from '@hcengineering/activity-resources' import chunter from '../../../plugin' - import { openMessageFromSpecial } from '../../../utils' import { savedAttachmentsStore } from '../utils' import Header from '../../Header.svelte' + import { openMessageFromSpecial } from '../../../navigation' const client = getClient() diff --git a/plugins/chunter-resources/src/components/chat/types.ts b/plugins/chunter-resources/src/components/chat/types.ts index 9767402ae7..0622e05947 100644 --- a/plugins/chunter-resources/src/components/chat/types.ts +++ b/plugins/chunter-resources/src/components/chat/types.ts @@ -17,8 +17,10 @@ import { type Doc, type DocumentQuery, type Ref } from '@hcengineering/core' import { type DocNotifyContext } from '@hcengineering/notification' import { type AnySvelteComponent, type IconSize, type Action } from '@hcengineering/ui' +export type ChatGroup = 'activity' | 'direct' | 'channels' | 'starred' + export interface ChatNavGroupModel { - id: string + id: ChatGroup label?: IntlString query: DocumentQuery sortFn: (items: ChatNavItemModel[], contexts: DocNotifyContext[]) => ChatNavItemModel[] @@ -37,3 +39,7 @@ export interface ChatNavItemModel { isSecondary: boolean withIconBackground: boolean } + +export interface SelectChannelEvent { + object: Doc +} diff --git a/plugins/chunter-resources/src/components/threads/Threads.svelte b/plugins/chunter-resources/src/components/threads/Threads.svelte index c43a7607c9..4f3009141b 100644 --- a/plugins/chunter-resources/src/components/threads/Threads.svelte +++ b/plugins/chunter-resources/src/components/threads/Threads.svelte @@ -22,7 +22,7 @@ import chunter from '../../plugin' import Header from '../Header.svelte' - import { openMessageFromSpecial } from '../../utils' + import { openMessageFromSpecial } from '../../navigation' const threadsQuery = createQuery() const me = getCurrentAccount() as PersonAccount diff --git a/plugins/chunter-resources/src/index.ts b/plugins/chunter-resources/src/index.ts index 70f9661a80..e57553dfb3 100644 --- a/plugins/chunter-resources/src/index.ts +++ b/plugins/chunter-resources/src/index.ts @@ -13,17 +13,14 @@ // limitations under the License. // -import { get, writable } from 'svelte/store' -import chunter, { type Channel, type ChatMessage, chunterId, type DirectMessage } from '@hcengineering/chunter' -import { getCurrentAccount, type Ref } from '@hcengineering/core' +import { writable } from 'svelte/store' +import chunter, { type Channel, type ChatMessage, type DirectMessage } from '@hcengineering/chunter' import { type Resources } from '@hcengineering/platform' import { MessageBox, getClient } from '@hcengineering/presentation' -import { closePanel, getCurrentLocation, getLocation, navigate, showPopup } from '@hcengineering/ui' +import { getLocation, navigate, showPopup } from '@hcengineering/ui' import { type ActivityMessage } from '@hcengineering/activity' -import notification, { type DocNotifyContext, notificationId } from '@hcengineering/notification' import ChannelPresenter from './components/ChannelPresenter.svelte' -import ChannelView from './components/ChannelView.svelte' import ChannelPanel from './components/ChannelPanel.svelte' import ChunterBrowser from './components/chat/specials/ChunterBrowser.svelte' import ConvertDmToPrivateChannelModal from './components/ConvertDmToPrivateChannel.svelte' @@ -60,19 +57,15 @@ import { ChannelTitleProvider, DirectTitleProvider, canDeleteMessage, - chunterSpaceLinkFragmentProvider, dmIdentifierProvider, getDmName, - getMessageLink, getTitle, getUnreadThreadsCount, canCopyMessageLink, - buildThreadLink, - getThreadLink, leaveChannelAction, removeChannelAction } from './utils' -import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources' +import { chunterSpaceLinkFragmentProvider, getThreadLink, getMessageLink } from './navigation' export { default as ChatMessagesPresenter } from './components/chat-message/ChatMessagesPresenter.svelte' export { default as ChatMessagePopup } from './components/chat-message/ChatMessagePopup.svelte' @@ -132,34 +125,6 @@ async function ConvertDmToPrivateChannel (dm: DirectMessage): Promise { }) } -export async function openChannel ( - notifyContext?: DocNotifyContext, - evt?: Event, - props?: { _id: Ref } -): Promise { - evt?.preventDefault() - - closePanel() - - const loc = getCurrentLocation() - const id = notifyContext?._id ?? props?._id - - if (id === undefined) { - return - } - - if (loc.path[3] === id) { - return - } - - loc.path[3] = id - loc.path.length = 4 - loc.query = { message: null } - - loc.fragment = undefined - - navigate(loc) -} export const userSearch = writable('') export async function chunterBrowserVisible (): Promise { @@ -176,34 +141,7 @@ export async function deleteChatMessage (message: ChatMessage): Promise { await client.remove(message) } -export async function replyToThread (message: ActivityMessage): Promise { - const loc = getCurrentLocation() - const client = getClient() - - const inboxClient = InboxNotificationsClientImpl.getClient() - - let contextId: Ref | undefined = get(inboxClient.contextByDoc).get(message.attachedTo)?._id - - if (contextId === undefined) { - contextId = await client.createDoc(notification.class.DocNotifyContext, message.space, { - attachedTo: message.attachedTo, - attachedToClass: message.attachedToClass, - user: getCurrentAccount()._id, - hidden: false, - lastViewedTimestamp: Date.now() - }) - } - - if (contextId === undefined) { - return - } - - if (loc.path[2] !== notificationId) { - loc.path[2] = chunterId - } - - navigate(buildThreadLink(loc, contextId, message._id)) -} +export { replyToThread } from './navigation' export default async (): Promise => ({ filter: { @@ -215,7 +153,6 @@ export default async (): Promise => ({ ThreadParentPresenter, ThreadViewPanel, ChannelHeader, - ChannelView, ChannelPanel, ChannelPresenter, DirectMessagePresenter, @@ -262,7 +199,6 @@ export default async (): Promise => ({ UnarchiveChannel, ConvertDmToPrivateChannel, DeleteChatMessage: deleteChatMessage, - OpenChannel: openChannel, LeaveChannel: leaveChannelAction, RemoveChannel: removeChannelAction } diff --git a/plugins/chunter-resources/src/navigation.ts b/plugins/chunter-resources/src/navigation.ts new file mode 100644 index 0000000000..2464ca26f5 --- /dev/null +++ b/plugins/chunter-resources/src/navigation.ts @@ -0,0 +1,139 @@ +import { getCurrentLocation, getCurrentResolvedLocation, getLocation, type Location, navigate } from '@hcengineering/ui' +import { type Ref, type Doc, type Class } from '@hcengineering/core' +import type { ActivityMessage } from '@hcengineering/activity' +import { chunterId, type ChunterSpace, type ThreadMessage } from '@hcengineering/chunter' +import { notificationId } from '@hcengineering/notification' +import { workbenchId } from '@hcengineering/workbench' + +import { chatSpecials } from './components/chat/utils' +import { isThreadMessage } from './utils' + +export function decodeChannelURI (value: string): [Ref, Ref>] { + return decodeURIComponent(value).split('|') as [Ref, Ref>] +} + +function encodeChannelURI (_id: Ref, _class: Ref>): string { + return encodeURIComponent([_id, _class].join('|')) +} + +export function openChannel (_id: Ref, _class: Ref>): void { + const loc = getCurrentLocation() + + const id = encodeChannelURI(_id, _class) + + if (loc.path[3] === id) { + return + } + + loc.path[3] = id + loc.path[4] = '' + loc.query = { ...loc.query, message: null } + loc.path.length = 4 + + navigate(loc) +} + +export async function openMessageFromSpecial (message?: ActivityMessage): Promise { + if (message === undefined) { + return + } + + const loc = getCurrentResolvedLocation() + + if (isThreadMessage(message)) { + loc.path[3] = encodeChannelURI(message.objectId, message.objectClass) + loc.path[4] = message.attachedTo + } else { + loc.path[3] = encodeChannelURI(message.attachedTo, message.attachedToClass) + } + + loc.query = { ...loc.query, message: message._id } + + navigate(loc) +} + +export function navigateToSpecial (specialId: string): void { + const loc = getLocation() + loc.path[2] = chunterId + loc.path[3] = specialId + navigate(loc) +} + +export async function getMessageLink (message: ActivityMessage): Promise { + const location = getCurrentResolvedLocation() + + let threadParent = '' + let _id: Ref + let _class: Ref> + + if (isThreadMessage(message)) { + threadParent = `/${message.attachedTo}` + _id = message.objectId + _class = message.objectClass + } else { + _id = message.attachedTo + _class = message.attachedToClass + } + + const id = encodeChannelURI(_id, _class) + + return `${window.location.protocol}//${window.location.host}/${workbenchId}/${location.path[1]}/${chunterId}/${id}${threadParent}?message=${message._id}` +} + +export async function chunterSpaceLinkFragmentProvider (doc: ChunterSpace): Promise { + const loc = getCurrentResolvedLocation() + + loc.path.length = 2 + loc.fragment = undefined + loc.query = undefined + loc.path[2] = chunterId + loc.path[3] = encodeChannelURI(doc._id, doc._class) + + return loc +} + +export function buildThreadLink ( + loc: Location, + _id: Ref, + _class: Ref>, + threadParent: Ref +): Location { + const specials = chatSpecials.map(({ id }) => id) + const id = encodeChannelURI(_id, _class) + const isSameChannel = loc.path[3] === id + + if (!isSameChannel) { + loc.query = { message: threadParent } + } + + if (loc.path[2] === chunterId && specials.includes(loc.path[3])) { + loc.path[4] = threadParent + return loc + } + + if (loc.path[2] !== notificationId) { + loc.path[2] = chunterId + } + + loc.path[3] = id + loc.path[4] = threadParent + loc.fragment = undefined + + return loc +} + +export async function getThreadLink (doc: ThreadMessage): Promise { + const loc = getCurrentResolvedLocation() + + return buildThreadLink(loc, doc.objectId, doc.objectClass, doc.attachedTo) +} + +export async function replyToThread (message: ActivityMessage): Promise { + const loc = getCurrentLocation() + + if (loc.path[2] !== notificationId) { + loc.path[2] = chunterId + } + + navigate(buildThreadLink(loc, message.attachedTo, message.attachedToClass, message._id)) +} diff --git a/plugins/chunter-resources/src/plugin.ts b/plugins/chunter-resources/src/plugin.ts index dda29a6ffd..32f2986b00 100644 --- a/plugins/chunter-resources/src/plugin.ts +++ b/plugins/chunter-resources/src/plugin.ts @@ -54,7 +54,6 @@ export default mergeIds(chunterId, chunter, { UnsubscribeMessage: '' as ViewAction, SubscribeComment: '' as ViewAction, UnsubscribeComment: '' as ViewAction, - OpenChannel: '' as ViewAction, LeaveChannel: '' as ViewAction, RemoveChannel: '' as ViewAction }, diff --git a/plugins/chunter-resources/src/utils.ts b/plugins/chunter-resources/src/utils.ts index 7837d80d36..9e0b5cd4ed 100644 --- a/plugins/chunter-resources/src/utils.ts +++ b/plugins/chunter-resources/src/utils.ts @@ -12,14 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -import { - type Channel, - type ChatMessage, - chunterId, - type ChunterSpace, - type DirectMessage, - type ThreadMessage -} from '@hcengineering/chunter' +import { type Channel, type ChatMessage, type DirectMessage, type ThreadMessage } from '@hcengineering/chunter' import contact, { type Employee, type PersonAccount, getName, type Person } from '@hcengineering/contact' import { employeeByIdStore, PersonIcon } from '@hcengineering/contact-resources' import { @@ -35,14 +28,7 @@ import { generateId } from '@hcengineering/core' import { getClient } from '@hcengineering/presentation' -import { - type AnySvelteComponent, - getCurrentResolvedLocation, - getLocation, - type Location, - navigate -} from '@hcengineering/ui' -import { workbenchId } from '@hcengineering/workbench' +import { type AnySvelteComponent } from '@hcengineering/ui' import { type Asset, getResource, translate } from '@hcengineering/platform' import { classIcon, getDocLinkTitle, getDocTitle } from '@hcengineering/view-resources' import activity, { @@ -57,13 +43,12 @@ import { InboxNotificationsClientImpl, isMentionNotification } from '@hcengineering/notification-resources' -import notification, { type DocNotifyContext, notificationId } from '@hcengineering/notification' +import notification, { type DocNotifyContext } from '@hcengineering/notification' import { get, type Unsubscriber } from 'svelte/store' import chunter from './plugin' import DirectIcon from './components/DirectIcon.svelte' import ChannelIcon from './components/ChannelIcon.svelte' -import { chatSpecials } from './components/chat/utils' export async function getDmName (client: Client, space?: Space): Promise { if (space === undefined) { @@ -205,40 +190,6 @@ export async function ChannelTitleProvider (client: Client, id: Ref): P return channel.name } -export async function openMessageFromSpecial (message?: ActivityMessage): Promise { - if (message === undefined) { - return - } - - const inboxClient = InboxNotificationsClientImpl.getClient() - const loc = getCurrentResolvedLocation() - - if (message._class === chunter.class.ThreadMessage) { - const threadMessage = message as ThreadMessage - - loc.path[4] = threadMessage.attachedTo - } else { - const context = get(inboxClient.contextByDoc).get(message.attachedTo) - - if (context === undefined) { - return - } - - loc.path[4] = context._id - } - - loc.query = { ...loc.query, message: message._id } - - navigate(loc) -} - -export function navigateToSpecial (specialId: string): void { - const loc = getLocation() - loc.path[2] = chunterId - loc.path[3] = specialId - navigate(loc) -} - export enum SearchType { Messages, Channels, @@ -246,28 +197,6 @@ export enum SearchType { Contacts } -export async function getMessageLink (message: ActivityMessage): Promise { - const inboxClient = InboxNotificationsClientImpl.getClient() - const location = getCurrentResolvedLocation() - - let context: DocNotifyContext | undefined - let threadParent: string = '' - - if (message._class === chunter.class.ThreadMessage) { - const threadMessage = message as ThreadMessage - threadParent = `/${threadMessage.attachedTo}` - context = get(inboxClient.contextByDoc).get(threadMessage.objectId) - } else { - context = get(inboxClient.contextByDoc).get(message.attachedTo) - } - - if (context === undefined) { - return '' - } - - return `${window.location.protocol}//${window.location.host}/${workbenchId}/${location.path[1]}/${chunterId}/${context._id}${threadParent}?message=${message._id}` -} - export async function getTitle (doc: Doc): Promise { const client = getClient() const hierarchy = client.getHierarchy() @@ -281,24 +210,6 @@ export async function getTitle (doc: Doc): Promise { return `${label}-${doc._id}` } -export async function chunterSpaceLinkFragmentProvider (doc: ChunterSpace): Promise { - const inboxClient = InboxNotificationsClientImpl.getClient() - const context = get(inboxClient.contextByDoc).get(doc._id) - const loc = getCurrentResolvedLocation() - - if (context === undefined) { - return loc - } - - loc.path.length = 2 - loc.fragment = undefined - loc.query = undefined - loc.path[2] = chunterId - loc.path[3] = context._id - - return loc -} - export function getObjectIcon (_class: Ref>): Asset | AnySvelteComponent | undefined { const client = getClient() const hierarchy = client.getHierarchy() @@ -387,54 +298,6 @@ export async function filterChatMessages ( return messages.filter((message) => filtersFns.some((filterFn) => filterFn(message, objectClass))) } -export function buildThreadLink (loc: Location, contextId: Ref, _id: Ref): Location { - const specials = chatSpecials.map(({ id }) => id) - const isSameContext = loc.path[3] === contextId - - if (!isSameContext) { - loc.query = { message: _id } - } - - if (loc.path[2] === chunterId && specials.includes(loc.path[3])) { - loc.path[4] = _id - return loc - } - - if (loc.path[2] !== notificationId) { - loc.path[2] = chunterId - } - - loc.path[3] = contextId - loc.path[4] = _id - loc.fragment = undefined - - return loc -} - -export async function getThreadLink (doc: ThreadMessage): Promise { - const loc = getCurrentResolvedLocation() - const client = getClient() - const inboxClient = InboxNotificationsClientImpl.getClient() - - let contextId: Ref | undefined = get(inboxClient.contextByDoc).get(doc.objectId)?._id - - if (contextId === undefined) { - contextId = await client.createDoc(notification.class.DocNotifyContext, doc.space, { - attachedTo: doc.attachedTo, - attachedToClass: doc.attachedToClass, - user: getCurrentAccount()._id, - hidden: false, - lastViewedTimestamp: Date.now() - }) - } - - if (contextId === undefined) { - return loc - } - - return buildThreadLink(loc, contextId, doc.attachedTo) -} - export async function joinChannel (channel: Channel, value: Ref | Array>): Promise { const client = getClient() @@ -533,3 +396,7 @@ export async function removeChannelAction (context?: DocNotifyContext): Promise< await deleteContextNotifications(context) await client.remove(context) } + +export function isThreadMessage (message: ActivityMessage): message is ThreadMessage { + return message._class === chunter.class.ThreadMessage +} diff --git a/plugins/chunter/src/index.ts b/plugins/chunter/src/index.ts index ddb523b21c..80e5c2105e 100644 --- a/plugins/chunter/src/index.ts +++ b/plugins/chunter/src/index.ts @@ -130,7 +130,6 @@ export default plugin(chunterId, { }, component: { DmHeader: '' as AnyComponent, - ChannelView: '' as AnyComponent, ThreadView: '' as AnyComponent, Thread: '' as AnyComponent, Reactions: '' as AnyComponent, @@ -211,7 +210,6 @@ export default plugin(chunterId, { }, action: { DeleteChatMessage: '' as Ref, - OpenChannel: '' as Ref, LeaveChannel: '' as Ref, RemoveChannel: '' as Ref, CloseConversation: '' as Ref diff --git a/plugins/notification-resources/src/components/inbox/Inbox.svelte b/plugins/notification-resources/src/components/inbox/Inbox.svelte index dd33db03d6..bec1f0ae16 100644 --- a/plugins/notification-resources/src/components/inbox/Inbox.svelte +++ b/plugins/notification-resources/src/components/inbox/Inbox.svelte @@ -45,6 +45,7 @@ import Filter from '../Filter.svelte' import { archiveAll, + decodeObjectURI, getDisplayInboxData, isMentionNotification, openInboxDoc, @@ -65,6 +66,7 @@ const inboxClient = InboxNotificationsClientImpl.getClient() const notificationsByContextStore = inboxClient.inboxNotificationsByContext const contextByIdStore = inboxClient.contextById + const contextByDocStore = inboxClient.contextByDoc const contextsStore = inboxClient.contexts const allTab: TabItem = { @@ -98,8 +100,10 @@ async function syncLocation (newLocation: Location): Promise { const loc = await resolveLocation(newLocation) + const [_id] = decodeObjectURI(loc?.loc.path[3] ?? '') + const context = $contextByDocStore.get(_id) - selectedContextId = loc?.loc.path[3] as Ref | undefined + selectedContextId = context?._id if (selectedContextId !== selectedContext?._id) { selectedContext = undefined @@ -179,7 +183,8 @@ const selectedMsg = selectedNotification.mentionedIn as Ref openInboxDoc( - selectedContext._id, + selectedContext.attachedTo, + selectedContext.attachedToClass, isActivityMessageClass(selectedContext.attachedToClass) ? (selectedContext.attachedTo as Ref) : undefined, @@ -192,20 +197,31 @@ const thread = await client.findOne(chunter.class.ThreadMessage, { _id: selectedContext.attachedTo as Ref }) - openInboxDoc(selectedContext._id, thread?.attachedTo, thread?._id) + openInboxDoc(selectedContext.attachedTo, selectedContext.attachedToClass, thread?.attachedTo, thread?._id) } else if (isReactionMessage(message)) { - openInboxDoc(selectedContext._id, undefined, selectedContext.attachedTo as Ref) + openInboxDoc( + selectedContext.attachedTo, + selectedContext.attachedToClass, + undefined, + selectedContext.attachedTo as Ref + ) } else { const selectedMsg = (selectedNotification as ActivityInboxNotification)?.attachedTo openInboxDoc( - selectedContext._id, + selectedContext.attachedTo, + selectedContext.attachedToClass, selectedMsg ? (selectedContext.attachedTo as Ref) : undefined, selectedMsg ?? (selectedContext.attachedTo as Ref) ) } } else { - openInboxDoc(selectedContext._id, undefined, (selectedNotification as ActivityInboxNotification)?.attachedTo) + openInboxDoc( + selectedContext.attachedTo, + selectedContext.attachedToClass, + undefined, + (selectedNotification as ActivityInboxNotification)?.attachedTo + ) } } diff --git a/plugins/notification-resources/src/utils.ts b/plugins/notification-resources/src/utils.ts index c582f4adc3..1b1c9e7976 100644 --- a/plugins/notification-resources/src/utils.ts +++ b/plugins/notification-resources/src/utils.ts @@ -409,9 +409,9 @@ export async function resolveLocation (loc: Location): Promise | undefined + const [_id, _class] = decodeObjectURI(loc.path[3]) - if (contextId === undefined) { + if (_id === undefined || _class === undefined) { return { loc: { path: [loc.path[0], loc.path[1], notificationId], @@ -424,12 +424,13 @@ export async function resolveLocation (loc: Location): Promise + _id: Ref, + _class: Ref> ): Promise { const client = getClient() @@ -437,35 +438,18 @@ async function generateLocation ( const workspace = loc.path[1] ?? '' const threadId = loc.path[4] as Ref | undefined - const contextNotification = await client.findOne(notification.class.InboxNotification, { - docNotifyContext: contextId - }) - - if (contextNotification === undefined) { - return { - loc: { - path: [loc.path[0], loc.path[1], notificationId], - fragment: undefined - }, - defaultLocation: { - path: [loc.path[0], loc.path[1], notificationId], - fragment: undefined - } - } - } - const thread = threadId !== undefined ? await client.findOne(activity.class.ActivityMessage, { _id: threadId }) : undefined if (thread === undefined) { return { loc: { - path: [appComponent, workspace, notificationId, contextId], + path: [appComponent, workspace, notificationId, encodeObjectURI(_id, _class)], fragment: undefined, query: { ...loc.query } }, defaultLocation: { - path: [appComponent, workspace, notificationId, contextId], + path: [appComponent, workspace, notificationId, encodeObjectURI(_id, _class)], fragment: undefined, query: { ...loc.query } } @@ -474,20 +458,29 @@ async function generateLocation ( return { loc: { - path: [appComponent, workspace, notificationId, contextId, threadId as string], + path: [appComponent, workspace, notificationId, encodeObjectURI(_id, _class), threadId as string], fragment: undefined, query: { ...loc.query } }, defaultLocation: { - path: [appComponent, workspace, notificationId, contextId, threadId as string], + path: [appComponent, workspace, notificationId, encodeObjectURI(_id, _class), threadId as string], fragment: undefined, query: { ...loc.query } } } } +export function decodeObjectURI (value: string): [Ref, Ref>] { + return decodeURIComponent(value).split('|') as [Ref, Ref>] +} + +function encodeObjectURI (_id: Ref, _class: Ref>): string { + return encodeURIComponent([_id, _class].join('|')) +} + export function openInboxDoc ( - contextId?: Ref, + _id?: Ref, + _class?: Ref>, thread?: Ref, message?: Ref ): void { @@ -497,14 +490,14 @@ export function openInboxDoc ( return } - if (contextId === undefined) { - loc.query = { ...loc.query, message: null } + if (_id === undefined || _class === undefined) { + loc.query = { message: null } loc.path.length = 3 navigate(loc) return } - loc.path[3] = contextId + loc.path[3] = encodeObjectURI(_id, _class) if (thread !== undefined) { loc.path[4] = thread