mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-25 09:50:19 +00:00
UBERF-5686: Fix copy link (#5368)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
0d1d1a8b8d
commit
d81f91fc3b
@ -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,
|
||||
|
@ -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<ActivityMessagesFilter>[] = []
|
||||
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<Doc>,
|
||||
@ -62,8 +62,8 @@
|
||||
|
||||
{#if dataProvider}
|
||||
<ChannelScrollView
|
||||
objectId={context.attachedTo}
|
||||
objectClass={context.attachedToClass}
|
||||
objectId={object._id}
|
||||
objectClass={object._class}
|
||||
{object}
|
||||
skipLabels={!isDocChannel}
|
||||
selectedFilters={filters}
|
||||
|
@ -36,7 +36,7 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if context}
|
||||
{#if object}
|
||||
<div class="antiComponent">
|
||||
<ChannelView {object} {context} embedded allowClose on:close />
|
||||
</div>
|
||||
|
@ -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 @@
|
||||
|
||||
<div class="popupPanel panel" class:embedded>
|
||||
<ChannelHeader
|
||||
_id={context.attachedTo}
|
||||
_class={context.attachedToClass}
|
||||
_id={object._id}
|
||||
_class={object._class}
|
||||
{object}
|
||||
{allowClose}
|
||||
{withAside}
|
||||
@ -72,7 +71,7 @@
|
||||
|
||||
<div class="popupPanel-body" class:asideShown={withAside && isAsideShown}>
|
||||
<div class="popupPanel-body__main">
|
||||
{#key context._id}
|
||||
{#key object._id}
|
||||
<ChannelComponent {context} {object} {filters} isAsideOpened={(withAside && isAsideShown) || isThreadOpened} />
|
||||
{/key}
|
||||
</div>
|
||||
@ -82,10 +81,10 @@
|
||||
<div class="popupPanel-body__aside" class:float={false} class:shown={withAside && isAsideShown}>
|
||||
<Separator name="aside" float index={0} />
|
||||
<div class="antiPanel-wrap__content">
|
||||
{#if hierarchy.isDerived(context.attachedToClass, chunter.class.Channel)}
|
||||
<ChannelAside _class={context.attachedToClass} object={toChannel(object)} />
|
||||
{#if hierarchy.isDerived(object._class, chunter.class.Channel)}
|
||||
<ChannelAside _class={object._class} object={toChannel(object)} />
|
||||
{:else}
|
||||
<DocAside _class={context.attachedToClass} {object} />
|
||||
<DocAside _class={object._class} {object} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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<DirectMessage> | undefined
|
||||
export let withSearch: boolean = true
|
||||
|
@ -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
|
||||
|
@ -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<Ref<Person>>, personById: IdMap<Person>) {
|
||||
function updateQuery (personIds: Set<Ref<Person>>, personById: IdMap<Person>): 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))
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Doc, IdMap, Ref } from '@hcengineering/core'
|
||||
import { Doc, Ref, Class } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import {
|
||||
Component,
|
||||
@ -24,7 +24,6 @@
|
||||
Separator,
|
||||
Location
|
||||
} from '@hcengineering/ui'
|
||||
import { DocNotifyContext } from '@hcengineering/notification'
|
||||
|
||||
import { NavigatorModel, SpecialNavModel } from '@hcengineering/workbench'
|
||||
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
||||
@ -33,13 +32,15 @@
|
||||
import ChatNavigator from './navigator/ChatNavigator.svelte'
|
||||
import ChannelView from '../ChannelView.svelte'
|
||||
import { chatSpecials, loadSavedAttachments } from './utils'
|
||||
import { SelectChannelEvent } from './types'
|
||||
import { decodeChannelURI, openChannel } from '../../navigation'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
export let navFloat: boolean = false
|
||||
export let appsDirection: 'vertical' | 'horizontal' = 'horizontal'
|
||||
|
||||
const notificationsClient = InboxNotificationsClientImpl.getClient()
|
||||
const contextByIdStore = notificationsClient.contextById
|
||||
const contextByDocStore = notificationsClient.contextByDoc
|
||||
const objectQuery = createQuery()
|
||||
|
||||
const navigatorModel: NavigatorModel = {
|
||||
@ -47,8 +48,7 @@
|
||||
specials: chatSpecials
|
||||
}
|
||||
|
||||
let selectedContextId: Ref<DocNotifyContext> | undefined = undefined
|
||||
let selectedContext: DocNotifyContext | undefined = undefined
|
||||
let selectedData: { _id: Ref<Doc>, _class: Ref<Class<Doc>> } | undefined = undefined
|
||||
|
||||
let currentSpecial: SpecialNavModel | undefined
|
||||
|
||||
@ -58,17 +58,24 @@
|
||||
syncLocation(loc)
|
||||
})
|
||||
|
||||
$: updateSelectedContext($contextByIdStore, selectedContextId)
|
||||
$: void loadObject(selectedData?._id, selectedData?._class)
|
||||
|
||||
async function loadObject (_id?: Ref<Doc>, _class?: Ref<Class<Doc>>): Promise<void> {
|
||||
if (_id === undefined || _class === undefined) {
|
||||
object = undefined
|
||||
objectQuery.unsubscribe()
|
||||
return
|
||||
}
|
||||
|
||||
$: selectedContext &&
|
||||
objectQuery.query(
|
||||
selectedContext.attachedToClass,
|
||||
{ _id: selectedContext.attachedTo },
|
||||
_class,
|
||||
{ _id },
|
||||
(res) => {
|
||||
object = res[0]
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
}
|
||||
|
||||
function syncLocation (loc: Location) {
|
||||
const specialId = loc.path[3]
|
||||
@ -76,39 +83,24 @@
|
||||
currentSpecial = navigatorModel?.specials?.find((special) => special.id === specialId)
|
||||
|
||||
if (currentSpecial !== undefined) {
|
||||
selectedContext = undefined
|
||||
selectedContextId = undefined
|
||||
selectedData = undefined
|
||||
} else {
|
||||
selectedContextId = loc.path[3] as Ref<DocNotifyContext> | undefined
|
||||
const [_id, _class] = decodeChannelURI(loc.path[3])
|
||||
|
||||
selectedData = { _id, _class }
|
||||
}
|
||||
}
|
||||
|
||||
function updateSelectedContext (contexts: IdMap<DocNotifyContext>, _id?: Ref<DocNotifyContext>) {
|
||||
if (selectedContextId === undefined) {
|
||||
selectedContext = undefined
|
||||
} else {
|
||||
selectedContext = contexts.get(selectedContextId)
|
||||
}
|
||||
function handleChannelSelected (event: CustomEvent): void {
|
||||
const detail = (event.detail ?? {}) as SelectChannelEvent
|
||||
|
||||
selectedData = { _id: detail.object._id, _class: detail.object._class }
|
||||
|
||||
if (selectedData._id !== object?._id) {
|
||||
object = detail.object
|
||||
}
|
||||
|
||||
function handleChannelSelected (event: CustomEvent) {
|
||||
const { context } = event.detail ?? {}
|
||||
|
||||
selectedContext = context
|
||||
selectedContextId = selectedContext?._id
|
||||
|
||||
if (selectedContext?.attachedTo !== object?._id) {
|
||||
object = undefined
|
||||
}
|
||||
|
||||
const loc = getCurrentLocation()
|
||||
|
||||
loc.path[3] = selectedContextId as string
|
||||
loc.path[4] = ''
|
||||
loc.query = { ...loc.query, message: null }
|
||||
loc.path.length = 4
|
||||
|
||||
navigate(loc)
|
||||
openChannel(selectedData._id, selectedData._class)
|
||||
}
|
||||
|
||||
defineSeparators('chat', [
|
||||
@ -127,7 +119,7 @@
|
||||
class="antiPanel-navigator {appsDirection === 'horizontal' ? 'portrait' : 'landscape'} background-surface-color"
|
||||
>
|
||||
<div class="antiPanel-wrap__content">
|
||||
<ChatNavigator {selectedContextId} {currentSpecial} on:select={handleChannelSelected} />
|
||||
<ChatNavigator objectId={selectedData?._id} {object} {currentSpecial} on:select={handleChannelSelected} />
|
||||
</div>
|
||||
<Separator name="chat" float={navFloat ? 'navigator' : true} index={0} />
|
||||
</div>
|
||||
@ -153,8 +145,9 @@
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{:else if selectedContext}
|
||||
<ChannelView context={selectedContext} {object} />
|
||||
{:else if object}
|
||||
{@const context = $contextByDocStore.get(object._id)}
|
||||
<ChannelView {object} {context} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -43,6 +43,6 @@
|
||||
|
||||
{#if threadId}
|
||||
<ThreadView _id={threadId} on:close />
|
||||
{:else if context}
|
||||
{:else if object}
|
||||
<ChannelView {object} {context} allowClose embedded on:close />
|
||||
{/if}
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
import Lock from '../../icons/Lock.svelte'
|
||||
import chunter from '../../../plugin'
|
||||
import { openChannel } from '../../../index'
|
||||
import { openChannel } from '../../../navigation'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
@ -60,14 +60,14 @@
|
||||
members: [accountId],
|
||||
topic: description
|
||||
})
|
||||
const notifyContextId = await client.createDoc(notification.class.DocNotifyContext, core.space.Space, {
|
||||
await client.createDoc(notification.class.DocNotifyContext, core.space.Space, {
|
||||
user: accountId,
|
||||
attachedTo: channelId,
|
||||
attachedToClass: chunter.class.Channel,
|
||||
hidden: false
|
||||
})
|
||||
|
||||
await openChannel(undefined, undefined, { _id: notifyContextId })
|
||||
openChannel(channelId, chunter.class.Channel)
|
||||
}
|
||||
|
||||
function handleCancel (): void {
|
||||
|
@ -27,7 +27,7 @@
|
||||
import chunter from '../../../plugin'
|
||||
import { buildDmName } from '../../../utils'
|
||||
import ChannelMembers from '../../ChannelMembers.svelte'
|
||||
import { openChannel } from '../../../index'
|
||||
import { openChannel } from '../../../navigation'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
@ -64,21 +64,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
const context = direct
|
||||
? await client.findOne(notification.class.DocNotifyContext, {
|
||||
user: myAccId,
|
||||
attachedTo: direct._id,
|
||||
attachedToClass: chunter.class.DirectMessage
|
||||
})
|
||||
: undefined
|
||||
|
||||
if (context !== undefined) {
|
||||
await client.diffUpdate(context, { hidden: false })
|
||||
await openChannel(context)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const dmId =
|
||||
direct?._id ??
|
||||
(await client.createDoc(chunter.class.DirectMessage, core.space.Space, {
|
||||
@ -89,14 +74,27 @@
|
||||
members: accIds
|
||||
}))
|
||||
|
||||
const notifyContextId = await client.createDoc(notification.class.DocNotifyContext, core.space.Space, {
|
||||
const context = await client.findOne(notification.class.DocNotifyContext, {
|
||||
user: myAccId,
|
||||
attachedTo: dmId,
|
||||
attachedToClass: chunter.class.DirectMessage
|
||||
})
|
||||
|
||||
if (context !== undefined) {
|
||||
await client.diffUpdate(context, { hidden: false })
|
||||
openChannel(dmId, chunter.class.DirectMessage)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
await client.createDoc(notification.class.DocNotifyContext, core.space.Space, {
|
||||
user: myAccId,
|
||||
attachedTo: dmId,
|
||||
attachedToClass: chunter.class.DirectMessage,
|
||||
hidden: false
|
||||
})
|
||||
|
||||
await openChannel(undefined, undefined, { _id: notifyContextId })
|
||||
openChannel(dmId, chunter.class.DirectMessage)
|
||||
}
|
||||
|
||||
function handleCancel (): void {
|
||||
|
@ -20,11 +20,12 @@
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import { Action } from '@hcengineering/ui'
|
||||
|
||||
import { ChatNavGroupModel } from '../types'
|
||||
import { ChatGroup, ChatNavGroupModel } from '../types'
|
||||
import ChatNavSection from './ChatNavSection.svelte'
|
||||
import chunter from '../../../plugin'
|
||||
|
||||
export let selectedContextId: Ref<DocNotifyContext> | undefined = undefined
|
||||
export let objectId: Ref<Doc> | undefined
|
||||
export let object: Doc | undefined
|
||||
export let model: ChatNavGroupModel
|
||||
|
||||
interface Section {
|
||||
@ -43,6 +44,8 @@
|
||||
let objectsByClass = new Map<Ref<Class<Doc>>, Doc[]>()
|
||||
let contexts: DocNotifyContext[] = []
|
||||
|
||||
let shouldPushObject = false
|
||||
|
||||
let sections: Section[] = []
|
||||
|
||||
$: contextsQuery.query(
|
||||
@ -63,10 +66,15 @@
|
||||
|
||||
$: loadObjects(contexts)
|
||||
|
||||
$: void getSections(objectsByClass, model).then((res) => {
|
||||
$: void getSections(objectsByClass, model, shouldPushObject ? object : undefined).then((res) => {
|
||||
sections = res
|
||||
})
|
||||
|
||||
$: shouldPushObject =
|
||||
object !== undefined &&
|
||||
getObjectGroup(object) === model.id &&
|
||||
!contexts.some(({ attachedTo }) => attachedTo === object?._id)
|
||||
|
||||
function loadObjects (contexts: DocNotifyContext[]): void {
|
||||
const contextsByClass = groupByArray(contexts, ({ attachedToClass }) => attachedToClass)
|
||||
|
||||
@ -92,9 +100,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
function getObjectGroup (object: Doc): ChatGroup {
|
||||
if (hierarchy.isDerived(object._class, chunter.class.Channel)) {
|
||||
return 'channels'
|
||||
}
|
||||
|
||||
if (hierarchy.isDerived(object._class, chunter.class.DirectMessage)) {
|
||||
return 'direct'
|
||||
}
|
||||
|
||||
return 'activity'
|
||||
}
|
||||
|
||||
async function getSections (
|
||||
objectsByClass: Map<Ref<Class<Doc>>, Doc[]>,
|
||||
model: ChatNavGroupModel
|
||||
model: ChatNavGroupModel,
|
||||
object: Doc | undefined
|
||||
): Promise<Section[]> {
|
||||
const result: Section[] = []
|
||||
|
||||
@ -108,13 +129,40 @@
|
||||
return result
|
||||
}
|
||||
|
||||
let isObjectPushed = false
|
||||
|
||||
if (
|
||||
Array.from(objectsByClass.values())
|
||||
.flat()
|
||||
.some((o) => o._id === object?._id)
|
||||
) {
|
||||
isObjectPushed = true
|
||||
}
|
||||
|
||||
for (const [_class, objects] of objectsByClass.entries()) {
|
||||
const clazz = hierarchy.getClass(_class)
|
||||
const sectionObjects = [...objects]
|
||||
|
||||
if (object && _class === object._class && !objects.some(({ _id }) => _id === object._id)) {
|
||||
isObjectPushed = true
|
||||
sectionObjects.push(object)
|
||||
}
|
||||
|
||||
result.push({
|
||||
id: _class,
|
||||
_class,
|
||||
objects,
|
||||
objects: sectionObjects,
|
||||
label: await translate(clazz.pluralLabel ?? clazz.label, {})
|
||||
})
|
||||
}
|
||||
|
||||
if (!isObjectPushed && object) {
|
||||
const clazz = hierarchy.getClass(object._class)
|
||||
|
||||
result.push({
|
||||
id: object._id,
|
||||
_class: object._class,
|
||||
objects: [object],
|
||||
label: await translate(clazz.pluralLabel ?? clazz.label, {})
|
||||
})
|
||||
}
|
||||
@ -141,7 +189,7 @@
|
||||
<ChatNavSection
|
||||
objects={section.objects}
|
||||
{contexts}
|
||||
{selectedContextId}
|
||||
{objectId}
|
||||
header={section.label}
|
||||
actions={getSectionActions(section, contexts)}
|
||||
sortFn={model.sortFn}
|
||||
|
@ -25,11 +25,14 @@
|
||||
isMentionNotification
|
||||
} from '@hcengineering/notification-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import view from '@hcengineering/view'
|
||||
import { Doc } from '@hcengineering/core'
|
||||
|
||||
import NavItem from './NavItem.svelte'
|
||||
import { ChatNavItemModel } from '../types'
|
||||
import { openChannel } from '../../../navigation'
|
||||
|
||||
export let context: DocNotifyContext
|
||||
export let context: DocNotifyContext | undefined
|
||||
export let item: ChatNavItemModel
|
||||
export let isSelected = false
|
||||
|
||||
@ -43,6 +46,9 @@
|
||||
let actions: Action[] = []
|
||||
|
||||
notificationClient.inboxNotificationsByContext.subscribe((res) => {
|
||||
if (context === undefined) {
|
||||
return
|
||||
}
|
||||
notifications = (res.get(context._id) ?? []).filter((n) => isMentionNotification(n) || isActivityNotification(n))
|
||||
})
|
||||
|
||||
@ -50,12 +56,25 @@
|
||||
notificationsCount = res
|
||||
})
|
||||
|
||||
$: void getChannelActions(context).then((res) => {
|
||||
$: void getChannelActions(context, item.object).then((res) => {
|
||||
actions = res
|
||||
})
|
||||
|
||||
async function getChannelActions (context: DocNotifyContext): Promise<Action[]> {
|
||||
const result = []
|
||||
async function getChannelActions (context: DocNotifyContext | undefined, object: Doc): Promise<Action[]> {
|
||||
const result: Action[] = []
|
||||
|
||||
if (context === undefined) {
|
||||
return []
|
||||
}
|
||||
|
||||
result.push({
|
||||
icon: view.icon.Open,
|
||||
label: view.string.Open,
|
||||
action: async () => {
|
||||
openChannel(object._id, object._class)
|
||||
}
|
||||
})
|
||||
|
||||
const excludedActions = [
|
||||
notification.action.DeleteContextNotifications,
|
||||
notification.action.UnReadNotifyContext,
|
||||
@ -100,6 +119,6 @@
|
||||
description={item.description}
|
||||
{actions}
|
||||
on:click={() => {
|
||||
dispatch('select', { doc: item.object, context })
|
||||
dispatch('select', { object: item.object })
|
||||
}}
|
||||
/>
|
||||
|
@ -33,7 +33,7 @@
|
||||
export let contexts: DocNotifyContext[]
|
||||
export let actions: Action[] = []
|
||||
export let maxItems: number | undefined = undefined
|
||||
export let selectedContextId: Ref<DocNotifyContext> | undefined = undefined
|
||||
export let objectId: Ref<Doc> | undefined
|
||||
export let sortFn: (items: ChatNavItemModel[], contexts: DocNotifyContext[]) => ChatNavItemModel[]
|
||||
|
||||
const client = getClient()
|
||||
@ -52,7 +52,7 @@
|
||||
|
||||
$: canShowMore = !!maxItems && items.length > maxItems
|
||||
|
||||
$: visibleItems = getVisibleItems(canShowMore, isShownMore, maxItems, items, selectedContextId, contexts)
|
||||
$: visibleItems = getVisibleItems(canShowMore, isShownMore, maxItems, items, objectId)
|
||||
|
||||
async function getChatNavItems (objects: Doc[]): Promise<ChatNavItemModel[]> {
|
||||
const items: ChatNavItemModel[] = []
|
||||
@ -98,8 +98,7 @@
|
||||
isShownMore: boolean,
|
||||
maxItems: number | undefined,
|
||||
items: ChatNavItemModel[],
|
||||
selectedContextId: Ref<DocNotifyContext> | undefined,
|
||||
contexts: DocNotifyContext[]
|
||||
selectedObjectId: Ref<Doc> | undefined
|
||||
): ChatNavItemModel[] {
|
||||
if (!canShowMore || isShownMore) {
|
||||
return items
|
||||
@ -107,23 +106,17 @@
|
||||
|
||||
const result = items.slice(0, maxItems)
|
||||
|
||||
if (selectedContextId === undefined) {
|
||||
if (selectedObjectId === undefined) {
|
||||
return result
|
||||
}
|
||||
|
||||
const context = contexts.find(({ _id }) => _id === selectedContextId)
|
||||
|
||||
if (context === undefined) {
|
||||
return result
|
||||
}
|
||||
|
||||
const exists = result.some(({ id }) => id === context.attachedTo)
|
||||
const exists = result.some(({ id }) => id === selectedObjectId)
|
||||
|
||||
if (exists) {
|
||||
return result
|
||||
}
|
||||
|
||||
const selectedItem = items.find(({ id }) => id === context?.attachedTo)
|
||||
const selectedItem = items.find(({ id }) => id === selectedObjectId)
|
||||
|
||||
if (selectedItem === undefined) {
|
||||
return result
|
||||
@ -148,9 +141,7 @@
|
||||
{#if !isCollapsed}
|
||||
{#each visibleItems as item (item.id)}
|
||||
{@const context = contexts.find(({ attachedTo }) => attachedTo === item.id)}
|
||||
{#if context}
|
||||
<ChatNavItem {context} isSelected={selectedContextId === context._id} {item} on:select />
|
||||
{/if}
|
||||
<ChatNavItem {context} isSelected={objectId === item.id} {item} on:select />
|
||||
{/each}
|
||||
{#if canShowMore}
|
||||
<div class="showMore">
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Doc, Ref } from '@hcengineering/core'
|
||||
import { Scroller, SearchEdit, Label, Button, IconAdd, showPopup, Menu } from '@hcengineering/ui'
|
||||
import { DocNotifyContext } from '@hcengineering/notification'
|
||||
import { SpecialNavModel } from '@hcengineering/workbench'
|
||||
@ -27,9 +27,10 @@
|
||||
import { chatNavGroupModels, chatSpecials } from '../utils'
|
||||
import ChatSpecialElement from './ChatSpecialElement.svelte'
|
||||
import { userSearch } from '../../../index'
|
||||
import { navigateToSpecial } from '../../../utils'
|
||||
import { navigateToSpecial } from '../../../navigation'
|
||||
|
||||
export let selectedContextId: Ref<DocNotifyContext> | undefined
|
||||
export let objectId: Ref<Doc> | undefined
|
||||
export let object: Doc | undefined
|
||||
export let currentSpecial: SpecialNavModel | undefined
|
||||
|
||||
const notificationClient = InboxNotificationsClientImpl.getClient()
|
||||
@ -105,7 +106,7 @@
|
||||
</div>
|
||||
<Scroller>
|
||||
{#each chatNavGroupModels as model}
|
||||
<ChatNavGroup {selectedContextId} {model} on:select />
|
||||
<ChatNavGroup {object} {objectId} {model} on:select />
|
||||
{/each}
|
||||
<div class="antiNav-space" />
|
||||
</Scroller>
|
||||
|
@ -24,24 +24,12 @@
|
||||
Space
|
||||
} from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
AnyComponent,
|
||||
Button,
|
||||
getCurrentResolvedLocation,
|
||||
Icon,
|
||||
Label,
|
||||
navigate,
|
||||
Scroller,
|
||||
SearchEdit,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import presentation, { createQuery } from '@hcengineering/presentation'
|
||||
import { AnyComponent, Button, Icon, Label, Scroller, SearchEdit, showPopup } from '@hcengineering/ui'
|
||||
import { FilterBar, FilterButton, SpacePresenter } from '@hcengineering/view-resources'
|
||||
import workbench from '@hcengineering/workbench'
|
||||
import { Channel } from '@hcengineering/chunter'
|
||||
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
||||
import { get } from 'svelte/store'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { openChannel } from '../../../navigation'
|
||||
|
||||
import { getObjectIcon, joinChannel, leaveChannel } from '../../../utils'
|
||||
import chunter from './../../../plugin'
|
||||
@ -54,12 +42,9 @@
|
||||
export let withFilterButton: boolean = true
|
||||
export let search: string = ''
|
||||
|
||||
const client = getClient()
|
||||
const me = getCurrentAccount()._id
|
||||
const channelsQuery = createQuery()
|
||||
|
||||
const notificationClient = InboxNotificationsClientImpl.getClient()
|
||||
|
||||
const sort: SortingQuery<Space> = {
|
||||
name: SortingOrder.Ascending
|
||||
}
|
||||
@ -114,28 +99,7 @@
|
||||
}
|
||||
|
||||
async function view (channel: Channel): Promise<void> {
|
||||
const loc = getCurrentResolvedLocation()
|
||||
const context = get(notificationClient.contextByDoc).get(channel._id)
|
||||
|
||||
let contextId = context?._id
|
||||
|
||||
if (contextId === undefined) {
|
||||
contextId = await client.createDoc(notification.class.DocNotifyContext, channel.space, {
|
||||
attachedToClass: channel._class,
|
||||
attachedTo: channel._id,
|
||||
user: me,
|
||||
hidden: false,
|
||||
lastViewedTimestamp: Date.now()
|
||||
})
|
||||
}
|
||||
|
||||
if (contextId === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
loc.path[3] = contextId
|
||||
|
||||
navigate(loc)
|
||||
openChannel(channel._id, channel._class)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -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 = ''
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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<DocNotifyContext>
|
||||
sortFn: (items: ChatNavItemModel[], contexts: DocNotifyContext[]) => ChatNavItemModel[]
|
||||
@ -37,3 +39,7 @@ export interface ChatNavItemModel {
|
||||
isSecondary: boolean
|
||||
withIconBackground: boolean
|
||||
}
|
||||
|
||||
export interface SelectChannelEvent {
|
||||
object: Doc
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<void> {
|
||||
})
|
||||
}
|
||||
|
||||
export async function openChannel (
|
||||
notifyContext?: DocNotifyContext,
|
||||
evt?: Event,
|
||||
props?: { _id: Ref<DocNotifyContext> }
|
||||
): Promise<void> {
|
||||
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<boolean> {
|
||||
@ -176,34 +141,7 @@ export async function deleteChatMessage (message: ChatMessage): Promise<void> {
|
||||
await client.remove(message)
|
||||
}
|
||||
|
||||
export async function replyToThread (message: ActivityMessage): Promise<void> {
|
||||
const loc = getCurrentLocation()
|
||||
const client = getClient()
|
||||
|
||||
const inboxClient = InboxNotificationsClientImpl.getClient()
|
||||
|
||||
let contextId: Ref<DocNotifyContext> | 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<Resources> => ({
|
||||
filter: {
|
||||
@ -215,7 +153,6 @@ export default async (): Promise<Resources> => ({
|
||||
ThreadParentPresenter,
|
||||
ThreadViewPanel,
|
||||
ChannelHeader,
|
||||
ChannelView,
|
||||
ChannelPanel,
|
||||
ChannelPresenter,
|
||||
DirectMessagePresenter,
|
||||
@ -262,7 +199,6 @@ export default async (): Promise<Resources> => ({
|
||||
UnarchiveChannel,
|
||||
ConvertDmToPrivateChannel,
|
||||
DeleteChatMessage: deleteChatMessage,
|
||||
OpenChannel: openChannel,
|
||||
LeaveChannel: leaveChannelAction,
|
||||
RemoveChannel: removeChannelAction
|
||||
}
|
||||
|
139
plugins/chunter-resources/src/navigation.ts
Normal file
139
plugins/chunter-resources/src/navigation.ts
Normal file
@ -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<Doc>, Ref<Class<Doc>>] {
|
||||
return decodeURIComponent(value).split('|') as [Ref<Doc>, Ref<Class<Doc>>]
|
||||
}
|
||||
|
||||
function encodeChannelURI (_id: Ref<Doc>, _class: Ref<Class<Doc>>): string {
|
||||
return encodeURIComponent([_id, _class].join('|'))
|
||||
}
|
||||
|
||||
export function openChannel (_id: Ref<Doc>, _class: Ref<Class<Doc>>): 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<void> {
|
||||
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<string> {
|
||||
const location = getCurrentResolvedLocation()
|
||||
|
||||
let threadParent = ''
|
||||
let _id: Ref<Doc>
|
||||
let _class: Ref<Class<Doc>>
|
||||
|
||||
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<Location> {
|
||||
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<Doc>,
|
||||
_class: Ref<Class<Doc>>,
|
||||
threadParent: Ref<ActivityMessage>
|
||||
): 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<Location> {
|
||||
const loc = getCurrentResolvedLocation()
|
||||
|
||||
return buildThreadLink(loc, doc.objectId, doc.objectClass, doc.attachedTo)
|
||||
}
|
||||
|
||||
export async function replyToThread (message: ActivityMessage): Promise<void> {
|
||||
const loc = getCurrentLocation()
|
||||
|
||||
if (loc.path[2] !== notificationId) {
|
||||
loc.path[2] = chunterId
|
||||
}
|
||||
|
||||
navigate(buildThreadLink(loc, message.attachedTo, message.attachedToClass, message._id))
|
||||
}
|
@ -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
|
||||
},
|
||||
|
@ -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<string> {
|
||||
if (space === undefined) {
|
||||
@ -205,40 +190,6 @@ export async function ChannelTitleProvider (client: Client, id: Ref<Channel>): P
|
||||
return channel.name
|
||||
}
|
||||
|
||||
export async function openMessageFromSpecial (message?: ActivityMessage): Promise<void> {
|
||||
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<string> {
|
||||
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<string> {
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
@ -281,24 +210,6 @@ export async function getTitle (doc: Doc): Promise<string> {
|
||||
return `${label}-${doc._id}`
|
||||
}
|
||||
|
||||
export async function chunterSpaceLinkFragmentProvider (doc: ChunterSpace): Promise<Location> {
|
||||
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<Class<Doc>>): 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<DocNotifyContext>, _id: Ref<ActivityMessage>): 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<Location> {
|
||||
const loc = getCurrentResolvedLocation()
|
||||
const client = getClient()
|
||||
const inboxClient = InboxNotificationsClientImpl.getClient()
|
||||
|
||||
let contextId: Ref<DocNotifyContext> | 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<Account> | Array<Ref<Account>>): Promise<void> {
|
||||
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
|
||||
}
|
||||
|
@ -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<Action>,
|
||||
OpenChannel: '' as Ref<Action>,
|
||||
LeaveChannel: '' as Ref<Action>,
|
||||
RemoveChannel: '' as Ref<Action>,
|
||||
CloseConversation: '' as Ref<Action>
|
||||
|
@ -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<void> {
|
||||
const loc = await resolveLocation(newLocation)
|
||||
const [_id] = decodeObjectURI(loc?.loc.path[3] ?? '')
|
||||
const context = $contextByDocStore.get(_id)
|
||||
|
||||
selectedContextId = loc?.loc.path[3] as Ref<DocNotifyContext> | undefined
|
||||
selectedContextId = context?._id
|
||||
|
||||
if (selectedContextId !== selectedContext?._id) {
|
||||
selectedContext = undefined
|
||||
@ -179,7 +183,8 @@
|
||||
const selectedMsg = selectedNotification.mentionedIn as Ref<ActivityMessage>
|
||||
|
||||
openInboxDoc(
|
||||
selectedContext._id,
|
||||
selectedContext.attachedTo,
|
||||
selectedContext.attachedToClass,
|
||||
isActivityMessageClass(selectedContext.attachedToClass)
|
||||
? (selectedContext.attachedTo as Ref<ActivityMessage>)
|
||||
: undefined,
|
||||
@ -192,20 +197,31 @@
|
||||
const thread = await client.findOne(chunter.class.ThreadMessage, {
|
||||
_id: selectedContext.attachedTo as Ref<ThreadMessage>
|
||||
})
|
||||
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<ActivityMessage>)
|
||||
openInboxDoc(
|
||||
selectedContext.attachedTo,
|
||||
selectedContext.attachedToClass,
|
||||
undefined,
|
||||
selectedContext.attachedTo as Ref<ActivityMessage>
|
||||
)
|
||||
} else {
|
||||
const selectedMsg = (selectedNotification as ActivityInboxNotification)?.attachedTo
|
||||
|
||||
openInboxDoc(
|
||||
selectedContext._id,
|
||||
selectedContext.attachedTo,
|
||||
selectedContext.attachedToClass,
|
||||
selectedMsg ? (selectedContext.attachedTo as Ref<ActivityMessage>) : undefined,
|
||||
selectedMsg ?? (selectedContext.attachedTo as Ref<ActivityMessage>)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
openInboxDoc(selectedContext._id, undefined, (selectedNotification as ActivityInboxNotification)?.attachedTo)
|
||||
openInboxDoc(
|
||||
selectedContext.attachedTo,
|
||||
selectedContext.attachedToClass,
|
||||
undefined,
|
||||
(selectedNotification as ActivityInboxNotification)?.attachedTo
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -409,9 +409,9 @@ export async function resolveLocation (loc: Location): Promise<ResolvedLocation
|
||||
return undefined
|
||||
}
|
||||
|
||||
const contextId = loc.path[3] as Ref<DocNotifyContext> | 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<ResolvedLocation
|
||||
}
|
||||
}
|
||||
|
||||
return await generateLocation(loc, contextId)
|
||||
return await generateLocation(loc, _id, _class)
|
||||
}
|
||||
|
||||
async function generateLocation (
|
||||
loc: Location,
|
||||
contextId: Ref<DocNotifyContext>
|
||||
_id: Ref<Doc>,
|
||||
_class: Ref<Class<Doc>>
|
||||
): Promise<ResolvedLocation | undefined> {
|
||||
const client = getClient()
|
||||
|
||||
@ -437,35 +438,18 @@ async function generateLocation (
|
||||
const workspace = loc.path[1] ?? ''
|
||||
const threadId = loc.path[4] as Ref<ActivityMessage> | 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<Doc>, Ref<Class<Doc>>] {
|
||||
return decodeURIComponent(value).split('|') as [Ref<Doc>, Ref<Class<Doc>>]
|
||||
}
|
||||
|
||||
function encodeObjectURI (_id: Ref<Doc>, _class: Ref<Class<Doc>>): string {
|
||||
return encodeURIComponent([_id, _class].join('|'))
|
||||
}
|
||||
|
||||
export function openInboxDoc (
|
||||
contextId?: Ref<DocNotifyContext>,
|
||||
_id?: Ref<Doc>,
|
||||
_class?: Ref<Class<Doc>>,
|
||||
thread?: Ref<ActivityMessage>,
|
||||
message?: Ref<ActivityMessage>
|
||||
): 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
|
||||
|
Loading…
Reference in New Issue
Block a user