Merge branch 'develop' of https://github.com/hcengineering/platform into UBERF-10525/mta

This commit is contained in:
Artem Savchenko 2025-05-14 14:05:23 +07:00
commit ea5f103f32
22 changed files with 179 additions and 125 deletions

View File

@ -798,7 +798,7 @@ importers:
version: file:projects/pod-github.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(gcp-metadata@5.3.0(encoding@0.1.13))(snappy@7.2.2)(socks@2.8.3)(utf-8-validate@6.0.4)(y-prosemirror@1.2.15(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)(y-protocols@1.0.6(yjs@13.6.23))(yjs@13.6.23))
'@rush-temp/pod-gmail':
specifier: file:./projects/pod-gmail.tgz
version: file:projects/pod-gmail.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(encoding@0.1.13)
version: file:projects/pod-gmail.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.4)
'@rush-temp/pod-inbound-mail':
specifier: file:./projects/pod-inbound-mail.tgz
version: file:projects/pod-inbound-mail.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))
@ -4880,7 +4880,7 @@ packages:
version: 0.0.0
'@rush-temp/pod-gmail@file:projects/pod-gmail.tgz':
resolution: {integrity: sha512-BuAMVBU4uCu4UQTXGYHfxeTgt9/A7OOZL3HA/pXq2X1lkL2w2xGt7M19tt2qBuAGAPJtSsZTMmEDVBRd6hgrWA==, tarball: file:projects/pod-gmail.tgz}
resolution: {integrity: sha512-5rn2Y1X7GtTiOKR5vbkqo5oB/zEeQXOiSw4nTPL8xp/Jx66PDnGjkwkHqBlberol5hG2/ns8im3Zqxql5O2VXw==, tarball: file:projects/pod-gmail.tgz}
version: 0.0.0
'@rush-temp/pod-inbound-mail@file:projects/pod-inbound-mail.tgz':
@ -22373,7 +22373,7 @@ snapshots:
- utf-8-validate
- y-prosemirror
'@rush-temp/pod-gmail@file:projects/pod-gmail.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(encoding@0.1.13)':
'@rush-temp/pod-gmail@file:projects/pod-gmail.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.4)':
dependencies:
'@tsconfig/node16': 1.0.4
'@types/cors': 2.8.17
@ -22382,6 +22382,7 @@ snapshots:
'@types/node': 20.11.19
'@types/sanitize-html': 2.15.0
'@types/uuid': 8.3.4
'@types/ws': 8.5.11
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
cors: 2.8.5
@ -22409,6 +22410,7 @@ snapshots:
ts-node-dev: 2.0.0(@types/node@20.11.19)(typescript@5.3.3)
typescript: 5.3.3
uuid: 8.3.2
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
transitivePeerDependencies:
- '@babel/core'
- '@jest/types'
@ -22416,9 +22418,11 @@ snapshots:
- '@swc/wasm'
- babel-jest
- babel-plugin-macros
- bufferutil
- encoding
- node-notifier
- supports-color
- utf-8-validate
'@rush-temp/pod-inbound-mail@file:projects/pod-inbound-mail.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))':
dependencies:

View File

@ -45,6 +45,7 @@ import core, {
type Domain,
type IndexingConfiguration,
type Ref,
type Blob,
type Timestamp,
type Tx,
type TxCUD
@ -227,6 +228,8 @@ export class TReaction extends TAttachedDoc implements Reaction {
@Prop(TypeString(), activity.string.Emoji)
emoji!: string
image?: Ref<Blob>
@Prop(TypePersonId(), view.string.Created)
createBy!: PersonId
}

View File

@ -187,6 +187,7 @@ class KeyValueClientImpl implements KeyValueClient {
errMessage.includes('network')
if (!isNetworkError || timeout < Date.now()) {
console.error(`KVS request failed for url ${url}`, err.message)
throw err
}

View File

@ -73,7 +73,6 @@ export const EmojiNode = Node.create<EmojiNodeOptions>({
addNodeView () {
return ({ node, HTMLAttributes }) => {
console.log('Node view')
const container = document.createElement('span')
const containerAttributes = mergeAttributes(
{

View File

@ -161,9 +161,9 @@
--theme-bg-divider-color: #282834;
--theme-mention-bg-color: rgba(55, 122, 230, 0.1);
--theme-mention-focused-bg-color: rgba(55, 122, 230, 0.2);
--theme-broken-mention-bg-color: rgba(202, 66, 66, .2);
--theme-broken-mention-color: rgb(202, 66, 66);
--theme-broken-mention-focused-bg-color: rgba(202, 66, 66, .3);
--theme-broken-mention-color: rgba(255, 255, 255, .4);
--theme-broken-mention-bg-color: rgba(255, 255, 255, .12);
--theme-broken-mention-focused-bg-color: rgba(255, 255, 255, .2);
--theme-trans-color: rgba(255, 255, 255, .3);
--theme-darker-color: rgba(255, 255, 255, .4);
@ -453,9 +453,9 @@
--theme-bg-divider-color: #E3E3E5;
--theme-mention-bg-color: rgba(55, 122, 230, 0.1);
--theme-mention-focused-bg-color: rgba(55, 122, 230, 0.2);
--theme-broken-mention-bg-color: rgba(202, 66, 66, .2);
--theme-broken-mention-color: rgb(202, 66, 66);
--theme-broken-mention-focused-bg-color: rgba(202, 66, 66, .3);
--theme-broken-mention-bg-color: rgba(0, 0, 0, .12);
--theme-broken-mention-color: rgba(0, 0, 0, .4);
--theme-broken-mention-focused-bg-color: rgba(0, 0, 0, .2);
--theme-link-preview-bg-color: #E5E8F0;
--theme-link-preview-description-color: #5A667E;

View File

@ -15,14 +15,13 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { Reaction } from '@hcengineering/activity'
import { Doc, getCurrentAccount, PersonId } from '@hcengineering/core'
import { Doc, getCurrentAccount, PersonId, Ref, Blob } from '@hcengineering/core'
import { IconAdd, showPopup, tooltip } from '@hcengineering/ui'
import { includesAny } from '@hcengineering/contact'
import emojiPlugin from '@hcengineering/emoji'
import ReactionsTooltip from './ReactionsTooltip.svelte'
import { updateDocReactions } from '../../utils'
import { getResource } from '@hcengineering/platform'
import { getBlobRef } from '@hcengineering/presentation'
export let reactions: Reaction[] = []
@ -32,19 +31,19 @@
const dispatch = createEventDispatcher()
const me = getCurrentAccount()
let reactionsPersons = new Map<string, PersonId[]>()
let reactionsPersons = new Map<string, { persons: PersonId[], image?: Ref<Blob> }>()
let opened: boolean = false
$: {
reactionsPersons.clear()
reactions.forEach((r) => {
const persons = reactionsPersons.get(r.emoji) ?? []
reactionsPersons.set(r.emoji, [...persons, r.createBy])
const emojiInfo = reactionsPersons.get(r.emoji) ?? { persons: [], image: r.image }
reactionsPersons.set(r.emoji, { persons: [...emojiInfo.persons, r.createBy], image: r.image })
})
reactionsPersons = reactionsPersons
}
function getClickHandler (emoji: string): ((e: CustomEvent) => void) | undefined {
function getClickHandler (emoji: { text: string, image?: Ref<Blob> }): ((e: CustomEvent) => void) | undefined {
if (readonly) return
return (e: CustomEvent) => {
e.stopPropagation()
@ -59,37 +58,35 @@
ev.stopPropagation()
opened = true
showPopup(emojiPlugin.component.EmojiPopup, {}, ev.target as HTMLElement, async (emoji) => {
if (emoji?.text !== undefined) await updateDocReactions(reactions, object, emoji.text)
if (emoji?.text !== undefined) {
await updateDocReactions(reactions, object, emoji.text, emoji.image)
}
opened = false
})
}
</script>
<div class="hulyReactions-container">
{#each [...reactionsPersons] as [emoji, persons]}
{#each [...reactionsPersons] as [emoji, emojiInfo]}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="hulyReactions-button"
class:highlight={includesAny(persons, me.socialIds)}
class:highlight={includesAny(emojiInfo.persons, me.socialIds)}
class:cursor-pointer={!readonly}
use:tooltip={{ component: ReactionsTooltip, props: { reactionAccounts: persons } }}
on:click={getClickHandler(emoji)}
use:tooltip={{ component: ReactionsTooltip, props: { reactionAccounts: emojiInfo.persons } }}
on:click={getClickHandler({ text: emoji, image: emojiInfo.image })}
>
{#await getResource(emojiPlugin.functions.GetCustomEmoji) then getCustomEmojiFunction}
{@const customEmoji = getCustomEmojiFunction(emoji)}
<span class="emoji">
{#if customEmoji === undefined}
{emoji}
{:else}
{@const alt = emoji}
{#await getBlobRef(customEmoji.image) then blobSrc}
<img src={blobSrc.src} {alt} />
{/await}
{/if}
</span>
{/await}
<span class="counter">{persons.length}</span>
<span class="emoji">
{#if emojiInfo.image === undefined || emojiInfo.image == null}
{emoji}
{:else}
{#await getBlobRef(emojiInfo.image) then blobSrc}
<img src={blobSrc.src} alt={emoji} />
{/await}
{/if}
</span>
<span class="counter">{emojiInfo.persons.length}</span>
</div>
{/each}
{#if object && reactionsPersons.size > 0 && !readonly}

View File

@ -51,7 +51,7 @@
const handleClick = (ev: CustomEvent) => {
if (readonly) return
void updateDocReactions(reactions, object, ev.detail)
void updateDocReactions(reactions, object, ev.detail?.text, ev.detail?.image)
}
</script>

View File

@ -1,5 +1,5 @@
import type { ActivityMessage, Reaction } from '@hcengineering/activity'
import core, { getCurrentAccount, isOtherHour, type Doc, type Ref, type Space } from '@hcengineering/core'
import core, { getCurrentAccount, isOtherHour, type Doc, type Ref, type Space, type Blob } from '@hcengineering/core'
import { getClient, isSpace } from '@hcengineering/presentation'
import {
closePopup,
@ -15,7 +15,12 @@ import { get } from 'svelte/store'
import { savedMessagesStore } from './activity'
import activity from './plugin'
export async function updateDocReactions (reactions: Reaction[], object?: Doc, emoji?: string): Promise<void> {
export async function updateDocReactions (
reactions: Reaction[],
object?: Doc,
emoji?: string,
image?: Ref<Blob>
): Promise<void> {
if (emoji === undefined || object === undefined) {
return
}
@ -28,6 +33,7 @@ export async function updateDocReactions (reactions: Reaction[], object?: Doc, e
if (reaction == null) {
await client.addCollection(activity.class.Reaction, object.space, object._id, object._class, 'reactions', {
emoji,
image,
createBy: currentAccount.primarySocialId
})
} else {
@ -61,7 +67,7 @@ export async function addReactionAction (
closePopup()
showPopup(emojiPlugin.component.EmojiPopup, {}, element, (emoji) => {
if (emoji?.text !== undefined) void updateDocReactions(reactions, message, emoji.text)
if (emoji?.text !== undefined) void updateDocReactions(reactions, message, emoji.text, emoji.image)
params?.onClose?.()
})
params?.onOpen?.()

View File

@ -25,7 +25,8 @@ import {
type RelatedDocument,
Timestamp,
Tx,
TxCUD
TxCUD,
Blob
} from '@hcengineering/core'
import type { Asset, IntlString, Plugin, Resource } from '@hcengineering/platform'
import { plugin } from '@hcengineering/platform'
@ -220,6 +221,7 @@ export interface Reaction extends AttachedDoc {
attachedTo: Ref<ActivityMessage>
attachedToClass: Ref<Class<ActivityMessage>>
emoji: string
image?: Ref<Blob>
createBy: PersonId
}

View File

@ -16,7 +16,7 @@
import { Attachment } from '@hcengineering/attachment'
import { Ref, type WithLookup } from '@hcengineering/core'
import { ListSelectionProvider } from '@hcengineering/view-resources'
import { Scroller, updatePopup } from '@hcengineering/ui'
import { updatePopup } from '@hcengineering/ui'
import { AttachmentImageSize } from '../types'
import AttachmentPreview from './AttachmentPreview.svelte'
@ -43,7 +43,7 @@
</script>
{#if attachments.length}
<Scroller contentDirection={'horizontal'} horizontal gap={'gap-3'} scrollSnap>
<div class="gallery">
{#each attachments as attachment}
<AttachmentPreview
value={attachment}
@ -54,5 +54,13 @@
on:open={(res) => (attachmentPopupId = res.detail)}
/>
{/each}
</Scroller>
</div>
{/if}
<style lang="scss">
.gallery {
display: grid;
grid-gap: 0.75rem;
grid-template-columns: repeat(auto-fill, 20rem);
}
</style>

View File

@ -13,6 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import { Analytics } from '@hcengineering/analytics'
import attachment, { Attachment, AttachmentsEvents } from '@hcengineering/attachment'
import contact from '@hcengineering/contact'
import core, { BlobMetadata, Doc, PersonId, Ref, generateId, type Blob, type Space } from '@hcengineering/core'
@ -34,11 +35,9 @@
defaultRefActions,
getModelRefActions
} from '@hcengineering/text-editor-resources'
import { AnySvelteComponent, getEventPositionElement, getPopupPositionElement, navigate } from '@hcengineering/ui'
import { type FileUploadCallbackParams, uploadFiles } from '@hcengineering/uploader'
import view from '@hcengineering/view'
import { getCollaborationUser, getObjectId, getObjectLinkFragment } from '@hcengineering/view-resources'
import { Analytics } from '@hcengineering/analytics'
import { AnySvelteComponent, getEventPositionElement, getPopupPositionElement } from '@hcengineering/ui'
import { uploadFiles, type FileUploadCallbackParams } from '@hcengineering/uploader'
import { getCollaborationUser, getObjectId, openDoc } from '@hcengineering/view-resources'
import AttachmentsGrid from './AttachmentsGrid.svelte'
@ -324,8 +323,7 @@
on:open-document={async (event) => {
const doc = await client.findOne(event.detail._class, { _id: event.detail._id })
if (doc != null) {
const location = await getObjectLinkFragment(client.getHierarchy(), doc, {}, view.component.EditDoc)
navigate(location)
await openDoc(client.getHierarchy(), doc)
}
}}
on:focus

View File

@ -13,13 +13,11 @@
// limitations under the License.
-->
<script lang="ts">
import { Doc, Class, Ref, updateAttribute } from '@hcengineering/core'
import { Class, Doc, Ref, updateAttribute } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import { createQuery, getAttribute, getClient, KeyedAttribute } from '@hcengineering/presentation'
import { navigate } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { getObjectLinkFragment } from '@hcengineering/view-resources'
import { openDoc } from '@hcengineering/view-resources'
import { createEventDispatcher } from 'svelte'
import AttachmentStyledBox from './AttachmentStyledBox.svelte'
@ -137,8 +135,7 @@
save(object, description)
const doc = await client.findOne(event.detail._class, { _id: event.detail._id })
if (doc != null) {
const location = await getObjectLinkFragment(client.getHierarchy(), doc, {}, view.component.EditDoc)
navigate(location)
await openDoc(client.getHierarchy(), doc)
}
}}
/>

View File

@ -20,9 +20,7 @@
import { getClient } from '@hcengineering/presentation'
import { Heading } from '@hcengineering/text-editor'
import { TableOfContents } from '@hcengineering/text-editor-resources'
import { navigate } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { getObjectLinkFragment } from '@hcengineering/view-resources'
import { openDoc } from '@hcengineering/view-resources'
import { createEventDispatcher } from 'svelte'
import ContentEditor from './ContentEditor.svelte'
@ -107,8 +105,7 @@
on:open-document={async (event) => {
const doc = await client.findOne(event.detail._class, { _id: event.detail._id })
if (doc != null) {
const location = await getObjectLinkFragment(client.getHierarchy(), doc, {}, view.component.EditDoc)
navigate(location)
await openDoc(client.getHierarchy(), doc)
}
}}
bind:this={editor}

View File

@ -13,48 +13,47 @@
// limitations under the License.
-->
<script lang="ts">
import { createEventDispatcher, onDestroy, tick } from 'svelte'
import { merge } from 'effector'
import { type Ref, type Blob, generateId } from '@hcengineering/core'
import { getResource, setPlatformStatus, unknownError } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import view from '@hcengineering/view'
import attachment, { Attachment } from '@hcengineering/attachment'
import documents, { DocumentState } from '@hcengineering/controlled-documents'
import { type Blob, type Ref, generateId } from '@hcengineering/core'
import { getResource, setPlatformStatus, unknownError } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { Editor, Heading } from '@hcengineering/text-editor'
import {
CollaboratorEditor,
TableOfContents,
TableOfContentsContent,
FocusExtension,
HeadingsExtension,
IsEmptyContentExtension,
NodeHighlightExtension,
NodeHighlightType,
highlightUpdateCommand,
getNodeElement
TableOfContents,
TableOfContentsContent,
getNodeElement,
highlightUpdateCommand
} from '@hcengineering/text-editor-resources'
import { navigate, EditBox, Scroller, Label } from '@hcengineering/ui'
import { getCollaborationUser, getObjectLinkFragment } from '@hcengineering/view-resources'
import { EditBox, Label, Scroller } from '@hcengineering/ui'
import { getCollaborationUser, openDoc } from '@hcengineering/view-resources'
import { merge } from 'effector'
import { createEventDispatcher, onDestroy, tick } from 'svelte'
import plugin from '../../plugin'
import {
$areDocumentCommentPopupsOpened as areDocumentCommentPopupsOpened,
$controlledDocument as controlledDocument,
$isEditable as isEditable,
$documentCommentHighlightedLocation as documentCommentHighlightedLocation,
$areDocumentCommentPopupsOpened as arePopupsOpened,
$canAddDocumentComments as canAddDocumentComments,
$canViewDocumentComments as canViewDocumentComments,
$controlledDocument as controlledDocument,
$documentCommentHighlightedLocation as documentCommentHighlightedLocation,
$documentComments as documentComments,
documentCommentsDisplayRequested,
documentCommentsHighlightUpdated,
documentCommentsLocationNavigateRequested,
$documentReleasedVersions as documentReleasedVersions
$documentReleasedVersions as documentReleasedVersions,
$isEditable as isEditable
} from '../../stores/editors/document'
import DocumentTitle from './DocumentTitle.svelte'
import DocumentPrintTitlePage from '../print/DocumentPrintTitlePage.svelte'
import { syncDocumentMetaTitle } from '../../utils'
import DocumentPrintTitlePage from '../print/DocumentPrintTitlePage.svelte'
import DocumentTitle from './DocumentTitle.svelte'
const client = getClient()
const hierarchy = client.getHierarchy()
@ -295,8 +294,7 @@
on:open-document={async (event) => {
const doc = await client.findOne(event.detail._class, { _id: event.detail._id })
if (doc != null) {
const location = await getObjectLinkFragment(client.getHierarchy(), doc, {}, view.component.EditDoc)
navigate(location)
await openDoc(client.getHierarchy(), doc)
}
}}
attachFile={async (file) => {

View File

@ -15,6 +15,7 @@
//
-->
<script lang="ts">
import { Analytics } from '@hcengineering/analytics'
import attachment, { Attachment } from '@hcengineering/attachment'
import core, { Doc, Ref, WithLookup, generateId, type Blob } from '@hcengineering/core'
import { Document, DocumentEvents } from '@hcengineering/document'
@ -35,7 +36,6 @@
TimeSince,
createFocusManager,
getPlatformColorDef,
navigate,
showPopup,
themeStore
} from '@hcengineering/ui'
@ -45,14 +45,13 @@
IconPicker,
ParentsNavigator,
RelationsEditor,
getObjectLinkFragment,
openDoc,
restrictionStore,
showMenu
} from '@hcengineering/view-resources'
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
import { Analytics } from '@hcengineering/analytics'
import { starDocument, unstarDocument, unlockContent } from '..'
import { starDocument, unlockContent, unstarDocument } from '..'
import document from '../plugin'
import { getDocumentUrl } from '../utils'
import DocumentEditor from './DocumentEditor.svelte'
@ -389,8 +388,7 @@
on:open-document={async (event) => {
const doc = await client.findOne(event.detail._class, { _id: event.detail._id })
if (doc != null) {
const location = await getObjectLinkFragment(client.getHierarchy(), doc, {}, view.component.EditDoc)
navigate(location)
await openDoc(client.getHierarchy(), doc)
}
}}
on:loaded={() => {

View File

@ -101,13 +101,22 @@
}
const sendEmoji = (emoji: EmojiWithGroup): void => {
selected = isCustomEmoji(emoji) ? emoji.image : emoji.emoji
addFrequentlyEmojis(emoji)
dispatch('close', {
text: selected,
codes: isCustomEmoji(emoji) ? emoji.image : emoji.hexcode.split('-').map((hc) => parseInt(hc, 16)),
image: isCustomEmoji(emoji) ? emoji.image : undefined
})
if (isCustomEmoji(emoji)) {
selected = emoji.image
dispatch('close', {
text: emoji.shortcode,
codes: emoji.image,
image: emoji.image
})
} else {
selected = emoji.emoji
dispatch('close', {
text: emoji.emoji,
codes: emoji.hexcode.split('-').map((hc) => parseInt(hc, 16)),
image: undefined
})
}
}
const selectedEmoji = (event: CustomEvent<EmojiWithGroup>): void => {

View File

@ -89,7 +89,6 @@ export function getCustomEmoji (shortcode: string | undefined): CustomEmoji | un
if (isCustomEmoji(e)) return e.shortcode === pureShortcode
return false
}) as CustomEmoji
console.log('CustomEmoji', shortcode, result)
return result
}

View File

@ -108,6 +108,7 @@
const newValue = !$isSharingEnabled
const audio = newValue && $isShareWithSound
await setShare(newValue, audio)
dispatch('close')
}
async function leave (): Promise<void> {

View File

@ -408,7 +408,12 @@ export async function getReferenceLabel<T extends Doc> (
const identifier = (await labelProviderFn?.(client, id, doc)) ?? ''
const title = (await titleProviderFn?.(client, id, doc)) ?? ''
const label = identifier !== '' && title !== '' && identifier !== title ? `${identifier} ${title}` : title ?? ''
const label =
identifier !== '' && title !== '' && identifier !== title
? `${identifier} ${title}`
: title !== ''
? title
: identifier
return label
}
@ -451,7 +456,6 @@ export async function getTargetObjectFromUrl (
urlOrLocation: string | Location
): Promise<{ _id: Ref<Doc>, _class: Ref<Class<Doc>> } | undefined> {
const client = getClient()
const hierarchy = client.getHierarchy()
let location: Location
if (typeof urlOrLocation === 'string') {
@ -469,19 +473,43 @@ export async function getTargetObjectFromUrl (
const appAlias = (location.path[2] ?? '').trim()
if (!(appAlias.length > 0)) return
const excludedApps = getMetadata(workbench.metadata.ExcludedApplications) ?? []
const apps: Application[] = client
.getModel()
.findAllSync<Application>(workbench.class.Application, { hidden: false, _id: { $nin: excludedApps } })
const app = apps.find((p) => p.alias === appAlias)
const locationResolver = app?.locationResolver
const locationDataResolver = app?.locationDataResolver
if (app?.locationResolver === undefined) return
const locationResolverFn = await getResource(app.locationResolver)
const resolvedLocation = await locationResolverFn(location)
if ((location.fragment ?? '') !== '') {
const obj = await getObjectFromFragment(location.fragment ?? '')
if (obj !== undefined) return obj
}
const locationParts = decodeURIComponent(resolvedLocation?.loc?.fragment ?? '').split('|')
if (locationResolver !== undefined) {
const locationResolverFn = await getResource(locationResolver)
const resolvedLocation = await locationResolverFn(location)
const obj = await getObjectFromFragment(resolvedLocation?.loc?.fragment ?? '')
if (obj !== undefined) return obj
}
if (locationDataResolver !== undefined) {
const locationDataResolverFn = await getResource(locationDataResolver)
const locationData = await locationDataResolverFn(location)
if (locationData.objectId !== undefined && locationData.objectClass !== undefined) {
return { _id: locationData.objectId, _class: locationData.objectClass }
}
}
}
async function getObjectFromFragment (
fragment: string
): Promise<{ _id: Ref<Doc>, _class: Ref<Class<Doc>> } | undefined> {
const client = getClient()
const hierarchy = client.getHierarchy()
const locationParts = decodeURIComponent(fragment).split('|')
const id = locationParts[1] as Ref<Doc>
const objectclass = locationParts[2] as Ref<Class<Doc>>
if (id === undefined || objectclass === undefined) return

View File

@ -399,7 +399,10 @@
}
}
loc.query = resolved.loc.query ?? loc.query ?? currentQuery ?? resolved.defaultLocation.query
loc.fragment = resolved.loc.fragment ?? loc.fragment ?? resolved.defaultLocation.fragment
loc.fragment =
(loc.fragment ?? '') !== '' && resolved.loc.fragment === resolved.defaultLocation.fragment
? loc.fragment
: resolved.loc.fragment ?? resolved.defaultLocation.fragment
return loc
}

View File

@ -14,7 +14,7 @@
"build": "compile",
"build:watch": "compile",
"test": "jest --passWithNoTests --silent",
"_phase:bundle": "rushx bundle",
"_phase:bundle": "rushx bundle --external=ws",
"_phase:docker-build": "rushx docker:build",
"_phase:docker-staging": "rushx docker:staging",
"bundle": "node ../../../common/scripts/esbuild.js --external=ws",
@ -38,6 +38,7 @@
"@types/sanitize-html": "^2.15.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"@types/ws": "^8.5.11",
"esbuild": "^0.24.2",
"eslint": "^8.54.0",
"eslint-config-standard-with-typescript": "^40.0.0",
@ -84,6 +85,7 @@
"gaxios": "^5.0.1",
"jwt-simple": "^0.5.6",
"uuid": "^8.3.2",
"ws": "^8.18.0",
"@hcengineering/analytics-service": "^0.6.0",
"sanitize-html": "^2.15.0"
}

View File

@ -93,35 +93,39 @@ export class GmailController {
this.ctx.info('Workspaces with integrations', { count: workspaceIds.size })
for (const workspace of workspaceIds) {
const wsToken = serviceToken(workspace)
const accountClient = getAccountClient(wsToken)
try {
const wsToken = serviceToken(workspace)
const accountClient = getAccountClient(wsToken)
const tokens = await getWorkspaceTokens(accountClient, workspace)
await limiter.add(async () => {
const info = await accountClient.getWorkspaceInfo()
const tokens = await getWorkspaceTokens(accountClient, workspace)
await limiter.add(async () => {
const info = await accountClient.getWorkspaceInfo()
if (info === undefined) {
this.ctx.info('workspace not found', { workspaceUuid: workspace })
return
}
if (!isActiveMode(info.mode)) {
this.ctx.info('workspace is not active', { workspaceUuid: workspace })
return
}
this.ctx.info('Use stored tokens', { count: tokens.length })
const startPromise = this.startWorkspace(workspace, tokens)
const timeoutPromise = new Promise<void>((resolve) => {
setTimeout(() => {
resolve()
}, 60000)
if (info === undefined) {
this.ctx.info('workspace not found', { workspaceUuid: workspace })
return
}
if (!isActiveMode(info.mode)) {
this.ctx.info('workspace is not active', { workspaceUuid: workspace })
return
}
this.ctx.info('Use stored tokens', { count: tokens.length })
const startPromise = this.startWorkspace(workspace, tokens)
const timeoutPromise = new Promise<void>((resolve) => {
setTimeout(() => {
resolve()
}, 60000)
})
await Promise.race([startPromise, timeoutPromise])
})
await Promise.race([startPromise, timeoutPromise])
})
} catch (err: any) {
this.ctx.error('Failed to create workspace client', { workspaceUuid: workspace, error: err.message })
}
}
await limiter.waitProcessing()
} catch (err: any) {
this.ctx.error('Failed to start existing integrations', err)
this.ctx.error('Failed to start existing integrations', { error: err.message })
}
}