mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-27 21:31:33 +03:00
UBERF-7239: Support short/custom links in inbox/chat/planner (#5815)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
61a0833ec4
commit
1e9fea356e
@ -301,6 +301,11 @@ function defineDocument (builder: Builder): void {
|
|||||||
encode: document.function.GetObjectLinkFragment
|
encode: document.function.GetObjectLinkFragment
|
||||||
})
|
})
|
||||||
|
|
||||||
|
builder.mixin(document.class.Document, core.class.Class, view.mixin.LinkIdProvider, {
|
||||||
|
encode: document.function.GetDocumentLinkId,
|
||||||
|
decode: document.function.ParseDocumentId
|
||||||
|
})
|
||||||
|
|
||||||
builder.mixin(document.class.Document, core.class.Class, view.mixin.ObjectIcon, {
|
builder.mixin(document.class.Document, core.class.Class, view.mixin.ObjectIcon, {
|
||||||
component: document.component.DocumentIcon
|
component: document.component.DocumentIcon
|
||||||
})
|
})
|
||||||
|
@ -932,6 +932,26 @@ export function createModel (builder: Builder): void {
|
|||||||
encode: recruit.function.GetIdObjectLinkFragment
|
encode: recruit.function.GetIdObjectLinkFragment
|
||||||
})
|
})
|
||||||
|
|
||||||
|
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.LinkIdProvider, {
|
||||||
|
encode: recruit.function.IdProvider,
|
||||||
|
decode: recruit.function.ParseLinkId
|
||||||
|
})
|
||||||
|
|
||||||
|
builder.mixin(recruit.class.Opinion, core.class.Class, view.mixin.LinkIdProvider, {
|
||||||
|
encode: recruit.function.IdProvider,
|
||||||
|
decode: recruit.function.ParseLinkId
|
||||||
|
})
|
||||||
|
|
||||||
|
builder.mixin(recruit.class.Review, core.class.Class, view.mixin.LinkIdProvider, {
|
||||||
|
encode: recruit.function.IdProvider,
|
||||||
|
decode: recruit.function.ParseLinkId
|
||||||
|
})
|
||||||
|
|
||||||
|
builder.mixin(recruit.class.Vacancy, core.class.Class, view.mixin.LinkIdProvider, {
|
||||||
|
encode: recruit.function.IdProvider,
|
||||||
|
decode: recruit.function.ParseLinkId
|
||||||
|
})
|
||||||
|
|
||||||
builder.createDoc(
|
builder.createDoc(
|
||||||
view.class.ActionCategory,
|
view.class.ActionCategory,
|
||||||
core.space.Model,
|
core.space.Model,
|
||||||
|
@ -58,7 +58,8 @@ export default mergeIds(recruitId, recruit, {
|
|||||||
GetTalentId: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
GetTalentId: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
||||||
HideDoneState: '' as ViewQueryAction,
|
HideDoneState: '' as ViewQueryAction,
|
||||||
HideArchivedVacancies: '' as ViewQueryAction,
|
HideArchivedVacancies: '' as ViewQueryAction,
|
||||||
ApplicantHasEmail: '' as Resource<ViewActionAvailabilityFunction>
|
ApplicantHasEmail: '' as Resource<ViewActionAvailabilityFunction>,
|
||||||
|
ParseLinkId: '' as Resource<(id: string) => Promise<Ref<Doc> | undefined>>
|
||||||
},
|
},
|
||||||
string: {
|
string: {
|
||||||
ApplicationsShort: '' as IntlString,
|
ApplicationsShort: '' as IntlString,
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
"@hcengineering/server-notification": "^0.6.1",
|
"@hcengineering/server-notification": "^0.6.1",
|
||||||
"@hcengineering/model-core": "^0.6.0",
|
"@hcengineering/model-core": "^0.6.0",
|
||||||
"@hcengineering/document": "^0.6.0",
|
"@hcengineering/document": "^0.6.0",
|
||||||
"@hcengineering/server-document": "^0.6.0"
|
"@hcengineering/server-document": "^0.6.0",
|
||||||
|
"@hcengineering/server-view": "^0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import document from '@hcengineering/document'
|
|||||||
import serverCore from '@hcengineering/server-core'
|
import serverCore from '@hcengineering/server-core'
|
||||||
import serverDocument from '@hcengineering/server-document'
|
import serverDocument from '@hcengineering/server-document'
|
||||||
import serverNotification from '@hcengineering/server-notification'
|
import serverNotification from '@hcengineering/server-notification'
|
||||||
|
import serverView from '@hcengineering/server-view'
|
||||||
|
|
||||||
export { serverDocumentId } from '@hcengineering/server-document'
|
export { serverDocumentId } from '@hcengineering/server-document'
|
||||||
|
|
||||||
@ -22,6 +23,10 @@ export function createModel (builder: Builder): void {
|
|||||||
presenter: serverDocument.function.DocumentTextPresenter
|
presenter: serverDocument.function.DocumentTextPresenter
|
||||||
})
|
})
|
||||||
|
|
||||||
|
builder.mixin(document.class.Document, core.class.Class, serverView.mixin.ServerLinkIdProvider, {
|
||||||
|
encode: serverDocument.function.DocumentLinkIdProvider
|
||||||
|
})
|
||||||
|
|
||||||
builder.mixin(document.class.Document, core.class.Class, serverCore.mixin.SearchPresenter, {
|
builder.mixin(document.class.Document, core.class.Class, serverCore.mixin.SearchPresenter, {
|
||||||
searchConfig: {
|
searchConfig: {
|
||||||
iconConfig: {
|
iconConfig: {
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
"@hcengineering/server-core": "^0.6.1",
|
"@hcengineering/server-core": "^0.6.1",
|
||||||
"@hcengineering/model-recruit": "^0.6.0",
|
"@hcengineering/model-recruit": "^0.6.0",
|
||||||
"@hcengineering/notification": "^0.6.23",
|
"@hcengineering/notification": "^0.6.23",
|
||||||
"@hcengineering/server-notification": "^0.6.1"
|
"@hcengineering/server-notification": "^0.6.1",
|
||||||
|
"@hcengineering/server-view": "^0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import serverNotification from '@hcengineering/server-notification'
|
|||||||
import serverRecruit from '@hcengineering/server-recruit'
|
import serverRecruit from '@hcengineering/server-recruit'
|
||||||
import serverContact from '@hcengineering/server-contact'
|
import serverContact from '@hcengineering/server-contact'
|
||||||
import contact from '@hcengineering/contact'
|
import contact from '@hcengineering/contact'
|
||||||
|
import serverView from '@hcengineering/server-view'
|
||||||
|
|
||||||
export { serverRecruitId } from '@hcengineering/server-recruit'
|
export { serverRecruitId } from '@hcengineering/server-recruit'
|
||||||
|
|
||||||
@ -43,6 +44,22 @@ export function createModel (builder: Builder): void {
|
|||||||
presenter: serverRecruit.function.VacancyTextPresenter
|
presenter: serverRecruit.function.VacancyTextPresenter
|
||||||
})
|
})
|
||||||
|
|
||||||
|
builder.mixin(recruit.class.Applicant, core.class.Class, serverView.mixin.ServerLinkIdProvider, {
|
||||||
|
encode: serverRecruit.function.LinkIdProvider
|
||||||
|
})
|
||||||
|
|
||||||
|
builder.mixin(recruit.class.Opinion, core.class.Class, serverView.mixin.ServerLinkIdProvider, {
|
||||||
|
encode: serverRecruit.function.LinkIdProvider
|
||||||
|
})
|
||||||
|
|
||||||
|
builder.mixin(recruit.class.Review, core.class.Class, serverView.mixin.ServerLinkIdProvider, {
|
||||||
|
encode: serverRecruit.function.LinkIdProvider
|
||||||
|
})
|
||||||
|
|
||||||
|
builder.mixin(recruit.class.Vacancy, core.class.Class, serverView.mixin.ServerLinkIdProvider, {
|
||||||
|
encode: serverRecruit.function.LinkIdProvider
|
||||||
|
})
|
||||||
|
|
||||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||||
trigger: serverRecruit.trigger.OnRecruitUpdate
|
trigger: serverRecruit.trigger.OnRecruitUpdate
|
||||||
})
|
})
|
||||||
|
@ -36,6 +36,8 @@
|
|||||||
"@hcengineering/server-notification": "^0.6.1",
|
"@hcengineering/server-notification": "^0.6.1",
|
||||||
"@hcengineering/model-tracker": "^0.6.0",
|
"@hcengineering/model-tracker": "^0.6.0",
|
||||||
"@hcengineering/server-tracker": "^0.6.0",
|
"@hcengineering/server-tracker": "^0.6.0",
|
||||||
"@hcengineering/contact": "^0.6.24"
|
"@hcengineering/contact": "^0.6.24",
|
||||||
|
"@hcengineering/model-core": "^0.6.0",
|
||||||
|
"@hcengineering/server-view": "^0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import serverCore from '@hcengineering/server-core'
|
|||||||
import serverNotification from '@hcengineering/server-notification'
|
import serverNotification from '@hcengineering/server-notification'
|
||||||
import serverTracker from '@hcengineering/server-tracker'
|
import serverTracker from '@hcengineering/server-tracker'
|
||||||
import contact from '@hcengineering/contact'
|
import contact from '@hcengineering/contact'
|
||||||
|
import serverView from '@hcengineering/server-view'
|
||||||
|
|
||||||
export { serverTrackerId } from '@hcengineering/server-tracker'
|
export { serverTrackerId } from '@hcengineering/server-tracker'
|
||||||
|
|
||||||
@ -37,6 +38,10 @@ export function createModel (builder: Builder): void {
|
|||||||
presenter: serverTracker.function.IssueNotificationContentProvider
|
presenter: serverTracker.function.IssueNotificationContentProvider
|
||||||
})
|
})
|
||||||
|
|
||||||
|
builder.mixin(tracker.class.Issue, core.class.Class, serverView.mixin.ServerLinkIdProvider, {
|
||||||
|
encode: serverTracker.function.IssueLinkIdProvider
|
||||||
|
})
|
||||||
|
|
||||||
builder.mixin(tracker.class.Issue, core.class.Class, serverCore.mixin.SearchPresenter, {
|
builder.mixin(tracker.class.Issue, core.class.Class, serverCore.mixin.SearchPresenter, {
|
||||||
searchConfig: {
|
searchConfig: {
|
||||||
iconConfig: {
|
iconConfig: {
|
||||||
|
@ -30,8 +30,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hcengineering/core": "^0.6.32",
|
"@hcengineering/core": "^0.6.32",
|
||||||
"@hcengineering/model": "^0.6.11",
|
"@hcengineering/model": "^0.6.11",
|
||||||
|
"@hcengineering/model-core": "^0.6.0",
|
||||||
"@hcengineering/platform": "^0.6.11",
|
"@hcengineering/platform": "^0.6.11",
|
||||||
"@hcengineering/server-view": "^0.6.0",
|
"@hcengineering/server-core": "^0.6.1",
|
||||||
"@hcengineering/server-core": "^0.6.1"
|
"@hcengineering/server-view": "^0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,14 +13,23 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import core from '@hcengineering/core'
|
import core, { type Doc } from '@hcengineering/core'
|
||||||
import { type Builder } from '@hcengineering/model'
|
import { type Builder, Mixin } from '@hcengineering/model'
|
||||||
import serverCore from '@hcengineering/server-core'
|
import serverCore, { type TriggerControl } from '@hcengineering/server-core'
|
||||||
import serverView from '@hcengineering/server-view'
|
import serverView, { type ServerLinkIdProvider } from '@hcengineering/server-view'
|
||||||
|
import { TClass } from '@hcengineering/model-core'
|
||||||
|
import { type Resource } from '@hcengineering/platform'
|
||||||
|
|
||||||
export { serverViewId } from '@hcengineering/server-view'
|
export { serverViewId } from '@hcengineering/server-view'
|
||||||
|
|
||||||
|
@Mixin(serverView.mixin.ServerLinkIdProvider, core.class.Class)
|
||||||
|
export class TServerLinkIdProvider extends TClass implements ServerLinkIdProvider {
|
||||||
|
encode!: Resource<(doc: Doc, control: TriggerControl) => Promise<string>>
|
||||||
|
}
|
||||||
|
|
||||||
export function createModel (builder: Builder): void {
|
export function createModel (builder: Builder): void {
|
||||||
|
builder.createModel(TServerLinkIdProvider)
|
||||||
|
|
||||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||||
trigger: serverView.trigger.OnCustomAttributeRemove
|
trigger: serverView.trigger.OnCustomAttributeRemove
|
||||||
})
|
})
|
||||||
|
@ -438,6 +438,11 @@ export function createModel (builder: Builder): void {
|
|||||||
builder.mixin(tracker.class.Component, core.class.Class, activity.mixin.ActivityDoc, {})
|
builder.mixin(tracker.class.Component, core.class.Class, activity.mixin.ActivityDoc, {})
|
||||||
builder.mixin(tracker.class.IssueTemplate, core.class.Class, activity.mixin.ActivityDoc, {})
|
builder.mixin(tracker.class.IssueTemplate, core.class.Class, activity.mixin.ActivityDoc, {})
|
||||||
|
|
||||||
|
builder.mixin(tracker.class.Issue, core.class.Class, view.mixin.LinkIdProvider, {
|
||||||
|
encode: tracker.function.GetIssueId,
|
||||||
|
decode: tracker.function.GetIssueIdByIdentifier
|
||||||
|
})
|
||||||
|
|
||||||
builder.createDoc(activity.class.ActivityMessageControl, core.space.Model, {
|
builder.createDoc(activity.class.ActivityMessageControl, core.space.Model, {
|
||||||
objectClass: tracker.class.Issue,
|
objectClass: tracker.class.Issue,
|
||||||
skip: [
|
skip: [
|
||||||
|
@ -90,7 +90,8 @@ import {
|
|||||||
type ObjectIcon,
|
type ObjectIcon,
|
||||||
type ObjectTooltip,
|
type ObjectTooltip,
|
||||||
type AttrPresenter,
|
type AttrPresenter,
|
||||||
type AttributeCategory
|
type AttributeCategory,
|
||||||
|
type LinkIdProvider
|
||||||
} from '@hcengineering/view'
|
} from '@hcengineering/view'
|
||||||
|
|
||||||
import view from './plugin'
|
import view from './plugin'
|
||||||
@ -352,6 +353,12 @@ export class TLinkProvider extends TClass implements LinkProvider {
|
|||||||
encode!: Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>
|
encode!: Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Mixin(view.mixin.LinkIdProvider, core.class.Class)
|
||||||
|
export class TLinkIdProvider extends TClass implements LinkIdProvider {
|
||||||
|
encode!: Resource<(doc: Doc) => Promise<string>>
|
||||||
|
decode!: Resource<(id: string) => Promise<Ref<Doc> | undefined>>
|
||||||
|
}
|
||||||
|
|
||||||
@Mixin(view.mixin.ObjectPanel, core.class.Class)
|
@Mixin(view.mixin.ObjectPanel, core.class.Class)
|
||||||
export class TObjectPanel extends TClass implements ObjectPanel {
|
export class TObjectPanel extends TClass implements ObjectPanel {
|
||||||
component!: AnyComponent
|
component!: AnyComponent
|
||||||
@ -450,7 +457,8 @@ export function createModel (builder: Builder): void {
|
|||||||
TObjectIdentifier,
|
TObjectIdentifier,
|
||||||
TObjectTooltip,
|
TObjectTooltip,
|
||||||
TObjectIcon,
|
TObjectIcon,
|
||||||
TAttrPresenter
|
TAttrPresenter,
|
||||||
|
TLinkIdProvider
|
||||||
)
|
)
|
||||||
|
|
||||||
classPresenter(
|
classPresenter(
|
||||||
|
@ -53,12 +53,12 @@ export function openPanel (
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function closePanel (shoulRedirect: boolean = true): void {
|
export function closePanel (shouldRedirect: boolean = true): void {
|
||||||
currentLocation = undefined
|
currentLocation = undefined
|
||||||
panelstore.update(() => {
|
panelstore.update(() => {
|
||||||
return { panel: undefined }
|
return { panel: undefined }
|
||||||
})
|
})
|
||||||
if (shoulRedirect) {
|
if (shouldRedirect) {
|
||||||
const loc = getLocation()
|
const loc = getLocation()
|
||||||
loc.fragment = undefined
|
loc.fragment = undefined
|
||||||
navigate(loc)
|
navigate(loc)
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Doc, Ref, Class } from '@hcengineering/core'
|
import { Doc, Ref, Class } from '@hcengineering/core'
|
||||||
import { createQuery } from '@hcengineering/presentation'
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
defineSeparators,
|
defineSeparators,
|
||||||
@ -26,29 +26,32 @@
|
|||||||
restoreLocation,
|
restoreLocation,
|
||||||
deviceOptionsStore as deviceInfo
|
deviceOptionsStore as deviceInfo
|
||||||
} from '@hcengineering/ui'
|
} from '@hcengineering/ui'
|
||||||
|
|
||||||
import { NavigatorModel, SpecialNavModel } from '@hcengineering/workbench'
|
import { NavigatorModel, SpecialNavModel } from '@hcengineering/workbench'
|
||||||
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import { chunterId } from '@hcengineering/chunter'
|
import { chunterId } from '@hcengineering/chunter'
|
||||||
import { ActivityMessage } from '@hcengineering/activity'
|
import view, { decodeObjectURI } from '@hcengineering/view'
|
||||||
|
import { parseLinkId, getObjectLinkId } from '@hcengineering/view-resources'
|
||||||
|
|
||||||
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 { SelectChannelEvent } from './types'
|
||||||
import { decodeChannelURI, openChannel } from '../../navigation'
|
import { openChannel } from '../../navigation'
|
||||||
|
|
||||||
const notificationsClient = InboxNotificationsClientImpl.getClient()
|
const notificationsClient = InboxNotificationsClientImpl.getClient()
|
||||||
const contextByDocStore = notificationsClient.contextByDoc
|
const contextByDocStore = notificationsClient.contextByDoc
|
||||||
const objectQuery = createQuery()
|
const objectQuery = createQuery()
|
||||||
|
const client = getClient()
|
||||||
|
|
||||||
const navigatorModel: NavigatorModel = {
|
const navigatorModel: NavigatorModel = {
|
||||||
spaces: [],
|
spaces: [],
|
||||||
specials: chatSpecials
|
specials: chatSpecials
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectedData: { _id: Ref<Doc>, _class: Ref<Class<Doc>> } | undefined = undefined
|
const linkProviders = client.getModel().findAllSync(view.mixin.LinkIdProvider, {})
|
||||||
|
|
||||||
|
let selectedData: { id: string, _class: Ref<Class<Doc>> } | undefined = undefined
|
||||||
|
|
||||||
let currentSpecial: SpecialNavModel | undefined
|
let currentSpecial: SpecialNavModel | undefined
|
||||||
|
|
||||||
@ -58,10 +61,18 @@
|
|||||||
syncLocation(loc)
|
syncLocation(loc)
|
||||||
})
|
})
|
||||||
|
|
||||||
$: void loadObject(selectedData?._id, selectedData?._class)
|
$: void loadObject(selectedData?.id, selectedData?._class)
|
||||||
|
|
||||||
async function loadObject (_id?: Ref<Doc>, _class?: Ref<Class<Doc>>): Promise<void> {
|
async function loadObject (id?: string, _class?: Ref<Class<Doc>>): Promise<void> {
|
||||||
if (_id == null || _class == null || _class === '') {
|
if (id == null || _class == null || _class === '') {
|
||||||
|
object = undefined
|
||||||
|
objectQuery.unsubscribe()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const _id: Ref<Doc> | undefined = await parseLinkId(linkProviders, id, _class)
|
||||||
|
|
||||||
|
if (_id === undefined) {
|
||||||
object = undefined
|
object = undefined
|
||||||
objectQuery.unsubscribe()
|
objectQuery.unsubscribe()
|
||||||
return
|
return
|
||||||
@ -84,7 +95,7 @@
|
|||||||
|
|
||||||
const id = loc.path[3]
|
const id = loc.path[3]
|
||||||
|
|
||||||
if (!id) {
|
if (id == null || id === '') {
|
||||||
currentSpecial = undefined
|
currentSpecial = undefined
|
||||||
selectedData = undefined
|
selectedData = undefined
|
||||||
object = undefined
|
object = undefined
|
||||||
@ -98,26 +109,30 @@
|
|||||||
selectedData = undefined
|
selectedData = undefined
|
||||||
object = undefined
|
object = undefined
|
||||||
} else {
|
} else {
|
||||||
const [_id, _class] = decodeChannelURI(loc.path[3])
|
const [id, _class] = decodeObjectURI(loc.path[3])
|
||||||
selectedData = { _id, _class }
|
selectedData = { id, _class }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleChannelSelected (event: CustomEvent): void {
|
async function handleChannelSelected (event: CustomEvent): Promise<void> {
|
||||||
if (event.detail === null) {
|
if (event.detail === null) {
|
||||||
selectedData = undefined
|
selectedData = undefined
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const detail = (event.detail ?? {}) as SelectChannelEvent
|
const detail = (event.detail ?? {}) as SelectChannelEvent
|
||||||
|
const _class = detail.object._class
|
||||||
|
const _id = detail.object._id
|
||||||
|
|
||||||
selectedData = { _id: detail.object._id, _class: detail.object._class }
|
const id = await getObjectLinkId(linkProviders, _id, _class, detail.object)
|
||||||
|
|
||||||
if (selectedData._id !== object?._id) {
|
selectedData = { id, _class }
|
||||||
|
|
||||||
|
if (_id !== object?._id) {
|
||||||
object = detail.object
|
object = detail.object
|
||||||
}
|
}
|
||||||
|
|
||||||
openChannel(selectedData._id, selectedData._class)
|
openChannel(selectedData.id, selectedData._class)
|
||||||
}
|
}
|
||||||
|
|
||||||
defineSeparators('chat', [
|
defineSeparators('chat', [
|
||||||
@ -134,7 +149,7 @@
|
|||||||
{#if $deviceInfo.navigator.visible}
|
{#if $deviceInfo.navigator.visible}
|
||||||
<div class="antiPanel-navigator {$deviceInfo.navigator.direction === 'horizontal' ? 'portrait' : 'landscape'}">
|
<div class="antiPanel-navigator {$deviceInfo.navigator.direction === 'horizontal' ? 'portrait' : 'landscape'}">
|
||||||
<div class="antiPanel-wrap__content hulyNavPanel-container">
|
<div class="antiPanel-wrap__content hulyNavPanel-container">
|
||||||
<ChatNavigator objectId={selectedData?._id} {object} {currentSpecial} on:select={handleChannelSelected} />
|
<ChatNavigator {object} {currentSpecial} on:select={handleChannelSelected} />
|
||||||
</div>
|
</div>
|
||||||
<Separator name="chat" float={$deviceInfo.navigator.float ? 'navigator' : true} index={0} />
|
<Separator name="chat" float={$deviceInfo.navigator.float ? 'navigator' : true} index={0} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
import ChatNavSection from './ChatNavSection.svelte'
|
import ChatNavSection from './ChatNavSection.svelte'
|
||||||
import chunter from '../../../plugin'
|
import chunter from '../../../plugin'
|
||||||
|
|
||||||
export let objectId: Ref<Doc> | undefined
|
|
||||||
export let object: Doc | undefined
|
export let object: Doc | undefined
|
||||||
export let model: ChatNavGroupModel
|
export let model: ChatNavGroupModel
|
||||||
|
|
||||||
@ -191,7 +190,7 @@
|
|||||||
id={section.id}
|
id={section.id}
|
||||||
objects={section.objects}
|
objects={section.objects}
|
||||||
{contexts}
|
{contexts}
|
||||||
{objectId}
|
objectId={object?._id}
|
||||||
header={section.label}
|
header={section.label}
|
||||||
actions={getSectionActions(section, contexts)}
|
actions={getSectionActions(section, contexts)}
|
||||||
sortFn={model.sortFn}
|
sortFn={model.sortFn}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import { getResource } from '@hcengineering/platform'
|
import { getResource } from '@hcengineering/platform'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import { Action, IconEdit } from '@hcengineering/ui'
|
import { Action, IconEdit } from '@hcengineering/ui'
|
||||||
import { getActions } from '@hcengineering/view-resources'
|
import { getActions, getObjectLinkId } from '@hcengineering/view-resources'
|
||||||
import {
|
import {
|
||||||
getNotificationsCount,
|
getNotificationsCount,
|
||||||
InboxNotificationsClientImpl,
|
InboxNotificationsClientImpl,
|
||||||
@ -68,6 +68,8 @@
|
|||||||
actions = res
|
actions = res
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const linkProviders = client.getModel().findAllSync(view.mixin.LinkIdProvider, {})
|
||||||
|
|
||||||
async function getChannelActions (context: DocNotifyContext | undefined, object: Doc): Promise<Action[]> {
|
async function getChannelActions (context: DocNotifyContext | undefined, object: Doc): Promise<Action[]> {
|
||||||
const result: Action[] = []
|
const result: Action[] = []
|
||||||
|
|
||||||
@ -79,7 +81,8 @@
|
|||||||
icon: view.icon.Open,
|
icon: view.icon.Open,
|
||||||
label: view.string.Open,
|
label: view.string.Open,
|
||||||
action: async () => {
|
action: async () => {
|
||||||
openChannel(object._id, object._class)
|
const id = await getObjectLinkId(linkProviders, object._id, object._class, object)
|
||||||
|
openChannel(id, object._class)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@
|
|||||||
import { userSearch } from '../../../index'
|
import { userSearch } from '../../../index'
|
||||||
import { navigateToSpecial } from '../../../navigation'
|
import { navigateToSpecial } from '../../../navigation'
|
||||||
|
|
||||||
export let objectId: Ref<Doc> | undefined
|
|
||||||
export let object: Doc | undefined
|
export let object: Doc | undefined
|
||||||
export let currentSpecial: SpecialNavModel | undefined
|
export let currentSpecial: SpecialNavModel | undefined
|
||||||
|
|
||||||
@ -112,7 +111,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<Scroller shrink>
|
<Scroller shrink>
|
||||||
{#each chatNavGroupModels as model}
|
{#each chatNavGroupModels as model}
|
||||||
<ChatNavGroup {object} {objectId} {model} on:select />
|
<ChatNavGroup {object} {model} on:select />
|
||||||
{/each}
|
{/each}
|
||||||
</Scroller>
|
</Scroller>
|
||||||
<NavFooter />
|
<NavFooter />
|
||||||
|
@ -4,22 +4,16 @@ import type { ActivityMessage } from '@hcengineering/activity'
|
|||||||
import { chunterId, type ChunterSpace, type ThreadMessage } from '@hcengineering/chunter'
|
import { chunterId, type ChunterSpace, type ThreadMessage } from '@hcengineering/chunter'
|
||||||
import { notificationId } from '@hcengineering/notification'
|
import { notificationId } from '@hcengineering/notification'
|
||||||
import { workbenchId } from '@hcengineering/workbench'
|
import { workbenchId } from '@hcengineering/workbench'
|
||||||
|
import { getObjectLinkId } from '@hcengineering/view-resources'
|
||||||
|
import { getClient } from '@hcengineering/presentation'
|
||||||
|
import view, { encodeObjectURI, decodeObjectURI } from '@hcengineering/view'
|
||||||
|
|
||||||
import { chatSpecials } from './components/chat/utils'
|
import { chatSpecials } from './components/chat/utils'
|
||||||
import { isThreadMessage } from './utils'
|
import { isThreadMessage } from './utils'
|
||||||
|
|
||||||
export function decodeChannelURI (value: string): [Ref<Doc>, Ref<Class<Doc>>] {
|
export function openChannel (_id: string, _class: Ref<Class<Doc>>, thread?: Ref<ActivityMessage>): void {
|
||||||
return decodeURIComponent(value).split('|') as [Ref<Doc>, Ref<Class<Doc>>]
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeChannelURI (_id: Ref<Doc>, _class: Ref<Class<Doc>>): string {
|
|
||||||
return [_id, _class].join('|')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function openChannel (_id: Ref<Doc>, _class: Ref<Class<Doc>>, thread?: Ref<ActivityMessage>): void {
|
|
||||||
const loc = getCurrentLocation()
|
const loc = getCurrentLocation()
|
||||||
|
const id = encodeObjectURI(_id, _class)
|
||||||
const id = encodeChannelURI(_id, _class)
|
|
||||||
|
|
||||||
if (loc.path[3] === id) {
|
if (loc.path[3] === id) {
|
||||||
return
|
return
|
||||||
@ -45,12 +39,18 @@ export async function openMessageFromSpecial (message?: ActivityMessage): Promis
|
|||||||
}
|
}
|
||||||
|
|
||||||
const loc = getCurrentResolvedLocation()
|
const loc = getCurrentResolvedLocation()
|
||||||
|
const client = getClient()
|
||||||
|
const providers = client.getModel().findAllSync(view.mixin.LinkIdProvider, {})
|
||||||
|
|
||||||
if (isThreadMessage(message)) {
|
if (isThreadMessage(message)) {
|
||||||
loc.path[3] = encodeChannelURI(message.objectId, message.objectClass)
|
const id = await getObjectLinkId(providers, message.objectId, message.objectClass)
|
||||||
|
|
||||||
|
loc.path[3] = encodeObjectURI(id, message.objectClass)
|
||||||
loc.path[4] = message.attachedTo
|
loc.path[4] = message.attachedTo
|
||||||
} else {
|
} else {
|
||||||
loc.path[3] = encodeChannelURI(message.attachedTo, message.attachedToClass)
|
const id = await getObjectLinkId(providers, message.attachedTo, message.attachedToClass)
|
||||||
|
|
||||||
|
loc.path[3] = encodeObjectURI(id, message.attachedToClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
loc.query = { ...loc.query, message: message._id }
|
loc.query = { ...loc.query, message: message._id }
|
||||||
@ -66,47 +66,58 @@ export function navigateToSpecial (specialId: string): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getMessageLink (message: ActivityMessage): Promise<string> {
|
export async function getMessageLink (message: ActivityMessage): Promise<string> {
|
||||||
|
const client = getClient()
|
||||||
const location = getCurrentResolvedLocation()
|
const location = getCurrentResolvedLocation()
|
||||||
|
const providers = client.getModel().findAllSync(view.mixin.LinkIdProvider, {})
|
||||||
|
|
||||||
let threadParent = ''
|
let threadParent = ''
|
||||||
let _id: Ref<Doc>
|
let _id: string
|
||||||
let _class: Ref<Class<Doc>>
|
let _class: Ref<Class<Doc>>
|
||||||
|
|
||||||
if (isThreadMessage(message)) {
|
if (isThreadMessage(message)) {
|
||||||
threadParent = `/${message.attachedTo}`
|
threadParent = `/${message.attachedTo}`
|
||||||
_id = message.objectId
|
_id = await getObjectLinkId(providers, message.objectId, message.objectClass)
|
||||||
_class = message.objectClass
|
_class = message.objectClass
|
||||||
} else {
|
} else {
|
||||||
_id = message.attachedTo
|
_id = await getObjectLinkId(providers, message.attachedTo, message.attachedToClass)
|
||||||
_class = message.attachedToClass
|
_class = message.attachedToClass
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = encodeChannelURI(_id, _class)
|
const id = encodeObjectURI(_id, _class)
|
||||||
|
|
||||||
return `${window.location.protocol}//${window.location.host}/${workbenchId}/${location.path[1]}/${chunterId}/${id}${threadParent}?message=${message._id}`
|
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> {
|
export async function chunterSpaceLinkFragmentProvider (doc: ChunterSpace): Promise<Location> {
|
||||||
const loc = getCurrentResolvedLocation()
|
const loc = getCurrentResolvedLocation()
|
||||||
|
const client = getClient()
|
||||||
|
const providers = client.getModel().findAllSync(view.mixin.LinkIdProvider, {})
|
||||||
|
|
||||||
|
const id = await getObjectLinkId(providers, doc._id, doc._class, doc)
|
||||||
|
|
||||||
loc.path.length = 2
|
loc.path.length = 2
|
||||||
loc.fragment = undefined
|
loc.fragment = undefined
|
||||||
loc.query = undefined
|
loc.query = undefined
|
||||||
loc.path[2] = chunterId
|
loc.path[2] = chunterId
|
||||||
loc.path[3] = encodeChannelURI(doc._id, doc._class)
|
loc.path[3] = encodeObjectURI(id, doc._class)
|
||||||
|
|
||||||
return loc
|
return loc
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildThreadLink (
|
export async function buildThreadLink (
|
||||||
loc: Location,
|
loc: Location,
|
||||||
_id: Ref<Doc>,
|
_id: Ref<Doc>,
|
||||||
_class: Ref<Class<Doc>>,
|
_class: Ref<Class<Doc>>,
|
||||||
threadParent: Ref<ActivityMessage>
|
threadParent: Ref<ActivityMessage>,
|
||||||
): Location {
|
doc?: Doc
|
||||||
|
): Promise<Location> {
|
||||||
|
const client = getClient()
|
||||||
|
const providers = client.getModel().findAllSync(view.mixin.LinkIdProvider, {})
|
||||||
|
const id = await getObjectLinkId(providers, _id, _class, doc)
|
||||||
|
|
||||||
const specials = chatSpecials.map(({ id }) => id)
|
const specials = chatSpecials.map(({ id }) => id)
|
||||||
const id = encodeChannelURI(_id, _class)
|
const objectURI = encodeObjectURI(id, _class)
|
||||||
const isSameChannel = loc.path[3] === id
|
const isSameChannel = loc.path[3] === objectURI
|
||||||
|
|
||||||
if (!isSameChannel) {
|
if (!isSameChannel) {
|
||||||
loc.query = { message: threadParent }
|
loc.query = { message: threadParent }
|
||||||
@ -121,7 +132,7 @@ export function buildThreadLink (
|
|||||||
loc.path[2] = chunterId
|
loc.path[2] = chunterId
|
||||||
}
|
}
|
||||||
|
|
||||||
loc.path[3] = id
|
loc.path[3] = objectURI
|
||||||
loc.path[4] = threadParent
|
loc.path[4] = threadParent
|
||||||
loc.fragment = undefined
|
loc.fragment = undefined
|
||||||
|
|
||||||
@ -131,7 +142,7 @@ export function buildThreadLink (
|
|||||||
export async function getThreadLink (doc: ThreadMessage): Promise<Location> {
|
export async function getThreadLink (doc: ThreadMessage): Promise<Location> {
|
||||||
const loc = getCurrentResolvedLocation()
|
const loc = getCurrentResolvedLocation()
|
||||||
|
|
||||||
return buildThreadLink(loc, doc.objectId, doc.objectClass, doc.attachedTo)
|
return await buildThreadLink(loc, doc.objectId, doc.objectClass, doc.attachedTo, doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function replyToThread (message: ActivityMessage): Promise<void> {
|
export async function replyToThread (message: ActivityMessage): Promise<void> {
|
||||||
@ -141,11 +152,37 @@ export async function replyToThread (message: ActivityMessage): Promise<void> {
|
|||||||
loc.path[2] = chunterId
|
loc.path[2] = chunterId
|
||||||
}
|
}
|
||||||
|
|
||||||
navigate(buildThreadLink(loc, message.attachedTo, message.attachedToClass, message._id))
|
const newLoc = await buildThreadLink(loc, message.attachedTo, message.attachedToClass, message._id)
|
||||||
|
|
||||||
|
navigate(newLoc)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMessageLocation (doc: ActivityMessage): Promise<Location> {
|
export async function getMessageLocation (doc: ActivityMessage): Promise<Location> {
|
||||||
const loc = getCurrentResolvedLocation()
|
const loc = getCurrentResolvedLocation()
|
||||||
|
|
||||||
return buildThreadLink(loc, doc.attachedTo, doc.attachedToClass, doc._id)
|
return await buildThreadLink(loc, doc.attachedTo, doc.attachedToClass, doc._id)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetChunterLocIfEqual (_id: Ref<Doc>, _class: Ref<Class<Doc>>, doc?: Doc): Promise<void> {
|
||||||
|
const loc = getCurrentLocation()
|
||||||
|
|
||||||
|
if (loc.path[2] !== chunterId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
const providers = client.getModel().findAllSync(view.mixin.LinkIdProvider, {})
|
||||||
|
const id = await getObjectLinkId(providers, _id, _class, doc)
|
||||||
|
|
||||||
|
const [locId] = decodeObjectURI(loc.path[3])
|
||||||
|
|
||||||
|
if (locId !== id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loc.path[3] = ''
|
||||||
|
loc.path[4] = ''
|
||||||
|
loc.query = {}
|
||||||
|
loc.path.length = 3
|
||||||
|
navigate(loc)
|
||||||
}
|
}
|
||||||
|
@ -12,13 +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 DirectMessage,
|
|
||||||
type ThreadMessage
|
|
||||||
} from '@hcengineering/chunter'
|
|
||||||
import contact, { type Employee, getName, type Person, type PersonAccount } from '@hcengineering/contact'
|
import contact, { type Employee, getName, type Person, type PersonAccount } from '@hcengineering/contact'
|
||||||
import { employeeByIdStore, PersonIcon } from '@hcengineering/contact-resources'
|
import { employeeByIdStore, PersonIcon } from '@hcengineering/contact-resources'
|
||||||
import {
|
import {
|
||||||
@ -34,7 +28,7 @@ import {
|
|||||||
type Timestamp
|
type Timestamp
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import { type AnySvelteComponent, getCurrentLocation, navigate } from '@hcengineering/ui'
|
import { type AnySvelteComponent } from '@hcengineering/ui'
|
||||||
import { type Asset, translate } from '@hcengineering/platform'
|
import { type Asset, translate } from '@hcengineering/platform'
|
||||||
import { classIcon, getDocLinkTitle, getDocTitle } from '@hcengineering/view-resources'
|
import { classIcon, getDocLinkTitle, getDocTitle } from '@hcengineering/view-resources'
|
||||||
import activity, {
|
import activity, {
|
||||||
@ -55,7 +49,7 @@ 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 { decodeChannelURI } from './navigation'
|
import { resetChunterLocIfEqual } from './navigation'
|
||||||
|
|
||||||
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) {
|
||||||
@ -350,7 +344,7 @@ export async function leaveChannel (channel: Channel, value: Ref<Account> | Arra
|
|||||||
const context = await client.findOne(notification.class.DocNotifyContext, { attachedTo: channel._id })
|
const context = await client.findOne(notification.class.DocNotifyContext, { attachedTo: channel._id })
|
||||||
|
|
||||||
await client.update(channel, { $pull: { members: value } })
|
await client.update(channel, { $pull: { members: value } })
|
||||||
await removeChannelAction(context)
|
await removeChannelAction(context, undefined, { object: channel })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,37 +396,31 @@ export async function readChannelMessages (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetChunterLoc (objectId: Ref<Doc>): void {
|
export async function leaveChannelAction (
|
||||||
const loc = getCurrentLocation()
|
context?: DocNotifyContext,
|
||||||
const [_id] = decodeChannelURI(loc.path[3])
|
_?: Event,
|
||||||
|
props?: { object?: Channel }
|
||||||
if (loc.path[2] !== chunterId || _id !== objectId) {
|
): Promise<void> {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loc.path[3] = ''
|
|
||||||
loc.path[4] = ''
|
|
||||||
loc.query = {}
|
|
||||||
loc.path.length = 3
|
|
||||||
navigate(loc)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function leaveChannelAction (context?: DocNotifyContext): Promise<void> {
|
|
||||||
if (context === undefined) {
|
if (context === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const channel = await client.findOne(chunter.class.Channel, { _id: context.attachedTo as Ref<Channel> })
|
const channel =
|
||||||
|
props?.object ?? (await client.findOne(chunter.class.Channel, { _id: context.attachedTo as Ref<Channel> }))
|
||||||
|
|
||||||
if (channel === undefined) {
|
if (channel === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await leaveChannel(channel, getCurrentAccount()._id)
|
await leaveChannel(channel, getCurrentAccount()._id)
|
||||||
resetChunterLoc(channel._id)
|
await resetChunterLocIfEqual(channel._id, channel._class, channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removeChannelAction (context?: DocNotifyContext): Promise<void> {
|
export async function removeChannelAction (
|
||||||
|
context?: DocNotifyContext,
|
||||||
|
_?: Event,
|
||||||
|
props?: { object?: Doc }
|
||||||
|
): Promise<void> {
|
||||||
if (context === undefined) {
|
if (context === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -442,7 +430,7 @@ export async function removeChannelAction (context?: DocNotifyContext): Promise<
|
|||||||
await archiveContextNotifications(context)
|
await archiveContextNotifications(context)
|
||||||
await client.remove(context)
|
await client.remove(context)
|
||||||
|
|
||||||
resetChunterLoc(context.attachedTo)
|
await resetChunterLocIfEqual(context.attachedTo, context.attachedToClass, props?.object)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isThreadMessage (message: ActivityMessage): message is ThreadMessage {
|
export function isThreadMessage (message: ActivityMessage): message is ThreadMessage {
|
||||||
|
@ -46,7 +46,15 @@ import TeamspaceSpacePresenter from './components/navigator/TeamspaceSpacePresen
|
|||||||
import CreateTeamspace from './components/teamspace/CreateTeamspace.svelte'
|
import CreateTeamspace from './components/teamspace/CreateTeamspace.svelte'
|
||||||
|
|
||||||
import document from './plugin'
|
import document from './plugin'
|
||||||
import { createEmptyDocument, documentTitleProvider, getDocumentLink, getDocumentUrl, resolveLocation } from './utils'
|
import {
|
||||||
|
createEmptyDocument,
|
||||||
|
documentTitleProvider,
|
||||||
|
getDocumentLink,
|
||||||
|
getDocumentLinkId,
|
||||||
|
getDocumentUrl,
|
||||||
|
parseDocumentId,
|
||||||
|
resolveLocation
|
||||||
|
} from './utils'
|
||||||
|
|
||||||
const toObjectSearchResult = (e: WithLookup<Document>): ObjectSearchResult => ({
|
const toObjectSearchResult = (e: WithLookup<Document>): ObjectSearchResult => ({
|
||||||
doc: e,
|
doc: e,
|
||||||
@ -187,7 +195,9 @@ export default async (): Promise<Resources> => ({
|
|||||||
GetObjectLinkFragment: getDocumentLink,
|
GetObjectLinkFragment: getDocumentLink,
|
||||||
DocumentTitleProvider: documentTitleProvider,
|
DocumentTitleProvider: documentTitleProvider,
|
||||||
CanLockDocument: canLockDocument,
|
CanLockDocument: canLockDocument,
|
||||||
CanUnlockDocument: canUnlockDocument
|
CanUnlockDocument: canUnlockDocument,
|
||||||
|
GetDocumentLinkId: getDocumentLinkId,
|
||||||
|
ParseDocumentId: parseDocumentId
|
||||||
},
|
},
|
||||||
resolver: {
|
resolver: {
|
||||||
Location: resolveLocation
|
Location: resolveLocation
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import { type Client, type Doc, type Ref } from '@hcengineering/core'
|
import { type Client, type Doc, type Ref } from '@hcengineering/core'
|
||||||
import document, { documentId } from '@hcengineering/document'
|
import document, { type Document, documentId } from '@hcengineering/document'
|
||||||
import { mergeIds, type IntlString, type Resource } from '@hcengineering/platform'
|
import { mergeIds, type IntlString, type Resource } from '@hcengineering/platform'
|
||||||
import { type AnyComponent, type Location } from '@hcengineering/ui'
|
import { type AnyComponent, type Location } from '@hcengineering/ui'
|
||||||
|
|
||||||
@ -29,7 +29,9 @@ export default mergeIds(documentId, document, {
|
|||||||
function: {
|
function: {
|
||||||
DocumentTitleProvider: '' as Resource<<T extends Doc>(client: Client, ref: Ref<T>, doc?: T) => Promise<string>>,
|
DocumentTitleProvider: '' as Resource<<T extends Doc>(client: Client, ref: Ref<T>, doc?: T) => Promise<string>>,
|
||||||
GetDocumentLink: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
GetDocumentLink: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
||||||
GetObjectLinkFragment: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>
|
GetObjectLinkFragment: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
|
||||||
|
GetDocumentLinkId: '' as Resource<(doc: Doc) => Promise<string>>,
|
||||||
|
ParseDocumentId: '' as Resource<(id: string) => Promise<Ref<Document> | undefined>>
|
||||||
},
|
},
|
||||||
string: {
|
string: {
|
||||||
DocumentNamePlaceholder: '' as IntlString,
|
DocumentNamePlaceholder: '' as IntlString,
|
||||||
|
@ -105,12 +105,17 @@ export async function generateLocation (loc: Location, id: Ref<Document>): Promi
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getDocumentIdFromFragment (fragment: string): Ref<Document> | undefined {
|
export function getDocumentIdFromFragment (fragment: string): Ref<Document> | undefined {
|
||||||
const [, _id] = decodeURIComponent(fragment).split('|')
|
const [, id] = decodeURIComponent(fragment).split('|')
|
||||||
return _id as Ref<Document>
|
|
||||||
|
if (id == null) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return (parseDocumentId(id) ?? id) as Ref<Document>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDocumentUrl (doc: Document): string {
|
export function getDocumentUrl (doc: Document): string {
|
||||||
const id = getDocumentId(doc)
|
const id = getDocumentLinkId(doc)
|
||||||
|
|
||||||
const location = getCurrentResolvedLocation()
|
const location = getCurrentResolvedLocation()
|
||||||
const frontUrl = getMetadata(presentation.metadata.FrontUrl)
|
const frontUrl = getMetadata(presentation.metadata.FrontUrl)
|
||||||
@ -124,17 +129,17 @@ export function getDocumentLink (doc: Document): Location {
|
|||||||
loc.fragment = undefined
|
loc.fragment = undefined
|
||||||
loc.query = undefined
|
loc.query = undefined
|
||||||
loc.path[2] = documentId
|
loc.path[2] = documentId
|
||||||
loc.path[3] = getDocumentId(doc)
|
loc.path[3] = getDocumentLinkId(doc)
|
||||||
|
|
||||||
return loc
|
return loc
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDocumentId (doc: Document): string {
|
export function getDocumentLinkId (doc: Document): string {
|
||||||
const slug = slugify(doc.name, { lower: true })
|
const slug = slugify(doc.name, { lower: true })
|
||||||
return `${slug}-${doc._id}`
|
return `${slug}-${doc._id}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDocumentId (shortLink: string): Ref<Document> | undefined {
|
export function parseDocumentId (shortLink: string): Ref<Document> | undefined {
|
||||||
const parts = shortLink.split('-')
|
const parts = shortLink.split('-')
|
||||||
if (parts.length > 1) {
|
if (parts.length > 1) {
|
||||||
return parts[parts.length - 1] as Ref<Document>
|
return parts[parts.length - 1] as Ref<Document>
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
deviceOptionsStore as deviceInfo
|
deviceOptionsStore as deviceInfo
|
||||||
} from '@hcengineering/ui'
|
} from '@hcengineering/ui'
|
||||||
import view from '@hcengineering/view'
|
import view from '@hcengineering/view'
|
||||||
import { ListSelectionProvider, restrictionStore, updateFocus } from '@hcengineering/view-resources'
|
import { ListSelectionProvider, parseLinkId, restrictionStore, updateFocus } from '@hcengineering/view-resources'
|
||||||
import workbench, { Application, NavigatorModel, SpecialNavModel, ViewConfiguration } from '@hcengineering/workbench'
|
import workbench, { Application, NavigatorModel, SpecialNavModel, ViewConfiguration } from '@hcengineering/workbench'
|
||||||
import { SpaceView, buildNavModel } from '@hcengineering/workbench-resources'
|
import { SpaceView, buildNavModel } from '@hcengineering/workbench-resources'
|
||||||
import guest from '../plugin'
|
import guest from '../plugin'
|
||||||
@ -189,11 +189,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const linkProviders = client.getModel().findAllSync(view.mixin.LinkIdProvider, {})
|
||||||
|
|
||||||
async function setOpenPanelFocus (fragment: string): Promise<void> {
|
async function setOpenPanelFocus (fragment: string): Promise<void> {
|
||||||
const props = decodeURIComponent(fragment).split('|')
|
const props = decodeURIComponent(fragment).split('|')
|
||||||
|
|
||||||
if (props.length >= 3) {
|
if (props.length >= 3) {
|
||||||
const doc = await client.findOne<Doc>(props[2] as Ref<Class<Doc>>, { _id: props[1] as Ref<Doc> })
|
const id = props[1]
|
||||||
|
const _class = props[2] as Ref<Class<Doc>>
|
||||||
|
const _id = await parseLinkId(linkProviders, id, _class)
|
||||||
|
|
||||||
|
const doc = await client.findOne<Doc>(_class, { _id })
|
||||||
if (doc !== undefined) {
|
if (doc !== undefined) {
|
||||||
await checkAccess(doc)
|
await checkAccess(doc)
|
||||||
const provider = ListSelectionProvider.Find(doc._id)
|
const provider = ListSelectionProvider.Find(doc._id)
|
||||||
@ -203,8 +209,8 @@
|
|||||||
})
|
})
|
||||||
openPanel(
|
openPanel(
|
||||||
props[0] as AnyComponent,
|
props[0] as AnyComponent,
|
||||||
props[1],
|
_id,
|
||||||
props[2],
|
_class,
|
||||||
(props[3] ?? undefined) as PopupAlignment,
|
(props[3] ?? undefined) as PopupAlignment,
|
||||||
(props[4] ?? undefined) as AnyComponent
|
(props[4] ?? undefined) as AnyComponent
|
||||||
)
|
)
|
||||||
@ -253,14 +259,15 @@
|
|||||||
async function getWindowTitle (loc: Location): Promise<string | undefined> {
|
async function getWindowTitle (loc: Location): Promise<string | undefined> {
|
||||||
if (loc.fragment == null) return
|
if (loc.fragment == null) return
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
const [, _id, _class] = decodeURIComponent(loc.fragment).split('|')
|
const [, id, _class] = decodeURIComponent(loc.fragment).split('|')
|
||||||
if (_class == null) return
|
if (_class == null) return
|
||||||
|
|
||||||
const mixin = hierarchy.classHierarchyMixin(_class as Ref<Class<Doc>>, view.mixin.ObjectTitle)
|
const mixin = hierarchy.classHierarchyMixin(_class as Ref<Class<Doc>>, view.mixin.ObjectTitle)
|
||||||
if (mixin === undefined) return
|
if (mixin === undefined) return
|
||||||
const titleProvider = await getResource(mixin.titleProvider)
|
const titleProvider = await getResource(mixin.titleProvider)
|
||||||
try {
|
try {
|
||||||
return await titleProvider(client, _id as Ref<Doc>)
|
const _id = await parseLinkId(linkProviders, id, _class as Ref<Class<Doc>>)
|
||||||
|
return await titleProvider(client, _id)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
Analytics.handleError(err)
|
Analytics.handleError(err)
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
@ -13,15 +13,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import { DocNotifyContext, InboxNotification, notificationId } from '@hcengineering/notification'
|
||||||
ActivityInboxNotification,
|
|
||||||
decodeObjectURI,
|
|
||||||
DocNotifyContext,
|
|
||||||
InboxNotification,
|
|
||||||
notificationId
|
|
||||||
} from '@hcengineering/notification'
|
|
||||||
import { ActionContext, createQuery, getClient } from '@hcengineering/presentation'
|
import { ActionContext, createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import view from '@hcengineering/view'
|
import view, { decodeObjectURI } from '@hcengineering/view'
|
||||||
import {
|
import {
|
||||||
AnyComponent,
|
AnyComponent,
|
||||||
Component,
|
Component,
|
||||||
@ -36,16 +30,16 @@
|
|||||||
TabList,
|
TabList,
|
||||||
deviceOptionsStore as deviceInfo
|
deviceOptionsStore as deviceInfo
|
||||||
} from '@hcengineering/ui'
|
} from '@hcengineering/ui'
|
||||||
import chunter, { ThreadMessage } from '@hcengineering/chunter'
|
import chunter from '@hcengineering/chunter'
|
||||||
import activity, { ActivityMessage } from '@hcengineering/activity'
|
import activity, { ActivityMessage } from '@hcengineering/activity'
|
||||||
import { isActivityMessageClass, isReactionMessage } from '@hcengineering/activity-resources'
|
|
||||||
import { get } from 'svelte/store'
|
import { get } from 'svelte/store'
|
||||||
import { translate } from '@hcengineering/platform'
|
import { translate } from '@hcengineering/platform'
|
||||||
import { getCurrentAccount, groupByArray, IdMap, Ref, SortingOrder } from '@hcengineering/core'
|
import { getCurrentAccount, groupByArray, IdMap, Ref, SortingOrder } from '@hcengineering/core'
|
||||||
|
import { parseLinkId } from '@hcengineering/view-resources'
|
||||||
|
|
||||||
import { InboxNotificationsClientImpl } from '../../inboxNotificationsClient'
|
import { InboxNotificationsClientImpl } from '../../inboxNotificationsClient'
|
||||||
import SettingsButton from './SettingsButton.svelte'
|
import SettingsButton from './SettingsButton.svelte'
|
||||||
import { getDisplayInboxData, isMentionNotification, openInboxDoc, resolveLocation } from '../../utils'
|
import { getDisplayInboxData, resetInboxContext, resolveLocation, selectInboxContext } from '../../utils'
|
||||||
import { InboxData, InboxNotificationsFilter } from '../../types'
|
import { InboxData, InboxNotificationsFilter } from '../../types'
|
||||||
import InboxGroupedListView from './InboxGroupedListView.svelte'
|
import InboxGroupedListView from './InboxGroupedListView.svelte'
|
||||||
import notification from '../../plugin'
|
import notification from '../../plugin'
|
||||||
@ -69,6 +63,8 @@
|
|||||||
labelIntl: notification.string.All
|
labelIntl: notification.string.All
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const linkProviders = client.getModel().findAllSync(view.mixin.LinkIdProvider, {})
|
||||||
|
|
||||||
let showArchive = false
|
let showArchive = false
|
||||||
let archivedActivityNotifications: InboxNotification[] = []
|
let archivedActivityNotifications: InboxNotification[] = []
|
||||||
let archivedOtherNotifications: InboxNotification[] = []
|
let archivedOtherNotifications: InboxNotification[] = []
|
||||||
@ -156,8 +152,9 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const [_id] = decodeObjectURI(loc?.loc.path[3] ?? '')
|
const [id, _class] = decodeObjectURI(loc?.loc.path[3] ?? '')
|
||||||
const context = $contextByDocStore.get(_id)
|
const _id = await parseLinkId(linkProviders, id, _class)
|
||||||
|
const context = _id ? $contextByDocStore.get(_id) : undefined
|
||||||
|
|
||||||
selectedContextId = context?._id
|
selectedContextId = context?._id
|
||||||
|
|
||||||
@ -229,56 +226,13 @@
|
|||||||
selectedContextId = selectedContext?._id
|
selectedContextId = selectedContext?._id
|
||||||
|
|
||||||
if (selectedContext === undefined) {
|
if (selectedContext === undefined) {
|
||||||
openInboxDoc()
|
resetInboxContext()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedNotification: InboxNotification | undefined = event?.detail?.notification
|
const selectedNotification: InboxNotification | undefined = event?.detail?.notification
|
||||||
|
|
||||||
if (isMentionNotification(selectedNotification) && isActivityMessageClass(selectedNotification.mentionedInClass)) {
|
void selectInboxContext(linkProviders, selectedContext, selectedNotification)
|
||||||
const selectedMsg = selectedNotification.mentionedIn as Ref<ActivityMessage>
|
|
||||||
|
|
||||||
openInboxDoc(
|
|
||||||
selectedContext.attachedTo,
|
|
||||||
selectedContext.attachedToClass,
|
|
||||||
isActivityMessageClass(selectedContext.attachedToClass)
|
|
||||||
? (selectedContext.attachedTo as Ref<ActivityMessage>)
|
|
||||||
: undefined,
|
|
||||||
selectedMsg
|
|
||||||
)
|
|
||||||
} else if (hierarchy.isDerived(selectedContext.attachedToClass, activity.class.ActivityMessage)) {
|
|
||||||
const message = event?.detail?.notification?.$lookup?.attachedTo
|
|
||||||
|
|
||||||
if (selectedContext.attachedToClass === chunter.class.ThreadMessage) {
|
|
||||||
const thread = await client.findOne(chunter.class.ThreadMessage, {
|
|
||||||
_id: selectedContext.attachedTo as Ref<ThreadMessage>
|
|
||||||
})
|
|
||||||
openInboxDoc(selectedContext.attachedTo, selectedContext.attachedToClass, thread?.attachedTo, thread?._id)
|
|
||||||
} else if (isReactionMessage(message)) {
|
|
||||||
openInboxDoc(
|
|
||||||
selectedContext.attachedTo,
|
|
||||||
selectedContext.attachedToClass,
|
|
||||||
undefined,
|
|
||||||
selectedContext.attachedTo as Ref<ActivityMessage>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
const selectedMsg = (selectedNotification as ActivityInboxNotification)?.attachedTo
|
|
||||||
|
|
||||||
openInboxDoc(
|
|
||||||
selectedContext.attachedTo,
|
|
||||||
selectedContext.attachedToClass,
|
|
||||||
selectedMsg ? (selectedContext.attachedTo as Ref<ActivityMessage>) : undefined,
|
|
||||||
selectedMsg ?? (selectedContext.attachedTo as Ref<ActivityMessage>)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
openInboxDoc(
|
|
||||||
selectedContext.attachedTo,
|
|
||||||
selectedContext.attachedToClass,
|
|
||||||
undefined,
|
|
||||||
(selectedNotification as ActivityInboxNotification)?.attachedTo
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateSelectedPanel (selectedContext?: DocNotifyContext): Promise<void> {
|
async function updateSelectedPanel (selectedContext?: DocNotifyContext): Promise<void> {
|
||||||
|
@ -18,7 +18,13 @@ import activity, {
|
|||||||
type DisplayDocUpdateMessage,
|
type DisplayDocUpdateMessage,
|
||||||
type DocUpdateMessage
|
type DocUpdateMessage
|
||||||
} from '@hcengineering/activity'
|
} from '@hcengineering/activity'
|
||||||
import { activityMessagesComparator, combineActivityMessages, messageInFocus } from '@hcengineering/activity-resources'
|
import {
|
||||||
|
activityMessagesComparator,
|
||||||
|
combineActivityMessages,
|
||||||
|
isActivityMessageClass,
|
||||||
|
isReactionMessage,
|
||||||
|
messageInFocus
|
||||||
|
} from '@hcengineering/activity-resources'
|
||||||
import {
|
import {
|
||||||
SortingOrder,
|
SortingOrder,
|
||||||
getCurrentAccount,
|
getCurrentAccount,
|
||||||
@ -31,8 +37,6 @@ import {
|
|||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import notification, {
|
import notification, {
|
||||||
NotificationStatus,
|
NotificationStatus,
|
||||||
decodeObjectURI,
|
|
||||||
encodeObjectURI,
|
|
||||||
notificationId,
|
notificationId,
|
||||||
type ActivityInboxNotification,
|
type ActivityInboxNotification,
|
||||||
type Collaborators,
|
type Collaborators,
|
||||||
@ -57,6 +61,9 @@ import { get, writable } from 'svelte/store'
|
|||||||
import { InboxNotificationsClientImpl } from './inboxNotificationsClient'
|
import { InboxNotificationsClientImpl } from './inboxNotificationsClient'
|
||||||
import { type InboxData, type InboxNotificationsFilter } from './types'
|
import { type InboxData, type InboxNotificationsFilter } from './types'
|
||||||
import { getMetadata } from '@hcengineering/platform'
|
import { getMetadata } from '@hcengineering/platform'
|
||||||
|
import { getObjectLinkId } from '@hcengineering/view-resources'
|
||||||
|
import { decodeObjectURI, encodeObjectURI, type LinkIdProvider } from '@hcengineering/view'
|
||||||
|
import chunter, { type ThreadMessage } from '@hcengineering/chunter'
|
||||||
|
|
||||||
export async function hasDocNotifyContextPinAction (docNotifyContext: DocNotifyContext): Promise<boolean> {
|
export async function hasDocNotifyContextPinAction (docNotifyContext: DocNotifyContext): Promise<boolean> {
|
||||||
if (docNotifyContext.hidden) {
|
if (docNotifyContext.hidden) {
|
||||||
@ -470,7 +477,7 @@ export async function resolveLocation (loc: Location): Promise<ResolvedLocation
|
|||||||
|
|
||||||
async function generateLocation (
|
async function generateLocation (
|
||||||
loc: Location,
|
loc: Location,
|
||||||
_id: Ref<Doc>,
|
_id: string,
|
||||||
_class: Ref<Class<Doc>>
|
_class: Ref<Class<Doc>>
|
||||||
): Promise<ResolvedLocation | undefined> {
|
): Promise<ResolvedLocation | undefined> {
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
@ -511,12 +518,13 @@ async function generateLocation (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function openInboxDoc (
|
async function navigateToInboxDoc (
|
||||||
|
providers: LinkIdProvider[],
|
||||||
_id?: Ref<Doc>,
|
_id?: Ref<Doc>,
|
||||||
_class?: Ref<Class<Doc>>,
|
_class?: Ref<Class<Doc>>,
|
||||||
thread?: Ref<ActivityMessage>,
|
thread?: Ref<ActivityMessage>,
|
||||||
message?: Ref<ActivityMessage>
|
message?: Ref<ActivityMessage>
|
||||||
): void {
|
): Promise<void> {
|
||||||
const loc = getLocation()
|
const loc = getLocation()
|
||||||
|
|
||||||
if (loc.path[2] !== notificationId) {
|
if (loc.path[2] !== notificationId) {
|
||||||
@ -524,14 +532,13 @@ export function openInboxDoc (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_id === undefined || _class === undefined) {
|
if (_id === undefined || _class === undefined) {
|
||||||
loc.query = { message: null }
|
resetInboxContext()
|
||||||
loc.path.length = 3
|
|
||||||
localStorage.setItem(`${locationStorageKeyId}_${notificationId}`, JSON.stringify(loc))
|
|
||||||
navigate(loc)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
loc.path[3] = encodeObjectURI(_id, _class)
|
const id = await getObjectLinkId(providers, _id, _class)
|
||||||
|
|
||||||
|
loc.path[3] = encodeObjectURI(id, _class)
|
||||||
|
|
||||||
if (thread !== undefined) {
|
if (thread !== undefined) {
|
||||||
loc.path[4] = thread
|
loc.path[4] = thread
|
||||||
@ -546,6 +553,96 @@ export function openInboxDoc (
|
|||||||
navigate(loc)
|
navigate(loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resetInboxContext (): void {
|
||||||
|
const loc = getLocation()
|
||||||
|
|
||||||
|
if (loc.path[2] !== notificationId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loc.query = { message: null }
|
||||||
|
loc.path.length = 3
|
||||||
|
|
||||||
|
localStorage.setItem(`${locationStorageKeyId}_${notificationId}`, JSON.stringify(loc))
|
||||||
|
|
||||||
|
navigate(loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function selectInboxContext (
|
||||||
|
linkProviders: LinkIdProvider[],
|
||||||
|
context: DocNotifyContext,
|
||||||
|
notification?: WithLookup<InboxNotification>
|
||||||
|
): Promise<void> {
|
||||||
|
const client = getClient()
|
||||||
|
const hierarchy = client.getHierarchy()
|
||||||
|
|
||||||
|
if (isMentionNotification(notification) && isActivityMessageClass(notification.mentionedInClass)) {
|
||||||
|
const selectedMsg = notification.mentionedIn as Ref<ActivityMessage>
|
||||||
|
|
||||||
|
void navigateToInboxDoc(
|
||||||
|
linkProviders,
|
||||||
|
context.attachedTo,
|
||||||
|
context.attachedToClass,
|
||||||
|
isActivityMessageClass(context.attachedToClass) ? (context.attachedTo as Ref<ActivityMessage>) : undefined,
|
||||||
|
selectedMsg
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (hierarchy.isDerived(context.attachedToClass, activity.class.ActivityMessage)) {
|
||||||
|
const message = (notification as WithLookup<ActivityInboxNotification>)?.$lookup?.attachedTo
|
||||||
|
|
||||||
|
if (context.attachedToClass === chunter.class.ThreadMessage) {
|
||||||
|
const thread = await client.findOne(
|
||||||
|
chunter.class.ThreadMessage,
|
||||||
|
{
|
||||||
|
_id: context.attachedTo as Ref<ThreadMessage>
|
||||||
|
},
|
||||||
|
{ projection: { _id: 1, attachedTo: 1 } }
|
||||||
|
)
|
||||||
|
|
||||||
|
void navigateToInboxDoc(
|
||||||
|
linkProviders,
|
||||||
|
context.attachedTo,
|
||||||
|
context.attachedToClass,
|
||||||
|
thread?.attachedTo,
|
||||||
|
thread?._id
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isReactionMessage(message)) {
|
||||||
|
void navigateToInboxDoc(
|
||||||
|
linkProviders,
|
||||||
|
context.attachedTo,
|
||||||
|
context.attachedToClass,
|
||||||
|
undefined,
|
||||||
|
context.attachedTo as Ref<ActivityMessage>
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedMsg = (notification as ActivityInboxNotification)?.attachedTo
|
||||||
|
|
||||||
|
void navigateToInboxDoc(
|
||||||
|
linkProviders,
|
||||||
|
context.attachedTo,
|
||||||
|
context.attachedToClass,
|
||||||
|
selectedMsg !== undefined ? (context.attachedTo as Ref<ActivityMessage>) : undefined,
|
||||||
|
selectedMsg ?? (context.attachedTo as Ref<ActivityMessage>)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
void navigateToInboxDoc(
|
||||||
|
linkProviders,
|
||||||
|
context.attachedTo,
|
||||||
|
context.attachedToClass,
|
||||||
|
undefined,
|
||||||
|
(notification as ActivityInboxNotification)?.attachedTo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const pushAllowed = writable<boolean>(false)
|
export const pushAllowed = writable<boolean>(false)
|
||||||
|
|
||||||
export async function checkPermission (value: boolean): Promise<boolean> {
|
export async function checkPermission (value: boolean): Promise<boolean> {
|
||||||
|
@ -420,5 +420,4 @@ const notification = plugin(notificationId, {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export * from './utils'
|
|
||||||
export default notification
|
export default notification
|
||||||
|
@ -76,6 +76,7 @@ import {
|
|||||||
getTalentId,
|
getTalentId,
|
||||||
getVacTitle,
|
getVacTitle,
|
||||||
objectLinkProvider,
|
objectLinkProvider,
|
||||||
|
parseLinkId,
|
||||||
resolveLocation
|
resolveLocation
|
||||||
} from './utils'
|
} from './utils'
|
||||||
|
|
||||||
@ -411,7 +412,8 @@ export default async (): Promise<Resources> => ({
|
|||||||
GetIdObjectLinkFragment: getObjectLink,
|
GetIdObjectLinkFragment: getObjectLink,
|
||||||
HideDoneState: hideDoneState,
|
HideDoneState: hideDoneState,
|
||||||
HideArchivedVacancies: hideArchivedVacancies,
|
HideArchivedVacancies: hideArchivedVacancies,
|
||||||
ApplicantHasEmail: applicantHasEmail
|
ApplicantHasEmail: applicantHasEmail,
|
||||||
|
ParseLinkId: parseLinkId
|
||||||
},
|
},
|
||||||
resolver: {
|
resolver: {
|
||||||
Location: resolveLocation
|
Location: resolveLocation
|
||||||
|
@ -149,7 +149,7 @@ export default mergeIds(recruitId, recruit, {
|
|||||||
CreateCandidate: '' as AnyComponent
|
CreateCandidate: '' as AnyComponent
|
||||||
},
|
},
|
||||||
function: {
|
function: {
|
||||||
IdProvider: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
IdProvider: '' as Resource<(doc: Doc) => Promise<string>>,
|
||||||
AppTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
AppTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
||||||
AppIdentifierProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
AppIdentifierProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
||||||
VacTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
VacTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
||||||
|
@ -92,15 +92,41 @@ async function generateIdLocation (loc: Location, shortLink: string): Promise<Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateLocation (loc: Location, shortLink: string): Promise<ResolvedLocation | undefined> {
|
export async function parseLinkId (id: string): Promise<Ref<Doc> | undefined> {
|
||||||
|
if (isShortId(id)) {
|
||||||
|
const client = getClient()
|
||||||
|
const hierarchy = client.getHierarchy()
|
||||||
|
const data = getShortLinkData(hierarchy, id)
|
||||||
|
|
||||||
|
if (data === undefined) {
|
||||||
|
return id as Ref<Doc>
|
||||||
|
}
|
||||||
|
|
||||||
|
const [_class, , number] = data
|
||||||
|
|
||||||
|
if (_class === undefined) {
|
||||||
|
return id as Ref<Doc>
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = await client.findOne(_class, { number }, { projection: { _id: 1 } })
|
||||||
|
|
||||||
|
return doc?._id
|
||||||
|
}
|
||||||
|
|
||||||
|
return id as Ref<Doc>
|
||||||
|
}
|
||||||
|
|
||||||
|
function getShortLinkData (
|
||||||
|
hierarchy: Hierarchy,
|
||||||
|
shortLink: string
|
||||||
|
): [Ref<Class<Doc>> | undefined, string, number] | undefined {
|
||||||
const tokens = shortLink.split('-')
|
const tokens = shortLink.split('-')
|
||||||
if (tokens.length < 2) {
|
if (tokens.length < 2) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
const classLabel = tokens[0]
|
const classLabel = tokens[0]
|
||||||
const number = Number(tokens[1])
|
const number = Number(tokens[1])
|
||||||
const client = getClient()
|
|
||||||
const hierarchy = client.getHierarchy()
|
|
||||||
const classes = [recruit.class.Applicant, recruit.class.Vacancy, recruit.class.Review]
|
const classes = [recruit.class.Applicant, recruit.class.Vacancy, recruit.class.Review]
|
||||||
let _class: Ref<Class<Doc>> | undefined
|
let _class: Ref<Class<Doc>> | undefined
|
||||||
for (const clazz of classes) {
|
for (const clazz of classes) {
|
||||||
@ -109,6 +135,21 @@ async function generateLocation (loc: Location, shortLink: string): Promise<Reso
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return [_class, classLabel, number]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateLocation (loc: Location, shortLink: string): Promise<ResolvedLocation | undefined> {
|
||||||
|
const client = getClient()
|
||||||
|
const hierarchy = client.getHierarchy()
|
||||||
|
const data = getShortLinkData(hierarchy, shortLink)
|
||||||
|
|
||||||
|
if (data === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const [_class, classLabel, number] = data
|
||||||
|
|
||||||
if (_class === undefined) {
|
if (_class === undefined) {
|
||||||
console.error(`Not found class with short label ${classLabel}`)
|
console.error(`Not found class with short label ${classLabel}`)
|
||||||
return undefined
|
return undefined
|
||||||
|
@ -41,7 +41,6 @@
|
|||||||
import ToDoGroup from './ToDoGroup.svelte'
|
import ToDoGroup from './ToDoGroup.svelte'
|
||||||
import MenuClose from './icons/MenuClose.svelte'
|
import MenuClose from './icons/MenuClose.svelte'
|
||||||
import MenuOpen from './icons/MenuOpen.svelte'
|
import MenuOpen from './icons/MenuOpen.svelte'
|
||||||
import IconDiff from './icons/Diff.svelte'
|
|
||||||
import time from '../plugin'
|
import time from '../plugin'
|
||||||
|
|
||||||
export let mode: ToDosMode
|
export let mode: ToDosMode
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
import { Component, showPanel } from '@hcengineering/ui'
|
import { Component, showPanel } from '@hcengineering/ui'
|
||||||
import view from '@hcengineering/view'
|
import view from '@hcengineering/view'
|
||||||
import time from '../plugin'
|
import time from '../plugin'
|
||||||
|
import { getObjectLinkId } from '@hcengineering/view-resources'
|
||||||
|
|
||||||
export let todo: ToDo
|
export let todo: ToDo
|
||||||
export let kind: 'default' | 'todo-line' = 'default'
|
export let kind: 'default' | 'todo-line' = 'default'
|
||||||
@ -27,6 +28,9 @@
|
|||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
|
|
||||||
|
const linkProviders = client.getModel().findAllSync(view.mixin.LinkIdProvider, {})
|
||||||
|
|
||||||
$: presenter = hierarchy.classHierarchyMixin<Doc, ItemPresenter>(todo.attachedToClass, time.mixin.ItemPresenter)
|
$: presenter = hierarchy.classHierarchyMixin<Doc, ItemPresenter>(todo.attachedToClass, time.mixin.ItemPresenter)
|
||||||
|
|
||||||
let doc: Doc | undefined = undefined
|
let doc: Doc | undefined = undefined
|
||||||
@ -35,12 +39,14 @@
|
|||||||
doc = res[0]
|
doc = res[0]
|
||||||
})
|
})
|
||||||
|
|
||||||
async function click (event: MouseEvent) {
|
async function click (event: MouseEvent): Promise<void> {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
if (!doc) return
|
if (!doc) return
|
||||||
const panelComponent = hierarchy.classHierarchyMixin<Class<Doc>, ObjectPanel>(doc._class, view.mixin.ObjectPanel)
|
const panelComponent = hierarchy.classHierarchyMixin<Class<Doc>, ObjectPanel>(doc._class, view.mixin.ObjectPanel)
|
||||||
const component = panelComponent?.component ?? view.component.EditDoc
|
const component = panelComponent?.component ?? view.component.EditDoc
|
||||||
showPanel(component, doc._id, doc._class, 'content')
|
const id = await getObjectLinkId(linkProviders, doc._id, doc._class, doc)
|
||||||
|
|
||||||
|
showPanel(component, id, doc._class, 'content')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -15,9 +15,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AttachmentStyleBoxCollabEditor } from '@hcengineering/attachment-resources'
|
import { AttachmentStyleBoxCollabEditor } from '@hcengineering/attachment-resources'
|
||||||
import { Class, Doc, Ref, WithLookup } from '@hcengineering/core'
|
import { Class, Doc, Ref, WithLookup } from '@hcengineering/core'
|
||||||
import notification from '@hcengineering/notification'
|
|
||||||
import { Panel } from '@hcengineering/panel'
|
import { Panel } from '@hcengineering/panel'
|
||||||
import { getResource } from '@hcengineering/platform'
|
|
||||||
import presentation, {
|
import presentation, {
|
||||||
ActionContext,
|
ActionContext,
|
||||||
ComponentExtensions,
|
ComponentExtensions,
|
||||||
@ -44,21 +42,23 @@
|
|||||||
import view from '@hcengineering/view'
|
import view from '@hcengineering/view'
|
||||||
import { DocNavLink, ParentsNavigator, showMenu } from '@hcengineering/view-resources'
|
import { DocNavLink, ParentsNavigator, showMenu } from '@hcengineering/view-resources'
|
||||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||||
import { generateIssueShortLink } from '../../../issues'
|
import { generateIssueShortLink, getIssueIdByIdentifier } from '../../../issues'
|
||||||
import tracker from '../../../plugin'
|
import tracker from '../../../plugin'
|
||||||
import IssueStatusActivity from '../IssueStatusActivity.svelte'
|
import IssueStatusActivity from '../IssueStatusActivity.svelte'
|
||||||
import ControlPanel from './ControlPanel.svelte'
|
import ControlPanel from './ControlPanel.svelte'
|
||||||
import CopyToClipboard from './CopyToClipboard.svelte'
|
import CopyToClipboard from './CopyToClipboard.svelte'
|
||||||
import SubIssueSelector from './SubIssueSelector.svelte'
|
import SubIssueSelector from './SubIssueSelector.svelte'
|
||||||
import SubIssues from './SubIssues.svelte'
|
import SubIssues from './SubIssues.svelte'
|
||||||
|
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
||||||
|
|
||||||
export let _id: Ref<Issue>
|
export let _id: Ref<Issue> | string
|
||||||
export let _class: Ref<Class<Issue>>
|
export let _class: Ref<Class<Issue>>
|
||||||
export let embedded: boolean = false
|
export let embedded: boolean = false
|
||||||
export let kind: 'default' | 'modern' = 'default'
|
export let kind: 'default' | 'modern' = 'default'
|
||||||
export let readonly: boolean = false
|
export let readonly: boolean = false
|
||||||
|
|
||||||
let lastId: Ref<Doc> = _id
|
let lastId: Ref<Issue> | undefined
|
||||||
|
|
||||||
const queryClient = createQuery()
|
const queryClient = createQuery()
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
@ -70,28 +70,39 @@
|
|||||||
let descriptionBox: AttachmentStyleBoxCollabEditor
|
let descriptionBox: AttachmentStyleBoxCollabEditor
|
||||||
let showAllMixins: boolean
|
let showAllMixins: boolean
|
||||||
|
|
||||||
const inboxClient = getResource(notification.function.GetInboxNotificationsClient).then((res) => res())
|
const inboxClient = InboxNotificationsClientImpl.getClient()
|
||||||
|
|
||||||
$: read(_id)
|
let issueId: Ref<Issue> | undefined
|
||||||
function read (_id: Ref<Doc>): void {
|
|
||||||
if (lastId !== _id) {
|
$: void getIssueIdByIdentifier(_id).then((res) => {
|
||||||
|
issueId = res ?? (_id as Ref<Issue>)
|
||||||
|
|
||||||
|
if (lastId === undefined) {
|
||||||
|
lastId = issueId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$: read(issueId)
|
||||||
|
|
||||||
|
function read (_id?: Ref<Issue>): void {
|
||||||
|
if (_id && lastId && lastId !== _id) {
|
||||||
const prev = lastId
|
const prev = lastId
|
||||||
lastId = _id
|
lastId = _id
|
||||||
void inboxClient.then((client) => client.readDoc(getClient(), prev))
|
void inboxClient.readDoc(getClient(), prev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy(async () => {
|
onDestroy(async () => {
|
||||||
void inboxClient.then((client) => client.readDoc(getClient(), _id))
|
if (issueId === undefined) return
|
||||||
|
void inboxClient.readDoc(getClient(), issueId)
|
||||||
})
|
})
|
||||||
|
|
||||||
$: _id !== undefined &&
|
$: if (issueId !== undefined && _class !== undefined) {
|
||||||
_class !== undefined &&
|
|
||||||
queryClient.query<Issue>(
|
queryClient.query<Issue>(
|
||||||
_class,
|
_class,
|
||||||
{ _id },
|
{ _id: issueId },
|
||||||
async (result) => {
|
async (result) => {
|
||||||
if (lastId !== _id) {
|
if (lastId !== issueId) {
|
||||||
await save()
|
await save()
|
||||||
}
|
}
|
||||||
;[issue] = result
|
;[issue] = result
|
||||||
@ -103,6 +114,7 @@
|
|||||||
limit: 1
|
limit: 1
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
$: canSave = title.trim().length > 0
|
$: canSave = title.trim().length > 0
|
||||||
$: hasParentIssue = issue?.attachedTo !== tracker.ids.NoParent
|
$: hasParentIssue = issue?.attachedTo !== tracker.ids.NoParent
|
||||||
|
@ -89,6 +89,7 @@ import CreateIssueTemplate from './components/templates/CreateIssueTemplate.svel
|
|||||||
import IssueExtra from './components/issues/IssueExtra.svelte'
|
import IssueExtra from './components/issues/IssueExtra.svelte'
|
||||||
import IssueStatusPresenter from './components/issues/IssueStatusPresenter.svelte'
|
import IssueStatusPresenter from './components/issues/IssueStatusPresenter.svelte'
|
||||||
import {
|
import {
|
||||||
|
getIssueIdByIdentifier,
|
||||||
getIssueTitle,
|
getIssueTitle,
|
||||||
getTitle,
|
getTitle,
|
||||||
issueIdentifierProvider,
|
issueIdentifierProvider,
|
||||||
@ -689,6 +690,7 @@ export default async (): Promise<Resources> => ({
|
|||||||
ComponentTitleProvider: getComponentTitle,
|
ComponentTitleProvider: getComponentTitle,
|
||||||
MilestoneTitleProvider: getMilestoneTitle,
|
MilestoneTitleProvider: getMilestoneTitle,
|
||||||
GetIssueId: getTitle,
|
GetIssueId: getTitle,
|
||||||
|
GetIssueIdByIdentifier: getIssueIdByIdentifier,
|
||||||
GetIssueLink: issueLinkProvider,
|
GetIssueLink: issueLinkProvider,
|
||||||
GetIssueLinkFragment: issueLinkFragmentProvider,
|
GetIssueLinkFragment: issueLinkFragmentProvider,
|
||||||
GetIssueTitle: getIssueTitle,
|
GetIssueTitle: getIssueTitle,
|
||||||
|
@ -41,7 +41,7 @@ export async function getTitle (doc: Doc): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function generateIssuePanelUri (issue: Issue): string {
|
export function generateIssuePanelUri (issue: Issue): string {
|
||||||
return getPanelURI(tracker.component.EditIssue, issue._id, issue._class, 'content')
|
return getPanelURI(tracker.component.EditIssue, issue.identifier, issue._class, 'content')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function issueLinkFragmentProvider (doc: Doc): Promise<Location> {
|
export async function issueLinkFragmentProvider (doc: Doc): Promise<Location> {
|
||||||
@ -126,3 +126,10 @@ export async function updateIssueRelation (
|
|||||||
}
|
}
|
||||||
await client.update(value, update)
|
await client.update(value, update)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getIssueIdByIdentifier (identifier: string): Promise<Ref<Issue> | undefined> {
|
||||||
|
const client = getClient()
|
||||||
|
const issue = await client.findOne(tracker.class.Issue, { identifier }, { projection: { _id: 1 } })
|
||||||
|
|
||||||
|
return issue?._id
|
||||||
|
}
|
||||||
|
@ -16,7 +16,7 @@ import { type StatusCategory, type Client, type Doc, type Ref, type Space } from
|
|||||||
import type { Asset, IntlString, Metadata, Resource } from '@hcengineering/platform'
|
import type { Asset, IntlString, Metadata, Resource } from '@hcengineering/platform'
|
||||||
import { mergeIds } from '@hcengineering/platform'
|
import { mergeIds } from '@hcengineering/platform'
|
||||||
import { type ProjectType } from '@hcengineering/task'
|
import { type ProjectType } from '@hcengineering/task'
|
||||||
import tracker, { trackerId, type IssueDraft } from '@hcengineering/tracker'
|
import tracker, { trackerId, type IssueDraft, type Issue } from '@hcengineering/tracker'
|
||||||
import { type AnyComponent, type Location } from '@hcengineering/ui'
|
import { type AnyComponent, type Location } from '@hcengineering/ui'
|
||||||
import {
|
import {
|
||||||
type CreateAggregationManagerFunc,
|
type CreateAggregationManagerFunc,
|
||||||
@ -378,7 +378,7 @@ export default mergeIds(trackerId, tracker, {
|
|||||||
IssueIdentifierProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
IssueIdentifierProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
||||||
ComponentTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
ComponentTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
||||||
MilestoneTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
MilestoneTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
||||||
GetIssueId: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
GetIssueId: '' as Resource<(doc: Doc) => Promise<string>>,
|
||||||
GetIssueLink: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
GetIssueLink: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
||||||
GetIssueLinkFragment: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
|
GetIssueLinkFragment: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
|
||||||
GetIssueTitle: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
GetIssueTitle: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
||||||
@ -393,7 +393,8 @@ export default mergeIds(trackerId, tracker, {
|
|||||||
GetVisibleFilters: '' as Resource<(filters: KeyFilter[], space?: Ref<Space>) => Promise<KeyFilter[]>>,
|
GetVisibleFilters: '' as Resource<(filters: KeyFilter[], space?: Ref<Space>) => Promise<KeyFilter[]>>,
|
||||||
IsProjectJoined: '' as Resource<(space: Space) => Promise<boolean>>,
|
IsProjectJoined: '' as Resource<(space: Space) => Promise<boolean>>,
|
||||||
IssueChatTitleProvider: '' as Resource<(object: Doc) => string>,
|
IssueChatTitleProvider: '' as Resource<(object: Doc) => string>,
|
||||||
GetIssueStatusCategories: '' as Resource<(project: ProjectType) => Array<Ref<StatusCategory>>>
|
GetIssueStatusCategories: '' as Resource<(project: ProjectType) => Array<Ref<StatusCategory>>>,
|
||||||
|
GetIssueIdByIdentifier: '' as Resource<(id: string) => Promise<Ref<Issue> | undefined>>
|
||||||
},
|
},
|
||||||
aggregation: {
|
aggregation: {
|
||||||
CreateComponentAggregationManager: '' as CreateAggregationManagerFunc,
|
CreateComponentAggregationManager: '' as CreateAggregationManagerFunc,
|
||||||
|
@ -31,26 +31,37 @@
|
|||||||
import view, { AttributeCategory } from '@hcengineering/view'
|
import view, { AttributeCategory } from '@hcengineering/view'
|
||||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||||
|
|
||||||
import { DocNavLink, ParentsNavigator, getDocAttrsInfo, getDocLabel, getDocMixins, showMenu } from '..'
|
import { DocNavLink, ParentsNavigator, getDocAttrsInfo, getDocLabel, getDocMixins, showMenu, parseLinkId } from '..'
|
||||||
import { getCollectionCounter } from '../utils'
|
import { getCollectionCounter } from '../utils'
|
||||||
import DocAttributeBar from './DocAttributeBar.svelte'
|
import DocAttributeBar from './DocAttributeBar.svelte'
|
||||||
|
|
||||||
export let _id: Ref<Doc>
|
export let _id: Ref<Doc> | string
|
||||||
export let _class: Ref<Class<Doc>>
|
export let _class: Ref<Class<Doc>>
|
||||||
export let embedded: boolean = false
|
export let embedded: boolean = false
|
||||||
export let readonly: boolean = false
|
export let readonly: boolean = false
|
||||||
|
|
||||||
let realObjectClass: Ref<Class<Doc>> = _class
|
let realObjectClass: Ref<Class<Doc>> = _class
|
||||||
let lastId: Ref<Doc> = _id
|
let lastId: Ref<Doc> | undefined
|
||||||
|
let objectId: Ref<Doc> | undefined
|
||||||
let object: Doc
|
let object: Doc
|
||||||
|
|
||||||
const pClient = getClient()
|
const pClient = getClient()
|
||||||
const hierarchy = pClient.getHierarchy()
|
const hierarchy = pClient.getHierarchy()
|
||||||
const inboxClient = getResource(notification.function.GetInboxNotificationsClient).then((res) => res())
|
const inboxClient = getResource(notification.function.GetInboxNotificationsClient).then((res) => res())
|
||||||
|
const linkProviders = pClient.getModel().findAllSync(view.mixin.LinkIdProvider, {})
|
||||||
|
|
||||||
$: read(_id)
|
$: void parseLinkId(linkProviders, _id, _class).then((res) => {
|
||||||
function read (_id: Ref<Doc>): void {
|
objectId = res ?? (_id as Ref<Doc>)
|
||||||
if (lastId !== _id) {
|
if (lastId !== undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastId = objectId
|
||||||
|
})
|
||||||
|
|
||||||
|
$: read(objectId)
|
||||||
|
|
||||||
|
function read (_id?: Ref<Doc>): void {
|
||||||
|
if (objectId && lastId && lastId !== _id) {
|
||||||
const prev = lastId
|
const prev = lastId
|
||||||
lastId = _id
|
lastId = _id
|
||||||
void inboxClient.then(async (client) => {
|
void inboxClient.then(async (client) => {
|
||||||
@ -61,14 +72,15 @@
|
|||||||
|
|
||||||
onDestroy(async () => {
|
onDestroy(async () => {
|
||||||
await inboxClient.then(async (client) => {
|
await inboxClient.then(async (client) => {
|
||||||
await client.readDoc(pClient, _id)
|
if (objectId === undefined) return
|
||||||
|
await client.readDoc(pClient, objectId)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const query = createQuery()
|
const query = createQuery()
|
||||||
$: updateQuery(_id, _class)
|
$: updateQuery(objectId, _class)
|
||||||
|
|
||||||
function updateQuery (_id: Ref<Doc>, _class: Ref<Class<Doc>>): void {
|
function updateQuery (_id?: Ref<Doc>, _class?: Ref<Class<Doc>>): void {
|
||||||
if (_id && _class) {
|
if (_id && _class) {
|
||||||
query.query(_class, { _id }, (result) => {
|
query.query(_class, { _id }, (result) => {
|
||||||
object = result[0]
|
object = result[0]
|
||||||
@ -146,12 +158,13 @@
|
|||||||
|
|
||||||
$: editorFooter = getEditorFooter(_class, object)
|
$: editorFooter = getEditorFooter(_class, object)
|
||||||
|
|
||||||
const getEditorOrDefault = reduceCalls(async function (_class: Ref<Class<Doc>>, _id: Ref<Doc>): Promise<void> {
|
const getEditorOrDefault = reduceCalls(async function (_class: Ref<Class<Doc>>, _id?: Ref<Doc>): Promise<void> {
|
||||||
|
if (objectId === undefined) return
|
||||||
await updateKeys()
|
await updateKeys()
|
||||||
mainEditor = getEditor(_class)
|
mainEditor = getEditor(_class)
|
||||||
})
|
})
|
||||||
|
|
||||||
$: void getEditorOrDefault(realObjectClass, _id)
|
$: void getEditorOrDefault(realObjectClass, objectId)
|
||||||
|
|
||||||
let title: string | undefined = undefined
|
let title: string | undefined = undefined
|
||||||
let rawTitle: string = ''
|
let rawTitle: string = ''
|
||||||
|
@ -87,7 +87,8 @@ import view, {
|
|||||||
type BuildModelOptions,
|
type BuildModelOptions,
|
||||||
type CollectionPresenter,
|
type CollectionPresenter,
|
||||||
type Viewlet,
|
type Viewlet,
|
||||||
type ViewletDescriptor
|
type ViewletDescriptor,
|
||||||
|
type LinkIdProvider
|
||||||
} from '@hcengineering/view'
|
} from '@hcengineering/view'
|
||||||
|
|
||||||
import contact, { getName, type Contact, type PersonAccount } from '@hcengineering/contact'
|
import contact, { getName, type Contact, type PersonAccount } from '@hcengineering/contact'
|
||||||
@ -1023,8 +1024,16 @@ export async function getObjectLinkFragment (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const loc = getCurrentResolvedLocation()
|
const loc = getCurrentResolvedLocation()
|
||||||
|
const idProvider = hierarchy.classHierarchyMixin(Hierarchy.mixinOrClass(object), view.mixin.LinkIdProvider)
|
||||||
|
|
||||||
|
let id: string = object._id
|
||||||
|
if (idProvider !== undefined) {
|
||||||
|
const encodeFn = await getResource(idProvider.encode)
|
||||||
|
id = await encodeFn(object)
|
||||||
|
}
|
||||||
|
|
||||||
if (hasResource(component) === true) {
|
if (hasResource(component) === true) {
|
||||||
loc.fragment = getPanelURI(component, object._id, Hierarchy.mixinOrClass(object), 'content')
|
loc.fragment = getPanelURI(component, id, Hierarchy.mixinOrClass(object), 'content')
|
||||||
}
|
}
|
||||||
return loc
|
return loc
|
||||||
}
|
}
|
||||||
@ -1448,3 +1457,43 @@ export function getCollaborationUser (): CollaborationUser {
|
|||||||
color
|
color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getObjectLinkId (
|
||||||
|
providers: LinkIdProvider[],
|
||||||
|
_id: Ref<Doc>,
|
||||||
|
_class: Ref<Class<Doc>>,
|
||||||
|
doc?: Doc
|
||||||
|
): Promise<string> {
|
||||||
|
const provider = providers.find(({ _id }) => _id === _class)
|
||||||
|
|
||||||
|
if (provider === undefined) {
|
||||||
|
return _id
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
const object = doc ?? (await client.findOne(_class, { _id }))
|
||||||
|
|
||||||
|
if (object === undefined) {
|
||||||
|
return _id
|
||||||
|
}
|
||||||
|
|
||||||
|
const encodeFn = await getResource(provider.encode)
|
||||||
|
return await encodeFn(object)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function parseLinkId<T extends Doc> (
|
||||||
|
providers: LinkIdProvider[],
|
||||||
|
id: string,
|
||||||
|
_class: Ref<Class<T>>
|
||||||
|
): Promise<Ref<T>> {
|
||||||
|
const provider = providers.find(({ _id }) => _id === _class)
|
||||||
|
|
||||||
|
if (provider === undefined) {
|
||||||
|
return id as Ref<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
const decodeFn = await getResource(provider.decode)
|
||||||
|
const _id = await decodeFn(id)
|
||||||
|
|
||||||
|
return (_id ?? id) as Ref<T>
|
||||||
|
}
|
||||||
|
@ -60,10 +60,12 @@ import {
|
|||||||
ViewAction,
|
ViewAction,
|
||||||
Viewlet,
|
Viewlet,
|
||||||
ViewletDescriptor,
|
ViewletDescriptor,
|
||||||
ViewletPreference
|
ViewletPreference,
|
||||||
|
LinkIdProvider
|
||||||
} from './types'
|
} from './types'
|
||||||
|
|
||||||
export * from './types'
|
export * from './types'
|
||||||
|
export * from './utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -103,6 +105,7 @@ const view = plugin(viewId, {
|
|||||||
AllValuesFunc: '' as Ref<Mixin<AllValuesFunc>>,
|
AllValuesFunc: '' as Ref<Mixin<AllValuesFunc>>,
|
||||||
ObjectPanel: '' as Ref<Mixin<ObjectPanel>>,
|
ObjectPanel: '' as Ref<Mixin<ObjectPanel>>,
|
||||||
LinkProvider: '' as Ref<Mixin<LinkProvider>>,
|
LinkProvider: '' as Ref<Mixin<LinkProvider>>,
|
||||||
|
LinkIdProvider: '' as Ref<Mixin<LinkIdProvider>>,
|
||||||
SpacePresenter: '' as Ref<Mixin<SpacePresenter>>,
|
SpacePresenter: '' as Ref<Mixin<SpacePresenter>>,
|
||||||
AttributeFilterPresenter: '' as Ref<Mixin<AttributeFilterPresenter>>,
|
AttributeFilterPresenter: '' as Ref<Mixin<AttributeFilterPresenter>>,
|
||||||
Aggregation: '' as Ref<Mixin<Aggregation>>,
|
Aggregation: '' as Ref<Mixin<Aggregation>>,
|
||||||
|
@ -756,6 +756,11 @@ export interface LinkProvider extends Class<Doc> {
|
|||||||
encode: Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>
|
encode: Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LinkIdProvider extends Class<Doc> {
|
||||||
|
encode: Resource<(doc: Doc) => Promise<string>>
|
||||||
|
decode: Resource<(id: string) => Promise<Ref<Doc> | undefined>>
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
|
@ -13,12 +13,12 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Ref, Doc, Class } from '@hcengineering/core'
|
import type { Class, Doc, Ref } from '@hcengineering/core'
|
||||||
|
|
||||||
export function decodeObjectURI (value: string): [Ref<Doc>, Ref<Class<Doc>>] {
|
export function decodeObjectURI (value: string): [Ref<Doc>, Ref<Class<Doc>>] {
|
||||||
return decodeURIComponent(value).split('|') as [Ref<Doc>, Ref<Class<Doc>>]
|
return decodeURIComponent(value).split('|') as [Ref<Doc>, Ref<Class<Doc>>]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function encodeObjectURI (_id: Ref<Doc>, _class: Ref<Class<Doc>>): string {
|
export function encodeObjectURI (_id: string, _class: Ref<Class<Doc>>): string {
|
||||||
return [_id, _class].join('|')
|
return [_id, _class].join('|')
|
||||||
}
|
}
|
@ -67,7 +67,8 @@
|
|||||||
NavLink,
|
NavLink,
|
||||||
accessDeniedStore,
|
accessDeniedStore,
|
||||||
migrateViewOpttions,
|
migrateViewOpttions,
|
||||||
updateFocus
|
updateFocus,
|
||||||
|
parseLinkId
|
||||||
} from '@hcengineering/view-resources'
|
} from '@hcengineering/view-resources'
|
||||||
import type { Application, NavigatorModel, SpecialNavModel, ViewConfiguration } from '@hcengineering/workbench'
|
import type { Application, NavigatorModel, SpecialNavModel, ViewConfiguration } from '@hcengineering/workbench'
|
||||||
import { getContext, onDestroy, onMount, tick } from 'svelte'
|
import { getContext, onDestroy, onMount, tick } from 'svelte'
|
||||||
@ -117,7 +118,10 @@
|
|||||||
let panelInstance: PanelInstance
|
let panelInstance: PanelInstance
|
||||||
let popupInstance: Popup
|
let popupInstance: Popup
|
||||||
|
|
||||||
|
const linkProviders = client.getModel().findAllSync(view.mixin.LinkIdProvider, {})
|
||||||
|
|
||||||
$deviceInfo.navigator.visible = getMetadata(workbench.metadata.NavigationExpandedDefault) ?? true
|
$deviceInfo.navigator.visible = getMetadata(workbench.metadata.NavigationExpandedDefault) ?? true
|
||||||
|
|
||||||
async function toggleNav (): Promise<void> {
|
async function toggleNav (): Promise<void> {
|
||||||
$deviceInfo.navigator.visible = !$deviceInfo.navigator.visible
|
$deviceInfo.navigator.visible = !$deviceInfo.navigator.visible
|
||||||
closeTooltip()
|
closeTooltip()
|
||||||
@ -202,14 +206,15 @@
|
|||||||
async function getWindowTitle (loc: Location): Promise<string | undefined> {
|
async function getWindowTitle (loc: Location): Promise<string | undefined> {
|
||||||
if (loc.fragment == null) return
|
if (loc.fragment == null) return
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
const [, _id, _class] = decodeURIComponent(loc.fragment).split('|')
|
const [, id, _class] = decodeURIComponent(loc.fragment).split('|')
|
||||||
if (_class == null) return
|
if (_class == null) return
|
||||||
|
|
||||||
const mixin = hierarchy.classHierarchyMixin(_class as Ref<Class<Doc>>, view.mixin.ObjectTitle)
|
const mixin = hierarchy.classHierarchyMixin(_class as Ref<Class<Doc>>, view.mixin.ObjectTitle)
|
||||||
if (mixin === undefined) return
|
if (mixin === undefined) return
|
||||||
const titleProvider = await getResource(mixin.titleProvider)
|
const titleProvider = await getResource(mixin.titleProvider)
|
||||||
try {
|
try {
|
||||||
return await titleProvider(client, _id as Ref<Doc>)
|
const _id = await parseLinkId(linkProviders, id, _class as Ref<Class<Doc>>)
|
||||||
|
return await titleProvider(client, _id)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
Analytics.handleError(err)
|
Analytics.handleError(err)
|
||||||
console.error(err)
|
console.error(err)
|
||||||
@ -398,7 +403,9 @@
|
|||||||
const props = decodeURIComponent(fragment).split('|')
|
const props = decodeURIComponent(fragment).split('|')
|
||||||
|
|
||||||
if (props.length >= 3) {
|
if (props.length >= 3) {
|
||||||
const doc = await client.findOne<Doc>(props[2] as Ref<Class<Doc>>, { _id: props[1] as Ref<Doc> })
|
const _class = props[2] as Ref<Class<Doc>>
|
||||||
|
const _id = await parseLinkId(linkProviders, props[1], _class)
|
||||||
|
const doc = await client.findOne<Doc>(_class, { _id })
|
||||||
|
|
||||||
if (doc !== undefined) {
|
if (doc !== undefined) {
|
||||||
const provider = ListSelectionProvider.Find(doc._id)
|
const provider = ListSelectionProvider.Find(doc._id)
|
||||||
@ -408,8 +415,8 @@
|
|||||||
})
|
})
|
||||||
openPanel(
|
openPanel(
|
||||||
props[0] as AnyComponent,
|
props[0] as AnyComponent,
|
||||||
props[1],
|
_id,
|
||||||
props[2],
|
_class,
|
||||||
(props[3] ?? undefined) as PopupAlignment,
|
(props[3] ?? undefined) as PopupAlignment,
|
||||||
(props[4] ?? undefined) as AnyComponent
|
(props[4] ?? undefined) as AnyComponent
|
||||||
)
|
)
|
||||||
|
@ -26,6 +26,10 @@ export async function documentHTMLPresenter (doc: Doc, control: TriggerControl):
|
|||||||
return `<a href="${link}">${document.name}</a>`
|
return `<a href="${link}">${document.name}</a>`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function documentLinkIdProvider (doc: Document): Promise<string> {
|
||||||
|
return getDocumentId(doc)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@ -38,6 +42,7 @@ export async function documentTextPresenter (doc: Doc): Promise<string> {
|
|||||||
export default async () => ({
|
export default async () => ({
|
||||||
function: {
|
function: {
|
||||||
DocumentHTMLPresenter: documentHTMLPresenter,
|
DocumentHTMLPresenter: documentHTMLPresenter,
|
||||||
DocumentTextPresenter: documentTextPresenter
|
DocumentTextPresenter: documentTextPresenter,
|
||||||
|
DocumentLinkIdProvider: documentLinkIdProvider
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hcengineering/platform": "^0.6.11",
|
"@hcengineering/platform": "^0.6.11",
|
||||||
|
"@hcengineering/core": "^0.6.32",
|
||||||
"@hcengineering/server-core": "^0.6.1",
|
"@hcengineering/server-core": "^0.6.1",
|
||||||
"@hcengineering/server-notification": "^0.6.1"
|
"@hcengineering/server-notification": "^0.6.1"
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import { Doc } from '@hcengineering/core'
|
||||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||||
import { plugin } from '@hcengineering/platform'
|
import { plugin } from '@hcengineering/platform'
|
||||||
import { Presenter } from '@hcengineering/server-notification'
|
import { Presenter } from '@hcengineering/server-notification'
|
||||||
@ -18,6 +19,7 @@ export const serverDocumentId = 'server-document' as Plugin
|
|||||||
export default plugin(serverDocumentId, {
|
export default plugin(serverDocumentId, {
|
||||||
function: {
|
function: {
|
||||||
DocumentHTMLPresenter: '' as Resource<Presenter>,
|
DocumentHTMLPresenter: '' as Resource<Presenter>,
|
||||||
DocumentTextPresenter: '' as Resource<Presenter>
|
DocumentTextPresenter: '' as Resource<Presenter>,
|
||||||
|
DocumentLinkIdProvider: '' as Resource<(doc: Doc) => Promise<string>>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
"@hcengineering/view": "^0.6.13",
|
"@hcengineering/view": "^0.6.13",
|
||||||
"@hcengineering/text": "^0.6.5",
|
"@hcengineering/text": "^0.6.5",
|
||||||
"@hcengineering/contact": "^0.6.24",
|
"@hcengineering/contact": "^0.6.24",
|
||||||
|
"@hcengineering/server-view": "^0.6.0",
|
||||||
"web-push": "~3.6.7"
|
"web-push": "~3.6.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,6 @@ import notification, {
|
|||||||
Collaborators,
|
Collaborators,
|
||||||
CommonInboxNotification,
|
CommonInboxNotification,
|
||||||
DocNotifyContext,
|
DocNotifyContext,
|
||||||
encodeObjectURI,
|
|
||||||
InboxNotification,
|
InboxNotification,
|
||||||
MentionInboxNotification,
|
MentionInboxNotification,
|
||||||
notificationId,
|
notificationId,
|
||||||
@ -80,6 +79,9 @@ import serverNotification, {
|
|||||||
import { stripTags } from '@hcengineering/text'
|
import { stripTags } from '@hcengineering/text'
|
||||||
import { workbenchId } from '@hcengineering/workbench'
|
import { workbenchId } from '@hcengineering/workbench'
|
||||||
import webpush, { WebPushError } from 'web-push'
|
import webpush, { WebPushError } from 'web-push'
|
||||||
|
import { encodeObjectURI } from '@hcengineering/view'
|
||||||
|
import serverView from '@hcengineering/server-view'
|
||||||
|
|
||||||
import { Content, NotifyResult, NotifyParams } from './types'
|
import { Content, NotifyResult, NotifyParams } from './types'
|
||||||
import {
|
import {
|
||||||
getHTMLPresenter,
|
getHTMLPresenter,
|
||||||
@ -554,12 +556,24 @@ export async function createPushFromInbox (
|
|||||||
cache.set(senderPerson._id, senderPerson)
|
cache.set(senderPerson._id, senderPerson)
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = [
|
const linkProviders = control.modelDb.findAllSync(serverView.mixin.ServerLinkIdProvider, {})
|
||||||
workbenchId,
|
const provider = linkProviders.find(({ _id }) => _id === attachedToClass)
|
||||||
control.workspace.workspaceUrl,
|
|
||||||
notificationId,
|
let id: string = attachedTo
|
||||||
encodeObjectURI(attachedTo, attachedToClass)
|
|
||||||
]
|
if (provider !== undefined) {
|
||||||
|
const encodeFn = await getResource(provider.encode)
|
||||||
|
const doc = cache.get(attachedTo) ?? (await control.findAll(attachedToClass, { _id: attachedTo }))[0]
|
||||||
|
|
||||||
|
if (doc === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.set(doc._id, doc)
|
||||||
|
id = await encodeFn(doc, control)
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = [workbenchId, control.workspace.workspaceUrl, notificationId, encodeObjectURI(id, attachedToClass)]
|
||||||
await createPushNotification(control, targetUser, title, body, _id, senderPerson, path)
|
await createPushNotification(control, targetUser, title, body, _id, senderPerson, path)
|
||||||
return control.txFactory.createTxCreateDoc(notification.class.BrowserNotification, notification.space.Notifications, {
|
return control.txFactory.createTxCreateDoc(notification.class.BrowserNotification, notification.space.Notifications, {
|
||||||
user: targetUser,
|
user: targetUser,
|
||||||
|
@ -110,7 +110,8 @@ export default async () => ({
|
|||||||
VacancyHTMLPresenter: vacancyHTMLPresenter,
|
VacancyHTMLPresenter: vacancyHTMLPresenter,
|
||||||
VacancyTextPresenter: vacancyTextPresenter,
|
VacancyTextPresenter: vacancyTextPresenter,
|
||||||
ApplicationHTMLPresenter: applicationHTMLPresenter,
|
ApplicationHTMLPresenter: applicationHTMLPresenter,
|
||||||
ApplicationTextPresenter: applicationTextPresenter
|
ApplicationTextPresenter: applicationTextPresenter,
|
||||||
|
LinkIdProvider: getSequenceId
|
||||||
},
|
},
|
||||||
trigger: {
|
trigger: {
|
||||||
OnRecruitUpdate
|
OnRecruitUpdate
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hcengineering/platform": "^0.6.11",
|
"@hcengineering/platform": "^0.6.11",
|
||||||
"@hcengineering/server-notification": "^0.6.1",
|
"@hcengineering/server-notification": "^0.6.1",
|
||||||
"@hcengineering/server-core": "^0.6.1"
|
"@hcengineering/server-core": "^0.6.1",
|
||||||
|
"@hcengineering/core": "^0.6.32"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import { Doc } from '@hcengineering/core'
|
||||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||||
import { plugin } from '@hcengineering/platform'
|
import { plugin } from '@hcengineering/platform'
|
||||||
import { TriggerFunc } from '@hcengineering/server-core'
|
import { TriggerFunc } from '@hcengineering/server-core'
|
||||||
@ -31,7 +32,8 @@ export default plugin(serverRecruitId, {
|
|||||||
ApplicationHTMLPresenter: '' as Resource<Presenter>,
|
ApplicationHTMLPresenter: '' as Resource<Presenter>,
|
||||||
ApplicationTextPresenter: '' as Resource<Presenter>,
|
ApplicationTextPresenter: '' as Resource<Presenter>,
|
||||||
VacancyHTMLPresenter: '' as Resource<Presenter>,
|
VacancyHTMLPresenter: '' as Resource<Presenter>,
|
||||||
VacancyTextPresenter: '' as Resource<Presenter>
|
VacancyTextPresenter: '' as Resource<Presenter>,
|
||||||
|
LinkIdProvider: '' as Resource<(doc: Doc) => Promise<string>>
|
||||||
},
|
},
|
||||||
trigger: {
|
trigger: {
|
||||||
OnRecruitUpdate: '' as Resource<TriggerFunc>
|
OnRecruitUpdate: '' as Resource<TriggerFunc>
|
||||||
|
@ -501,12 +501,17 @@ function updateIssueParentEstimations (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function issueLinkIdProvider (issue: Issue): Promise<string> {
|
||||||
|
return issue.identifier
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||||
export default async () => ({
|
export default async () => ({
|
||||||
function: {
|
function: {
|
||||||
IssueHTMLPresenter: issueHTMLPresenter,
|
IssueHTMLPresenter: issueHTMLPresenter,
|
||||||
IssueTextPresenter: issueTextPresenter,
|
IssueTextPresenter: issueTextPresenter,
|
||||||
IssueNotificationContentProvider: getIssueNotificationContent
|
IssueNotificationContentProvider: getIssueNotificationContent,
|
||||||
|
IssueLinkIdProvider: issueLinkIdProvider
|
||||||
},
|
},
|
||||||
trigger: {
|
trigger: {
|
||||||
OnIssueUpdate,
|
OnIssueUpdate,
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hcengineering/platform": "^0.6.11",
|
"@hcengineering/platform": "^0.6.11",
|
||||||
"@hcengineering/server-notification": "^0.6.1",
|
"@hcengineering/server-notification": "^0.6.1",
|
||||||
"@hcengineering/server-core": "^0.6.1"
|
"@hcengineering/server-core": "^0.6.1",
|
||||||
|
"@hcengineering/core": "^0.6.32"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import { Doc } from '@hcengineering/core'
|
||||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||||
import { plugin } from '@hcengineering/platform'
|
import { plugin } from '@hcengineering/platform'
|
||||||
import { TriggerFunc } from '@hcengineering/server-core'
|
import { TriggerFunc } from '@hcengineering/server-core'
|
||||||
@ -30,7 +31,8 @@ export default plugin(serverTrackerId, {
|
|||||||
function: {
|
function: {
|
||||||
IssueHTMLPresenter: '' as Resource<Presenter>,
|
IssueHTMLPresenter: '' as Resource<Presenter>,
|
||||||
IssueTextPresenter: '' as Resource<Presenter>,
|
IssueTextPresenter: '' as Resource<Presenter>,
|
||||||
IssueNotificationContentProvider: '' as Resource<NotificationContentProvider>
|
IssueNotificationContentProvider: '' as Resource<NotificationContentProvider>,
|
||||||
|
IssueLinkIdProvider: '' as Resource<(doc: Doc) => Promise<string>>
|
||||||
},
|
},
|
||||||
trigger: {
|
trigger: {
|
||||||
OnIssueUpdate: '' as Resource<TriggerFunc>,
|
OnIssueUpdate: '' as Resource<TriggerFunc>,
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hcengineering/server-notification": "^0.6.1",
|
"@hcengineering/server-notification": "^0.6.1",
|
||||||
"@hcengineering/platform": "^0.6.11",
|
"@hcengineering/platform": "^0.6.11",
|
||||||
|
"@hcengineering/core": "^0.6.32",
|
||||||
"@hcengineering/server-core": "^0.6.1"
|
"@hcengineering/server-core": "^0.6.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,18 +14,26 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||||
import { TriggerFunc } from '@hcengineering/server-core'
|
import { TriggerControl, TriggerFunc } from '@hcengineering/server-core'
|
||||||
import { plugin } from '@hcengineering/platform'
|
import { plugin } from '@hcengineering/platform'
|
||||||
|
import { Class, Doc, Mixin, Ref } from '@hcengineering/core'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export const serverViewId = 'server-view' as Plugin
|
export const serverViewId = 'server-view' as Plugin
|
||||||
|
|
||||||
|
export interface ServerLinkIdProvider extends Class<Doc> {
|
||||||
|
encode: Resource<(doc: Doc, control: TriggerControl) => Promise<string>>
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export default plugin(serverViewId, {
|
export default plugin(serverViewId, {
|
||||||
|
mixin: {
|
||||||
|
ServerLinkIdProvider: '' as Ref<Mixin<ServerLinkIdProvider>>
|
||||||
|
},
|
||||||
trigger: {
|
trigger: {
|
||||||
OnCustomAttributeRemove: '' as Resource<TriggerFunc>
|
OnCustomAttributeRemove: '' as Resource<TriggerFunc>
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user