mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-25 18:02:04 +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
|
encode: chunter.function.GetThreadLink
|
||||||
})
|
})
|
||||||
|
|
||||||
// Note: it is not working now, need to fix navigation by url UBERF-5686
|
createAction(
|
||||||
// createAction(
|
builder,
|
||||||
// builder,
|
{
|
||||||
// {
|
action: view.actionImpl.CopyTextToClipboard,
|
||||||
// action: view.actionImpl.CopyTextToClipboard,
|
actionProps: {
|
||||||
// actionProps: {
|
textProvider: chunter.function.GetLink
|
||||||
// textProvider: chunter.function.GetLink
|
},
|
||||||
// },
|
label: chunter.string.CopyLink,
|
||||||
// label: chunter.string.CopyLink,
|
icon: chunter.icon.Copy,
|
||||||
// icon: chunter.icon.Copy,
|
keyBinding: [],
|
||||||
// keyBinding: [],
|
input: 'none',
|
||||||
// input: 'none',
|
category: chunter.category.Chunter,
|
||||||
// category: chunter.category.Chunter,
|
target: activity.class.ActivityMessage,
|
||||||
// target: activity.class.ActivityMessage,
|
visibilityTester: chunter.function.CanCopyMessageLink,
|
||||||
// visibilityTester: chunter.function.CanCopyMessageLink,
|
context: {
|
||||||
// context: {
|
mode: ['context', 'browser'],
|
||||||
// mode: ['context', 'browser'],
|
application: chunter.app.Chunter,
|
||||||
// application: chunter.app.Chunter,
|
group: 'copy'
|
||||||
// group: 'copy'
|
}
|
||||||
// }
|
},
|
||||||
// },
|
chunter.action.CopyChatMessageLink
|
||||||
// chunter.action.CopyChatMessageLink
|
)
|
||||||
// )
|
|
||||||
|
|
||||||
builder.mixin(chunter.class.ChunterMessage, core.class.Class, view.mixin.ClassFilters, {
|
builder.mixin(chunter.class.ChunterMessage, core.class.Class, view.mixin.ClassFilters, {
|
||||||
filters: ['space', '_class']
|
filters: ['space', '_class']
|
||||||
@ -602,20 +601,6 @@ export function createModel (builder: Builder, options = { addApplication: true
|
|||||||
chunter.action.LeaveChannel
|
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, {
|
createAction(builder, {
|
||||||
...notificationActionTemplates.pinContext,
|
...notificationActionTemplates.pinContext,
|
||||||
label: chunter.string.StarChannel,
|
label: chunter.string.StarChannel,
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
import ChannelScrollView from './ChannelScrollView.svelte'
|
import ChannelScrollView from './ChannelScrollView.svelte'
|
||||||
import { ChannelDataProvider } from '../channelDataProvider'
|
import { ChannelDataProvider } from '../channelDataProvider'
|
||||||
|
|
||||||
export let context: DocNotifyContext
|
export let object: Doc
|
||||||
export let object: Doc | undefined
|
export let context: DocNotifyContext | undefined
|
||||||
export let filters: Ref<ActivityMessagesFilter>[] = []
|
export let filters: Ref<ActivityMessagesFilter>[] = []
|
||||||
export let isAsideOpened = false
|
export let isAsideOpened = false
|
||||||
|
|
||||||
@ -39,11 +39,11 @@
|
|||||||
selectedMessageId = getMessageFromLoc(newLocation)
|
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
|
$: _class = isDocChannel ? activity.class.ActivityMessage : chunter.class.ChatMessage
|
||||||
$: collection = isDocChannel ? 'comments' : 'messages'
|
$: collection = isDocChannel ? 'comments' : 'messages'
|
||||||
|
|
||||||
$: updateDataProvider(context.attachedTo, _class, context.lastViewedTimestamp, selectedMessageId)
|
$: updateDataProvider(object._id, _class, context?.lastViewedTimestamp, selectedMessageId)
|
||||||
|
|
||||||
function updateDataProvider (
|
function updateDataProvider (
|
||||||
attachedTo: Ref<Doc>,
|
attachedTo: Ref<Doc>,
|
||||||
@ -62,8 +62,8 @@
|
|||||||
|
|
||||||
{#if dataProvider}
|
{#if dataProvider}
|
||||||
<ChannelScrollView
|
<ChannelScrollView
|
||||||
objectId={context.attachedTo}
|
objectId={object._id}
|
||||||
objectClass={context.attachedToClass}
|
objectClass={object._class}
|
||||||
{object}
|
{object}
|
||||||
skipLabels={!isDocChannel}
|
skipLabels={!isDocChannel}
|
||||||
selectedFilters={filters}
|
selectedFilters={filters}
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if context}
|
{#if object}
|
||||||
<div class="antiComponent">
|
<div class="antiComponent">
|
||||||
<ChannelView {object} {context} embedded allowClose on:close />
|
<ChannelView {object} {context} embedded allowClose on:close />
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
import chunter from '../plugin'
|
import chunter from '../plugin'
|
||||||
import ChannelAside from './chat/ChannelAside.svelte'
|
import ChannelAside from './chat/ChannelAside.svelte'
|
||||||
|
|
||||||
export let context: DocNotifyContext
|
export let object: Doc
|
||||||
export let object: Doc | undefined = undefined
|
export let context: DocNotifyContext | undefined
|
||||||
export let allowClose = false
|
export let allowClose = false
|
||||||
export let embedded = false
|
export let embedded = false
|
||||||
|
|
||||||
@ -43,9 +43,8 @@
|
|||||||
isThreadOpened = newLocation.path[4] != null
|
isThreadOpened = newLocation.path[4] != null
|
||||||
})
|
})
|
||||||
|
|
||||||
$: isDocChat = !hierarchy.isDerived(context.attachedToClass, chunter.class.ChunterSpace)
|
$: isDocChat = !hierarchy.isDerived(object._class, chunter.class.ChunterSpace)
|
||||||
$: withAside =
|
$: withAside = !embedded && !isThreadOpened && !hierarchy.isDerived(object._class, chunter.class.DirectMessage)
|
||||||
!embedded && !isThreadOpened && !hierarchy.isDerived(context.attachedToClass, chunter.class.DirectMessage)
|
|
||||||
|
|
||||||
function toChannel (object?: Doc): Channel | undefined {
|
function toChannel (object?: Doc): Channel | undefined {
|
||||||
return object as Channel | undefined
|
return object as Channel | undefined
|
||||||
@ -56,8 +55,8 @@
|
|||||||
|
|
||||||
<div class="popupPanel panel" class:embedded>
|
<div class="popupPanel panel" class:embedded>
|
||||||
<ChannelHeader
|
<ChannelHeader
|
||||||
_id={context.attachedTo}
|
_id={object._id}
|
||||||
_class={context.attachedToClass}
|
_class={object._class}
|
||||||
{object}
|
{object}
|
||||||
{allowClose}
|
{allowClose}
|
||||||
{withAside}
|
{withAside}
|
||||||
@ -72,7 +71,7 @@
|
|||||||
|
|
||||||
<div class="popupPanel-body" class:asideShown={withAside && isAsideShown}>
|
<div class="popupPanel-body" class:asideShown={withAside && isAsideShown}>
|
||||||
<div class="popupPanel-body__main">
|
<div class="popupPanel-body__main">
|
||||||
{#key context._id}
|
{#key object._id}
|
||||||
<ChannelComponent {context} {object} {filters} isAsideOpened={(withAside && isAsideShown) || isThreadOpened} />
|
<ChannelComponent {context} {object} {filters} isAsideOpened={(withAside && isAsideShown) || isThreadOpened} />
|
||||||
{/key}
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
@ -82,10 +81,10 @@
|
|||||||
<div class="popupPanel-body__aside" class:float={false} class:shown={withAside && isAsideShown}>
|
<div class="popupPanel-body__aside" class:float={false} class:shown={withAside && isAsideShown}>
|
||||||
<Separator name="aside" float index={0} />
|
<Separator name="aside" float index={0} />
|
||||||
<div class="antiPanel-wrap__content">
|
<div class="antiPanel-wrap__content">
|
||||||
{#if hierarchy.isDerived(context.attachedToClass, chunter.class.Channel)}
|
{#if hierarchy.isDerived(object._class, chunter.class.Channel)}
|
||||||
<ChannelAside _class={context.attachedToClass} object={toChannel(object)} />
|
<ChannelAside _class={object._class} object={toChannel(object)} />
|
||||||
{:else}
|
{:else}
|
||||||
<DocAside _class={context.attachedToClass} {object} />
|
<DocAside _class={object._class} {object} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,9 +21,11 @@
|
|||||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import { SearchEdit } from '@hcengineering/ui'
|
import { SearchEdit } from '@hcengineering/ui'
|
||||||
import { openDoc } from '@hcengineering/view-resources'
|
import { openDoc } from '@hcengineering/view-resources'
|
||||||
|
|
||||||
import { userSearch } from '../index'
|
import { userSearch } from '../index'
|
||||||
import chunter from '../plugin'
|
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 spaceId: Ref<DirectMessage> | undefined
|
||||||
export let withSearch: boolean = true
|
export let withSearch: boolean = true
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
import { ActivityMessagesFilter } from '@hcengineering/activity'
|
import { ActivityMessagesFilter } from '@hcengineering/activity'
|
||||||
|
|
||||||
import { userSearch } from '../index'
|
import { userSearch } from '../index'
|
||||||
import { navigateToSpecial } from '../utils'
|
import { navigateToSpecial } from '../navigation'
|
||||||
import ChannelMessagesFilter from './ChannelMessagesFilter.svelte'
|
import ChannelMessagesFilter from './ChannelMessagesFilter.svelte'
|
||||||
|
|
||||||
export let object: Doc | undefined = undefined
|
export let object: Doc | undefined = undefined
|
||||||
|
@ -25,9 +25,8 @@
|
|||||||
} from '@hcengineering/notification'
|
} from '@hcengineering/notification'
|
||||||
import { getResource } from '@hcengineering/platform'
|
import { getResource } from '@hcengineering/platform'
|
||||||
import { Label, TimeSince, getLocation, navigate } from '@hcengineering/ui'
|
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 object: ActivityMessage
|
||||||
export let embedded = false
|
export let embedded = false
|
||||||
@ -71,14 +70,14 @@
|
|||||||
.some(({ isViewed }) => !isViewed)
|
.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)
|
displayPersons = Array.from(personIds)
|
||||||
.map((id) => personById.get(id))
|
.map((id) => personById.get(id))
|
||||||
.filter((person): person is Person => person !== undefined)
|
.filter((person): person is Person => person !== undefined)
|
||||||
.slice(0, maxDisplayPersons - 1)
|
.slice(0, maxDisplayPersons - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleReply (e: any) {
|
function handleReply (e: MouseEvent): void {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
@ -87,17 +86,7 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inboxClient === undefined) {
|
navigate(buildThreadLink(getLocation(), object.attachedTo, object.attachedToClass, object._id))
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const context = get(inboxClient.contextByDoc).get(object.attachedTo)
|
|
||||||
|
|
||||||
if (context === undefined) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
navigate(buildThreadLink(getLocation(), context._id, object._id))
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Doc, IdMap, Ref } from '@hcengineering/core'
|
import { Doc, Ref, Class } from '@hcengineering/core'
|
||||||
import { createQuery } from '@hcengineering/presentation'
|
import { createQuery } from '@hcengineering/presentation'
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
@ -24,7 +24,6 @@
|
|||||||
Separator,
|
Separator,
|
||||||
Location
|
Location
|
||||||
} from '@hcengineering/ui'
|
} from '@hcengineering/ui'
|
||||||
import { DocNotifyContext } from '@hcengineering/notification'
|
|
||||||
|
|
||||||
import { NavigatorModel, SpecialNavModel } from '@hcengineering/workbench'
|
import { NavigatorModel, SpecialNavModel } from '@hcengineering/workbench'
|
||||||
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
||||||
@ -33,13 +32,15 @@
|
|||||||
import ChatNavigator from './navigator/ChatNavigator.svelte'
|
import ChatNavigator from './navigator/ChatNavigator.svelte'
|
||||||
import ChannelView from '../ChannelView.svelte'
|
import ChannelView from '../ChannelView.svelte'
|
||||||
import { chatSpecials, loadSavedAttachments } from './utils'
|
import { chatSpecials, loadSavedAttachments } from './utils'
|
||||||
|
import { SelectChannelEvent } from './types'
|
||||||
|
import { decodeChannelURI, openChannel } from '../../navigation'
|
||||||
|
|
||||||
export let visibleNav: boolean = true
|
export let visibleNav: boolean = true
|
||||||
export let navFloat: boolean = false
|
export let navFloat: boolean = false
|
||||||
export let appsDirection: 'vertical' | 'horizontal' = 'horizontal'
|
export let appsDirection: 'vertical' | 'horizontal' = 'horizontal'
|
||||||
|
|
||||||
const notificationsClient = InboxNotificationsClientImpl.getClient()
|
const notificationsClient = InboxNotificationsClientImpl.getClient()
|
||||||
const contextByIdStore = notificationsClient.contextById
|
const contextByDocStore = notificationsClient.contextByDoc
|
||||||
const objectQuery = createQuery()
|
const objectQuery = createQuery()
|
||||||
|
|
||||||
const navigatorModel: NavigatorModel = {
|
const navigatorModel: NavigatorModel = {
|
||||||
@ -47,8 +48,7 @@
|
|||||||
specials: chatSpecials
|
specials: chatSpecials
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectedContextId: Ref<DocNotifyContext> | undefined = undefined
|
let selectedData: { _id: Ref<Doc>, _class: Ref<Class<Doc>> } | undefined = undefined
|
||||||
let selectedContext: DocNotifyContext | undefined = undefined
|
|
||||||
|
|
||||||
let currentSpecial: SpecialNavModel | undefined
|
let currentSpecial: SpecialNavModel | undefined
|
||||||
|
|
||||||
@ -58,17 +58,24 @@
|
|||||||
syncLocation(loc)
|
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(
|
objectQuery.query(
|
||||||
selectedContext.attachedToClass,
|
_class,
|
||||||
{ _id: selectedContext.attachedTo },
|
{ _id },
|
||||||
(res) => {
|
(res) => {
|
||||||
object = res[0]
|
object = res[0]
|
||||||
},
|
},
|
||||||
{ limit: 1 }
|
{ limit: 1 }
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function syncLocation (loc: Location) {
|
function syncLocation (loc: Location) {
|
||||||
const specialId = loc.path[3]
|
const specialId = loc.path[3]
|
||||||
@ -76,39 +83,24 @@
|
|||||||
currentSpecial = navigatorModel?.specials?.find((special) => special.id === specialId)
|
currentSpecial = navigatorModel?.specials?.find((special) => special.id === specialId)
|
||||||
|
|
||||||
if (currentSpecial !== undefined) {
|
if (currentSpecial !== undefined) {
|
||||||
selectedContext = undefined
|
selectedData = undefined
|
||||||
selectedContextId = undefined
|
|
||||||
} else {
|
} 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>) {
|
function handleChannelSelected (event: CustomEvent): void {
|
||||||
if (selectedContextId === undefined) {
|
const detail = (event.detail ?? {}) as SelectChannelEvent
|
||||||
selectedContext = undefined
|
|
||||||
} else {
|
|
||||||
selectedContext = contexts.get(selectedContextId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleChannelSelected (event: CustomEvent) {
|
selectedData = { _id: detail.object._id, _class: detail.object._class }
|
||||||
const { context } = event.detail ?? {}
|
|
||||||
|
|
||||||
selectedContext = context
|
if (selectedData._id !== object?._id) {
|
||||||
selectedContextId = selectedContext?._id
|
object = detail.object
|
||||||
|
|
||||||
if (selectedContext?.attachedTo !== object?._id) {
|
|
||||||
object = undefined
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const loc = getCurrentLocation()
|
openChannel(selectedData._id, selectedData._class)
|
||||||
|
|
||||||
loc.path[3] = selectedContextId as string
|
|
||||||
loc.path[4] = ''
|
|
||||||
loc.query = { ...loc.query, message: null }
|
|
||||||
loc.path.length = 4
|
|
||||||
|
|
||||||
navigate(loc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defineSeparators('chat', [
|
defineSeparators('chat', [
|
||||||
@ -127,7 +119,7 @@
|
|||||||
class="antiPanel-navigator {appsDirection === 'horizontal' ? 'portrait' : 'landscape'} background-surface-color"
|
class="antiPanel-navigator {appsDirection === 'horizontal' ? 'portrait' : 'landscape'} background-surface-color"
|
||||||
>
|
>
|
||||||
<div class="antiPanel-wrap__content">
|
<div class="antiPanel-wrap__content">
|
||||||
<ChatNavigator {selectedContextId} {currentSpecial} on:select={handleChannelSelected} />
|
<ChatNavigator objectId={selectedData?._id} {object} {currentSpecial} on:select={handleChannelSelected} />
|
||||||
</div>
|
</div>
|
||||||
<Separator name="chat" float={navFloat ? 'navigator' : true} index={0} />
|
<Separator name="chat" float={navFloat ? 'navigator' : true} index={0} />
|
||||||
</div>
|
</div>
|
||||||
@ -153,8 +145,9 @@
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{:else if selectedContext}
|
{:else if object}
|
||||||
<ChannelView context={selectedContext} {object} />
|
{@const context = $contextByDocStore.get(object._id)}
|
||||||
|
<ChannelView {object} {context} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,6 +43,6 @@
|
|||||||
|
|
||||||
{#if threadId}
|
{#if threadId}
|
||||||
<ThreadView _id={threadId} on:close />
|
<ThreadView _id={threadId} on:close />
|
||||||
{:else if context}
|
{:else if object}
|
||||||
<ChannelView {object} {context} allowClose embedded on:close />
|
<ChannelView {object} {context} allowClose embedded on:close />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
import Lock from '../../icons/Lock.svelte'
|
import Lock from '../../icons/Lock.svelte'
|
||||||
import chunter from '../../../plugin'
|
import chunter from '../../../plugin'
|
||||||
import { openChannel } from '../../../index'
|
import { openChannel } from '../../../navigation'
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
@ -60,14 +60,14 @@
|
|||||||
members: [accountId],
|
members: [accountId],
|
||||||
topic: description
|
topic: description
|
||||||
})
|
})
|
||||||
const notifyContextId = await client.createDoc(notification.class.DocNotifyContext, core.space.Space, {
|
await client.createDoc(notification.class.DocNotifyContext, core.space.Space, {
|
||||||
user: accountId,
|
user: accountId,
|
||||||
attachedTo: channelId,
|
attachedTo: channelId,
|
||||||
attachedToClass: chunter.class.Channel,
|
attachedToClass: chunter.class.Channel,
|
||||||
hidden: false
|
hidden: false
|
||||||
})
|
})
|
||||||
|
|
||||||
await openChannel(undefined, undefined, { _id: notifyContextId })
|
openChannel(channelId, chunter.class.Channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCancel (): void {
|
function handleCancel (): void {
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
import chunter from '../../../plugin'
|
import chunter from '../../../plugin'
|
||||||
import { buildDmName } from '../../../utils'
|
import { buildDmName } from '../../../utils'
|
||||||
import ChannelMembers from '../../ChannelMembers.svelte'
|
import ChannelMembers from '../../ChannelMembers.svelte'
|
||||||
import { openChannel } from '../../../index'
|
import { openChannel } from '../../../navigation'
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const client = getClient()
|
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 =
|
const dmId =
|
||||||
direct?._id ??
|
direct?._id ??
|
||||||
(await client.createDoc(chunter.class.DirectMessage, core.space.Space, {
|
(await client.createDoc(chunter.class.DirectMessage, core.space.Space, {
|
||||||
@ -89,14 +74,27 @@
|
|||||||
members: accIds
|
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,
|
user: myAccId,
|
||||||
attachedTo: dmId,
|
attachedTo: dmId,
|
||||||
attachedToClass: chunter.class.DirectMessage,
|
attachedToClass: chunter.class.DirectMessage,
|
||||||
hidden: false
|
hidden: false
|
||||||
})
|
})
|
||||||
|
|
||||||
await openChannel(undefined, undefined, { _id: notifyContextId })
|
openChannel(dmId, chunter.class.DirectMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCancel (): void {
|
function handleCancel (): void {
|
||||||
|
@ -20,11 +20,12 @@
|
|||||||
import { translate } from '@hcengineering/platform'
|
import { translate } from '@hcengineering/platform'
|
||||||
import { Action } from '@hcengineering/ui'
|
import { Action } from '@hcengineering/ui'
|
||||||
|
|
||||||
import { ChatNavGroupModel } from '../types'
|
import { ChatGroup, ChatNavGroupModel } from '../types'
|
||||||
import ChatNavSection from './ChatNavSection.svelte'
|
import ChatNavSection from './ChatNavSection.svelte'
|
||||||
import chunter from '../../../plugin'
|
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
|
export let model: ChatNavGroupModel
|
||||||
|
|
||||||
interface Section {
|
interface Section {
|
||||||
@ -43,6 +44,8 @@
|
|||||||
let objectsByClass = new Map<Ref<Class<Doc>>, Doc[]>()
|
let objectsByClass = new Map<Ref<Class<Doc>>, Doc[]>()
|
||||||
let contexts: DocNotifyContext[] = []
|
let contexts: DocNotifyContext[] = []
|
||||||
|
|
||||||
|
let shouldPushObject = false
|
||||||
|
|
||||||
let sections: Section[] = []
|
let sections: Section[] = []
|
||||||
|
|
||||||
$: contextsQuery.query(
|
$: contextsQuery.query(
|
||||||
@ -63,10 +66,15 @@
|
|||||||
|
|
||||||
$: loadObjects(contexts)
|
$: loadObjects(contexts)
|
||||||
|
|
||||||
$: void getSections(objectsByClass, model).then((res) => {
|
$: void getSections(objectsByClass, model, shouldPushObject ? object : undefined).then((res) => {
|
||||||
sections = res
|
sections = res
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$: shouldPushObject =
|
||||||
|
object !== undefined &&
|
||||||
|
getObjectGroup(object) === model.id &&
|
||||||
|
!contexts.some(({ attachedTo }) => attachedTo === object?._id)
|
||||||
|
|
||||||
function loadObjects (contexts: DocNotifyContext[]): void {
|
function loadObjects (contexts: DocNotifyContext[]): void {
|
||||||
const contextsByClass = groupByArray(contexts, ({ attachedToClass }) => attachedToClass)
|
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 (
|
async function getSections (
|
||||||
objectsByClass: Map<Ref<Class<Doc>>, Doc[]>,
|
objectsByClass: Map<Ref<Class<Doc>>, Doc[]>,
|
||||||
model: ChatNavGroupModel
|
model: ChatNavGroupModel,
|
||||||
|
object: Doc | undefined
|
||||||
): Promise<Section[]> {
|
): Promise<Section[]> {
|
||||||
const result: Section[] = []
|
const result: Section[] = []
|
||||||
|
|
||||||
@ -108,13 +129,40 @@
|
|||||||
return result
|
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()) {
|
for (const [_class, objects] of objectsByClass.entries()) {
|
||||||
const clazz = hierarchy.getClass(_class)
|
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({
|
result.push({
|
||||||
id: _class,
|
id: _class,
|
||||||
_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, {})
|
label: await translate(clazz.pluralLabel ?? clazz.label, {})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -141,7 +189,7 @@
|
|||||||
<ChatNavSection
|
<ChatNavSection
|
||||||
objects={section.objects}
|
objects={section.objects}
|
||||||
{contexts}
|
{contexts}
|
||||||
{selectedContextId}
|
{objectId}
|
||||||
header={section.label}
|
header={section.label}
|
||||||
actions={getSectionActions(section, contexts)}
|
actions={getSectionActions(section, contexts)}
|
||||||
sortFn={model.sortFn}
|
sortFn={model.sortFn}
|
||||||
|
@ -25,11 +25,14 @@
|
|||||||
isMentionNotification
|
isMentionNotification
|
||||||
} from '@hcengineering/notification-resources'
|
} from '@hcengineering/notification-resources'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import view from '@hcengineering/view'
|
||||||
|
import { Doc } from '@hcengineering/core'
|
||||||
|
|
||||||
import NavItem from './NavItem.svelte'
|
import NavItem from './NavItem.svelte'
|
||||||
import { ChatNavItemModel } from '../types'
|
import { ChatNavItemModel } from '../types'
|
||||||
|
import { openChannel } from '../../../navigation'
|
||||||
|
|
||||||
export let context: DocNotifyContext
|
export let context: DocNotifyContext | undefined
|
||||||
export let item: ChatNavItemModel
|
export let item: ChatNavItemModel
|
||||||
export let isSelected = false
|
export let isSelected = false
|
||||||
|
|
||||||
@ -43,6 +46,9 @@
|
|||||||
let actions: Action[] = []
|
let actions: Action[] = []
|
||||||
|
|
||||||
notificationClient.inboxNotificationsByContext.subscribe((res) => {
|
notificationClient.inboxNotificationsByContext.subscribe((res) => {
|
||||||
|
if (context === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
notifications = (res.get(context._id) ?? []).filter((n) => isMentionNotification(n) || isActivityNotification(n))
|
notifications = (res.get(context._id) ?? []).filter((n) => isMentionNotification(n) || isActivityNotification(n))
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -50,12 +56,25 @@
|
|||||||
notificationsCount = res
|
notificationsCount = res
|
||||||
})
|
})
|
||||||
|
|
||||||
$: void getChannelActions(context).then((res) => {
|
$: void getChannelActions(context, item.object).then((res) => {
|
||||||
actions = res
|
actions = res
|
||||||
})
|
})
|
||||||
|
|
||||||
async function getChannelActions (context: DocNotifyContext): Promise<Action[]> {
|
async function getChannelActions (context: DocNotifyContext | undefined, object: Doc): Promise<Action[]> {
|
||||||
const result = []
|
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 = [
|
const excludedActions = [
|
||||||
notification.action.DeleteContextNotifications,
|
notification.action.DeleteContextNotifications,
|
||||||
notification.action.UnReadNotifyContext,
|
notification.action.UnReadNotifyContext,
|
||||||
@ -100,6 +119,6 @@
|
|||||||
description={item.description}
|
description={item.description}
|
||||||
{actions}
|
{actions}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
dispatch('select', { doc: item.object, context })
|
dispatch('select', { object: item.object })
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
export let contexts: DocNotifyContext[]
|
export let contexts: DocNotifyContext[]
|
||||||
export let actions: Action[] = []
|
export let actions: Action[] = []
|
||||||
export let maxItems: number | undefined = undefined
|
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[]
|
export let sortFn: (items: ChatNavItemModel[], contexts: DocNotifyContext[]) => ChatNavItemModel[]
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
@ -52,7 +52,7 @@
|
|||||||
|
|
||||||
$: canShowMore = !!maxItems && items.length > maxItems
|
$: 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[]> {
|
async function getChatNavItems (objects: Doc[]): Promise<ChatNavItemModel[]> {
|
||||||
const items: ChatNavItemModel[] = []
|
const items: ChatNavItemModel[] = []
|
||||||
@ -98,8 +98,7 @@
|
|||||||
isShownMore: boolean,
|
isShownMore: boolean,
|
||||||
maxItems: number | undefined,
|
maxItems: number | undefined,
|
||||||
items: ChatNavItemModel[],
|
items: ChatNavItemModel[],
|
||||||
selectedContextId: Ref<DocNotifyContext> | undefined,
|
selectedObjectId: Ref<Doc> | undefined
|
||||||
contexts: DocNotifyContext[]
|
|
||||||
): ChatNavItemModel[] {
|
): ChatNavItemModel[] {
|
||||||
if (!canShowMore || isShownMore) {
|
if (!canShowMore || isShownMore) {
|
||||||
return items
|
return items
|
||||||
@ -107,23 +106,17 @@
|
|||||||
|
|
||||||
const result = items.slice(0, maxItems)
|
const result = items.slice(0, maxItems)
|
||||||
|
|
||||||
if (selectedContextId === undefined) {
|
if (selectedObjectId === undefined) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = contexts.find(({ _id }) => _id === selectedContextId)
|
const exists = result.some(({ id }) => id === selectedObjectId)
|
||||||
|
|
||||||
if (context === undefined) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
const exists = result.some(({ id }) => id === context.attachedTo)
|
|
||||||
|
|
||||||
if (exists) {
|
if (exists) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedItem = items.find(({ id }) => id === context?.attachedTo)
|
const selectedItem = items.find(({ id }) => id === selectedObjectId)
|
||||||
|
|
||||||
if (selectedItem === undefined) {
|
if (selectedItem === undefined) {
|
||||||
return result
|
return result
|
||||||
@ -148,9 +141,7 @@
|
|||||||
{#if !isCollapsed}
|
{#if !isCollapsed}
|
||||||
{#each visibleItems as item (item.id)}
|
{#each visibleItems as item (item.id)}
|
||||||
{@const context = contexts.find(({ attachedTo }) => attachedTo === item.id)}
|
{@const context = contexts.find(({ attachedTo }) => attachedTo === item.id)}
|
||||||
{#if context}
|
<ChatNavItem {context} isSelected={objectId === item.id} {item} on:select />
|
||||||
<ChatNavItem {context} isSelected={selectedContextId === context._id} {item} on:select />
|
|
||||||
{/if}
|
|
||||||
{/each}
|
{/each}
|
||||||
{#if canShowMore}
|
{#if canShowMore}
|
||||||
<div class="showMore">
|
<div class="showMore">
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<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 { Scroller, SearchEdit, Label, Button, IconAdd, showPopup, Menu } from '@hcengineering/ui'
|
||||||
import { DocNotifyContext } from '@hcengineering/notification'
|
import { DocNotifyContext } from '@hcengineering/notification'
|
||||||
import { SpecialNavModel } from '@hcengineering/workbench'
|
import { SpecialNavModel } from '@hcengineering/workbench'
|
||||||
@ -27,9 +27,10 @@
|
|||||||
import { chatNavGroupModels, chatSpecials } from '../utils'
|
import { chatNavGroupModels, chatSpecials } from '../utils'
|
||||||
import ChatSpecialElement from './ChatSpecialElement.svelte'
|
import ChatSpecialElement from './ChatSpecialElement.svelte'
|
||||||
import { userSearch } from '../../../index'
|
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
|
export let currentSpecial: SpecialNavModel | undefined
|
||||||
|
|
||||||
const notificationClient = InboxNotificationsClientImpl.getClient()
|
const notificationClient = InboxNotificationsClientImpl.getClient()
|
||||||
@ -105,7 +106,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<Scroller>
|
<Scroller>
|
||||||
{#each chatNavGroupModels as model}
|
{#each chatNavGroupModels as model}
|
||||||
<ChatNavGroup {selectedContextId} {model} on:select />
|
<ChatNavGroup {object} {objectId} {model} on:select />
|
||||||
{/each}
|
{/each}
|
||||||
<div class="antiNav-space" />
|
<div class="antiNav-space" />
|
||||||
</Scroller>
|
</Scroller>
|
||||||
|
@ -24,24 +24,12 @@
|
|||||||
Space
|
Space
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { IntlString } from '@hcengineering/platform'
|
import { IntlString } from '@hcengineering/platform'
|
||||||
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
|
import presentation, { createQuery } from '@hcengineering/presentation'
|
||||||
import {
|
import { AnyComponent, Button, Icon, Label, Scroller, SearchEdit, showPopup } from '@hcengineering/ui'
|
||||||
AnyComponent,
|
|
||||||
Button,
|
|
||||||
getCurrentResolvedLocation,
|
|
||||||
Icon,
|
|
||||||
Label,
|
|
||||||
navigate,
|
|
||||||
Scroller,
|
|
||||||
SearchEdit,
|
|
||||||
showPopup
|
|
||||||
} from '@hcengineering/ui'
|
|
||||||
import { FilterBar, FilterButton, SpacePresenter } from '@hcengineering/view-resources'
|
import { FilterBar, FilterButton, SpacePresenter } from '@hcengineering/view-resources'
|
||||||
import workbench from '@hcengineering/workbench'
|
import workbench from '@hcengineering/workbench'
|
||||||
import { Channel } from '@hcengineering/chunter'
|
import { Channel } from '@hcengineering/chunter'
|
||||||
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
import { openChannel } from '../../../navigation'
|
||||||
import { get } from 'svelte/store'
|
|
||||||
import notification from '@hcengineering/notification'
|
|
||||||
|
|
||||||
import { getObjectIcon, joinChannel, leaveChannel } from '../../../utils'
|
import { getObjectIcon, joinChannel, leaveChannel } from '../../../utils'
|
||||||
import chunter from './../../../plugin'
|
import chunter from './../../../plugin'
|
||||||
@ -54,12 +42,9 @@
|
|||||||
export let withFilterButton: boolean = true
|
export let withFilterButton: boolean = true
|
||||||
export let search: string = ''
|
export let search: string = ''
|
||||||
|
|
||||||
const client = getClient()
|
|
||||||
const me = getCurrentAccount()._id
|
const me = getCurrentAccount()._id
|
||||||
const channelsQuery = createQuery()
|
const channelsQuery = createQuery()
|
||||||
|
|
||||||
const notificationClient = InboxNotificationsClientImpl.getClient()
|
|
||||||
|
|
||||||
const sort: SortingQuery<Space> = {
|
const sort: SortingQuery<Space> = {
|
||||||
name: SortingOrder.Ascending
|
name: SortingOrder.Ascending
|
||||||
}
|
}
|
||||||
@ -114,28 +99,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function view (channel: Channel): Promise<void> {
|
async function view (channel: Channel): Promise<void> {
|
||||||
const loc = getCurrentResolvedLocation()
|
openChannel(channel._id, channel._class)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
import { ActivityMessagePresenter } from '@hcengineering/activity-resources'
|
import { ActivityMessagePresenter } from '@hcengineering/activity-resources'
|
||||||
|
|
||||||
import plugin from '../../../plugin'
|
import plugin from '../../../plugin'
|
||||||
import { openMessageFromSpecial } from '../../../utils'
|
import { openMessageFromSpecial } from '../../../navigation'
|
||||||
|
|
||||||
export let withHeader: boolean = true
|
export let withHeader: boolean = true
|
||||||
export let search: string = ''
|
export let search: string = ''
|
||||||
|
@ -24,9 +24,9 @@
|
|||||||
import { ActivityMessagePresenter, savedMessagesStore } from '@hcengineering/activity-resources'
|
import { ActivityMessagePresenter, savedMessagesStore } from '@hcengineering/activity-resources'
|
||||||
|
|
||||||
import chunter from '../../../plugin'
|
import chunter from '../../../plugin'
|
||||||
import { openMessageFromSpecial } from '../../../utils'
|
|
||||||
import { savedAttachmentsStore } from '../utils'
|
import { savedAttachmentsStore } from '../utils'
|
||||||
import Header from '../../Header.svelte'
|
import Header from '../../Header.svelte'
|
||||||
|
import { openMessageFromSpecial } from '../../../navigation'
|
||||||
|
|
||||||
const client = getClient()
|
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 DocNotifyContext } from '@hcengineering/notification'
|
||||||
import { type AnySvelteComponent, type IconSize, type Action } from '@hcengineering/ui'
|
import { type AnySvelteComponent, type IconSize, type Action } from '@hcengineering/ui'
|
||||||
|
|
||||||
|
export type ChatGroup = 'activity' | 'direct' | 'channels' | 'starred'
|
||||||
|
|
||||||
export interface ChatNavGroupModel {
|
export interface ChatNavGroupModel {
|
||||||
id: string
|
id: ChatGroup
|
||||||
label?: IntlString
|
label?: IntlString
|
||||||
query: DocumentQuery<DocNotifyContext>
|
query: DocumentQuery<DocNotifyContext>
|
||||||
sortFn: (items: ChatNavItemModel[], contexts: DocNotifyContext[]) => ChatNavItemModel[]
|
sortFn: (items: ChatNavItemModel[], contexts: DocNotifyContext[]) => ChatNavItemModel[]
|
||||||
@ -37,3 +39,7 @@ export interface ChatNavItemModel {
|
|||||||
isSecondary: boolean
|
isSecondary: boolean
|
||||||
withIconBackground: boolean
|
withIconBackground: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SelectChannelEvent {
|
||||||
|
object: Doc
|
||||||
|
}
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
import chunter from '../../plugin'
|
import chunter from '../../plugin'
|
||||||
import Header from '../Header.svelte'
|
import Header from '../Header.svelte'
|
||||||
import { openMessageFromSpecial } from '../../utils'
|
import { openMessageFromSpecial } from '../../navigation'
|
||||||
|
|
||||||
const threadsQuery = createQuery()
|
const threadsQuery = createQuery()
|
||||||
const me = getCurrentAccount() as PersonAccount
|
const me = getCurrentAccount() as PersonAccount
|
||||||
|
@ -13,17 +13,14 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { get, writable } from 'svelte/store'
|
import { writable } from 'svelte/store'
|
||||||
import chunter, { type Channel, type ChatMessage, chunterId, type DirectMessage } from '@hcengineering/chunter'
|
import chunter, { type Channel, type ChatMessage, type DirectMessage } from '@hcengineering/chunter'
|
||||||
import { getCurrentAccount, type Ref } from '@hcengineering/core'
|
|
||||||
import { type Resources } from '@hcengineering/platform'
|
import { type Resources } from '@hcengineering/platform'
|
||||||
import { MessageBox, getClient } from '@hcengineering/presentation'
|
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 { type ActivityMessage } from '@hcengineering/activity'
|
||||||
import notification, { type DocNotifyContext, notificationId } from '@hcengineering/notification'
|
|
||||||
|
|
||||||
import ChannelPresenter from './components/ChannelPresenter.svelte'
|
import ChannelPresenter from './components/ChannelPresenter.svelte'
|
||||||
import ChannelView from './components/ChannelView.svelte'
|
|
||||||
import ChannelPanel from './components/ChannelPanel.svelte'
|
import ChannelPanel from './components/ChannelPanel.svelte'
|
||||||
import ChunterBrowser from './components/chat/specials/ChunterBrowser.svelte'
|
import ChunterBrowser from './components/chat/specials/ChunterBrowser.svelte'
|
||||||
import ConvertDmToPrivateChannelModal from './components/ConvertDmToPrivateChannel.svelte'
|
import ConvertDmToPrivateChannelModal from './components/ConvertDmToPrivateChannel.svelte'
|
||||||
@ -60,19 +57,15 @@ import {
|
|||||||
ChannelTitleProvider,
|
ChannelTitleProvider,
|
||||||
DirectTitleProvider,
|
DirectTitleProvider,
|
||||||
canDeleteMessage,
|
canDeleteMessage,
|
||||||
chunterSpaceLinkFragmentProvider,
|
|
||||||
dmIdentifierProvider,
|
dmIdentifierProvider,
|
||||||
getDmName,
|
getDmName,
|
||||||
getMessageLink,
|
|
||||||
getTitle,
|
getTitle,
|
||||||
getUnreadThreadsCount,
|
getUnreadThreadsCount,
|
||||||
canCopyMessageLink,
|
canCopyMessageLink,
|
||||||
buildThreadLink,
|
|
||||||
getThreadLink,
|
|
||||||
leaveChannelAction,
|
leaveChannelAction,
|
||||||
removeChannelAction
|
removeChannelAction
|
||||||
} from './utils'
|
} 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 ChatMessagesPresenter } from './components/chat-message/ChatMessagesPresenter.svelte'
|
||||||
export { default as ChatMessagePopup } from './components/chat-message/ChatMessagePopup.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 const userSearch = writable('')
|
||||||
|
|
||||||
export async function chunterBrowserVisible (): Promise<boolean> {
|
export async function chunterBrowserVisible (): Promise<boolean> {
|
||||||
@ -176,34 +141,7 @@ export async function deleteChatMessage (message: ChatMessage): Promise<void> {
|
|||||||
await client.remove(message)
|
await client.remove(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function replyToThread (message: ActivityMessage): Promise<void> {
|
export { replyToThread } from './navigation'
|
||||||
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 default async (): Promise<Resources> => ({
|
export default async (): Promise<Resources> => ({
|
||||||
filter: {
|
filter: {
|
||||||
@ -215,7 +153,6 @@ export default async (): Promise<Resources> => ({
|
|||||||
ThreadParentPresenter,
|
ThreadParentPresenter,
|
||||||
ThreadViewPanel,
|
ThreadViewPanel,
|
||||||
ChannelHeader,
|
ChannelHeader,
|
||||||
ChannelView,
|
|
||||||
ChannelPanel,
|
ChannelPanel,
|
||||||
ChannelPresenter,
|
ChannelPresenter,
|
||||||
DirectMessagePresenter,
|
DirectMessagePresenter,
|
||||||
@ -262,7 +199,6 @@ export default async (): Promise<Resources> => ({
|
|||||||
UnarchiveChannel,
|
UnarchiveChannel,
|
||||||
ConvertDmToPrivateChannel,
|
ConvertDmToPrivateChannel,
|
||||||
DeleteChatMessage: deleteChatMessage,
|
DeleteChatMessage: deleteChatMessage,
|
||||||
OpenChannel: openChannel,
|
|
||||||
LeaveChannel: leaveChannelAction,
|
LeaveChannel: leaveChannelAction,
|
||||||
RemoveChannel: removeChannelAction
|
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,
|
UnsubscribeMessage: '' as ViewAction,
|
||||||
SubscribeComment: '' as ViewAction,
|
SubscribeComment: '' as ViewAction,
|
||||||
UnsubscribeComment: '' as ViewAction,
|
UnsubscribeComment: '' as ViewAction,
|
||||||
OpenChannel: '' as ViewAction,
|
|
||||||
LeaveChannel: '' as ViewAction,
|
LeaveChannel: '' as ViewAction,
|
||||||
RemoveChannel: '' as ViewAction
|
RemoveChannel: '' as ViewAction
|
||||||
},
|
},
|
||||||
|
@ -12,14 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
import {
|
import { type Channel, type ChatMessage, type DirectMessage, type ThreadMessage } from '@hcengineering/chunter'
|
||||||
type Channel,
|
|
||||||
type ChatMessage,
|
|
||||||
chunterId,
|
|
||||||
type ChunterSpace,
|
|
||||||
type DirectMessage,
|
|
||||||
type ThreadMessage
|
|
||||||
} from '@hcengineering/chunter'
|
|
||||||
import contact, { type Employee, type PersonAccount, getName, type Person } from '@hcengineering/contact'
|
import contact, { type Employee, type PersonAccount, getName, type Person } from '@hcengineering/contact'
|
||||||
import { employeeByIdStore, PersonIcon } from '@hcengineering/contact-resources'
|
import { employeeByIdStore, PersonIcon } from '@hcengineering/contact-resources'
|
||||||
import {
|
import {
|
||||||
@ -35,14 +28,7 @@ import {
|
|||||||
generateId
|
generateId
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import {
|
import { type AnySvelteComponent } from '@hcengineering/ui'
|
||||||
type AnySvelteComponent,
|
|
||||||
getCurrentResolvedLocation,
|
|
||||||
getLocation,
|
|
||||||
type Location,
|
|
||||||
navigate
|
|
||||||
} from '@hcengineering/ui'
|
|
||||||
import { workbenchId } from '@hcengineering/workbench'
|
|
||||||
import { type Asset, getResource, translate } from '@hcengineering/platform'
|
import { type Asset, getResource, translate } from '@hcengineering/platform'
|
||||||
import { classIcon, getDocLinkTitle, getDocTitle } from '@hcengineering/view-resources'
|
import { classIcon, getDocLinkTitle, getDocTitle } from '@hcengineering/view-resources'
|
||||||
import activity, {
|
import activity, {
|
||||||
@ -57,13 +43,12 @@ import {
|
|||||||
InboxNotificationsClientImpl,
|
InboxNotificationsClientImpl,
|
||||||
isMentionNotification
|
isMentionNotification
|
||||||
} from '@hcengineering/notification-resources'
|
} 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 { get, type Unsubscriber } from 'svelte/store'
|
||||||
|
|
||||||
import chunter from './plugin'
|
import chunter from './plugin'
|
||||||
import DirectIcon from './components/DirectIcon.svelte'
|
import DirectIcon from './components/DirectIcon.svelte'
|
||||||
import ChannelIcon from './components/ChannelIcon.svelte'
|
import ChannelIcon from './components/ChannelIcon.svelte'
|
||||||
import { chatSpecials } from './components/chat/utils'
|
|
||||||
|
|
||||||
export async function getDmName (client: Client, space?: Space): Promise<string> {
|
export async function getDmName (client: Client, space?: Space): Promise<string> {
|
||||||
if (space === undefined) {
|
if (space === undefined) {
|
||||||
@ -205,40 +190,6 @@ export async function ChannelTitleProvider (client: Client, id: Ref<Channel>): P
|
|||||||
return channel.name
|
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 {
|
export enum SearchType {
|
||||||
Messages,
|
Messages,
|
||||||
Channels,
|
Channels,
|
||||||
@ -246,28 +197,6 @@ export enum SearchType {
|
|||||||
Contacts
|
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> {
|
export async function getTitle (doc: Doc): Promise<string> {
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
@ -281,24 +210,6 @@ export async function getTitle (doc: Doc): Promise<string> {
|
|||||||
return `${label}-${doc._id}`
|
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 {
|
export function getObjectIcon (_class: Ref<Class<Doc>>): Asset | AnySvelteComponent | undefined {
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
@ -387,54 +298,6 @@ export async function filterChatMessages (
|
|||||||
return messages.filter((message) => filtersFns.some((filterFn) => filterFn(message, objectClass)))
|
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> {
|
export async function joinChannel (channel: Channel, value: Ref<Account> | Array<Ref<Account>>): Promise<void> {
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
|
||||||
@ -533,3 +396,7 @@ export async function removeChannelAction (context?: DocNotifyContext): Promise<
|
|||||||
await deleteContextNotifications(context)
|
await deleteContextNotifications(context)
|
||||||
await client.remove(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: {
|
component: {
|
||||||
DmHeader: '' as AnyComponent,
|
DmHeader: '' as AnyComponent,
|
||||||
ChannelView: '' as AnyComponent,
|
|
||||||
ThreadView: '' as AnyComponent,
|
ThreadView: '' as AnyComponent,
|
||||||
Thread: '' as AnyComponent,
|
Thread: '' as AnyComponent,
|
||||||
Reactions: '' as AnyComponent,
|
Reactions: '' as AnyComponent,
|
||||||
@ -211,7 +210,6 @@ export default plugin(chunterId, {
|
|||||||
},
|
},
|
||||||
action: {
|
action: {
|
||||||
DeleteChatMessage: '' as Ref<Action>,
|
DeleteChatMessage: '' as Ref<Action>,
|
||||||
OpenChannel: '' as Ref<Action>,
|
|
||||||
LeaveChannel: '' as Ref<Action>,
|
LeaveChannel: '' as Ref<Action>,
|
||||||
RemoveChannel: '' as Ref<Action>,
|
RemoveChannel: '' as Ref<Action>,
|
||||||
CloseConversation: '' as Ref<Action>
|
CloseConversation: '' as Ref<Action>
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
import Filter from '../Filter.svelte'
|
import Filter from '../Filter.svelte'
|
||||||
import {
|
import {
|
||||||
archiveAll,
|
archiveAll,
|
||||||
|
decodeObjectURI,
|
||||||
getDisplayInboxData,
|
getDisplayInboxData,
|
||||||
isMentionNotification,
|
isMentionNotification,
|
||||||
openInboxDoc,
|
openInboxDoc,
|
||||||
@ -65,6 +66,7 @@
|
|||||||
const inboxClient = InboxNotificationsClientImpl.getClient()
|
const inboxClient = InboxNotificationsClientImpl.getClient()
|
||||||
const notificationsByContextStore = inboxClient.inboxNotificationsByContext
|
const notificationsByContextStore = inboxClient.inboxNotificationsByContext
|
||||||
const contextByIdStore = inboxClient.contextById
|
const contextByIdStore = inboxClient.contextById
|
||||||
|
const contextByDocStore = inboxClient.contextByDoc
|
||||||
const contextsStore = inboxClient.contexts
|
const contextsStore = inboxClient.contexts
|
||||||
|
|
||||||
const allTab: TabItem = {
|
const allTab: TabItem = {
|
||||||
@ -98,8 +100,10 @@
|
|||||||
|
|
||||||
async function syncLocation (newLocation: Location): Promise<void> {
|
async function syncLocation (newLocation: Location): Promise<void> {
|
||||||
const loc = await resolveLocation(newLocation)
|
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) {
|
if (selectedContextId !== selectedContext?._id) {
|
||||||
selectedContext = undefined
|
selectedContext = undefined
|
||||||
@ -179,7 +183,8 @@
|
|||||||
const selectedMsg = selectedNotification.mentionedIn as Ref<ActivityMessage>
|
const selectedMsg = selectedNotification.mentionedIn as Ref<ActivityMessage>
|
||||||
|
|
||||||
openInboxDoc(
|
openInboxDoc(
|
||||||
selectedContext._id,
|
selectedContext.attachedTo,
|
||||||
|
selectedContext.attachedToClass,
|
||||||
isActivityMessageClass(selectedContext.attachedToClass)
|
isActivityMessageClass(selectedContext.attachedToClass)
|
||||||
? (selectedContext.attachedTo as Ref<ActivityMessage>)
|
? (selectedContext.attachedTo as Ref<ActivityMessage>)
|
||||||
: undefined,
|
: undefined,
|
||||||
@ -192,20 +197,31 @@
|
|||||||
const thread = await client.findOne(chunter.class.ThreadMessage, {
|
const thread = await client.findOne(chunter.class.ThreadMessage, {
|
||||||
_id: selectedContext.attachedTo as Ref<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)) {
|
} 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 {
|
} else {
|
||||||
const selectedMsg = (selectedNotification as ActivityInboxNotification)?.attachedTo
|
const selectedMsg = (selectedNotification as ActivityInboxNotification)?.attachedTo
|
||||||
|
|
||||||
openInboxDoc(
|
openInboxDoc(
|
||||||
selectedContext._id,
|
selectedContext.attachedTo,
|
||||||
|
selectedContext.attachedToClass,
|
||||||
selectedMsg ? (selectedContext.attachedTo as Ref<ActivityMessage>) : undefined,
|
selectedMsg ? (selectedContext.attachedTo as Ref<ActivityMessage>) : undefined,
|
||||||
selectedMsg ?? (selectedContext.attachedTo as Ref<ActivityMessage>)
|
selectedMsg ?? (selectedContext.attachedTo as Ref<ActivityMessage>)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
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 {
|
return {
|
||||||
loc: {
|
loc: {
|
||||||
path: [loc.path[0], loc.path[1], notificationId],
|
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 (
|
async function generateLocation (
|
||||||
loc: Location,
|
loc: Location,
|
||||||
contextId: Ref<DocNotifyContext>
|
_id: Ref<Doc>,
|
||||||
|
_class: Ref<Class<Doc>>
|
||||||
): Promise<ResolvedLocation | undefined> {
|
): Promise<ResolvedLocation | undefined> {
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
|
||||||
@ -437,35 +438,18 @@ async function generateLocation (
|
|||||||
const workspace = loc.path[1] ?? ''
|
const workspace = loc.path[1] ?? ''
|
||||||
const threadId = loc.path[4] as Ref<ActivityMessage> | undefined
|
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 =
|
const thread =
|
||||||
threadId !== undefined ? await client.findOne(activity.class.ActivityMessage, { _id: threadId }) : undefined
|
threadId !== undefined ? await client.findOne(activity.class.ActivityMessage, { _id: threadId }) : undefined
|
||||||
|
|
||||||
if (thread === undefined) {
|
if (thread === undefined) {
|
||||||
return {
|
return {
|
||||||
loc: {
|
loc: {
|
||||||
path: [appComponent, workspace, notificationId, contextId],
|
path: [appComponent, workspace, notificationId, encodeObjectURI(_id, _class)],
|
||||||
fragment: undefined,
|
fragment: undefined,
|
||||||
query: { ...loc.query }
|
query: { ...loc.query }
|
||||||
},
|
},
|
||||||
defaultLocation: {
|
defaultLocation: {
|
||||||
path: [appComponent, workspace, notificationId, contextId],
|
path: [appComponent, workspace, notificationId, encodeObjectURI(_id, _class)],
|
||||||
fragment: undefined,
|
fragment: undefined,
|
||||||
query: { ...loc.query }
|
query: { ...loc.query }
|
||||||
}
|
}
|
||||||
@ -474,20 +458,29 @@ async function generateLocation (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
loc: {
|
loc: {
|
||||||
path: [appComponent, workspace, notificationId, contextId, threadId as string],
|
path: [appComponent, workspace, notificationId, encodeObjectURI(_id, _class), threadId as string],
|
||||||
fragment: undefined,
|
fragment: undefined,
|
||||||
query: { ...loc.query }
|
query: { ...loc.query }
|
||||||
},
|
},
|
||||||
defaultLocation: {
|
defaultLocation: {
|
||||||
path: [appComponent, workspace, notificationId, contextId, threadId as string],
|
path: [appComponent, workspace, notificationId, encodeObjectURI(_id, _class), threadId as string],
|
||||||
fragment: undefined,
|
fragment: undefined,
|
||||||
query: { ...loc.query }
|
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 (
|
export function openInboxDoc (
|
||||||
contextId?: Ref<DocNotifyContext>,
|
_id?: Ref<Doc>,
|
||||||
|
_class?: Ref<Class<Doc>>,
|
||||||
thread?: Ref<ActivityMessage>,
|
thread?: Ref<ActivityMessage>,
|
||||||
message?: Ref<ActivityMessage>
|
message?: Ref<ActivityMessage>
|
||||||
): void {
|
): void {
|
||||||
@ -497,14 +490,14 @@ export function openInboxDoc (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contextId === undefined) {
|
if (_id === undefined || _class === undefined) {
|
||||||
loc.query = { ...loc.query, message: null }
|
loc.query = { message: null }
|
||||||
loc.path.length = 3
|
loc.path.length = 3
|
||||||
navigate(loc)
|
navigate(loc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
loc.path[3] = contextId
|
loc.path[3] = encodeObjectURI(_id, _class)
|
||||||
|
|
||||||
if (thread !== undefined) {
|
if (thread !== undefined) {
|
||||||
loc.path[4] = thread
|
loc.path[4] = thread
|
||||||
|
Loading…
Reference in New Issue
Block a user