mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
[Part 2]: Provide more spaces to queries/finds (#6230)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
b5ee82cf1a
commit
7860f06aae
@ -687,7 +687,11 @@ export function isAdminUser (): boolean {
|
||||
}
|
||||
|
||||
export function isSpace (space: Doc): space is Space {
|
||||
return getClient().getHierarchy().isDerived(space._class, core.class.Space)
|
||||
return isSpaceClass(space._class)
|
||||
}
|
||||
|
||||
export function isSpaceClass (_class: Ref<Class<Doc>>): boolean {
|
||||
return getClient().getHierarchy().isDerived(_class, core.class.Space)
|
||||
}
|
||||
|
||||
export function setPresentationCookie (token: string, workspaceId: string): void {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import activity, { type ActivityMessage, type SavedMessage } from '@hcengineering/activity'
|
||||
import { type Ref, SortingOrder, type WithLookup } from '@hcengineering/core'
|
||||
import core, { type Ref, SortingOrder, type WithLookup } from '@hcengineering/core'
|
||||
import { writable } from 'svelte/store'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
|
||||
@ -14,7 +14,7 @@ export function loadSavedMessages (): void {
|
||||
if (client !== undefined) {
|
||||
savedMessagesQuery.query(
|
||||
activity.class.SavedMessage,
|
||||
{},
|
||||
{ space: core.space.Workspace },
|
||||
(res) => {
|
||||
savedMessagesStore.set(res.filter(({ $lookup }) => $lookup?.attachedTo !== undefined))
|
||||
},
|
||||
|
@ -13,20 +13,26 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import activity, { ActivityExtension, ActivityMessage, DisplayActivityMessage } from '@hcengineering/activity'
|
||||
import activity, {
|
||||
ActivityExtension,
|
||||
ActivityMessage,
|
||||
ActivityReference,
|
||||
DisplayActivityMessage,
|
||||
WithReferences
|
||||
} from '@hcengineering/activity'
|
||||
import { Doc, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { createQuery, getClient, isSpace } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Grid, Label, Spinner, location, Lazy } from '@hcengineering/ui'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
|
||||
import ActivityExtensionComponent from './ActivityExtension.svelte'
|
||||
import ActivityFilter from './ActivityFilter.svelte'
|
||||
import { combineActivityMessages } from '../activityMessagesUtils'
|
||||
import { canGroupMessages, getMessageFromLoc } from '../utils'
|
||||
import { combineActivityMessages, sortActivityMessages } from '../activityMessagesUtils'
|
||||
import { canGroupMessages, getMessageFromLoc, getSpace } from '../utils'
|
||||
import ActivityMessagePresenter from './activity-message/ActivityMessagePresenter.svelte'
|
||||
import { messageInFocus } from '../activity'
|
||||
|
||||
export let object: Doc
|
||||
export let object: WithReferences<Doc>
|
||||
export let showCommenInput: boolean = true
|
||||
export let transparent: boolean = false
|
||||
export let focusIndex: number = -1
|
||||
@ -34,12 +40,17 @@
|
||||
|
||||
const client = getClient()
|
||||
const activityMessagesQuery = createQuery()
|
||||
const refsQuery = createQuery()
|
||||
|
||||
let extensions: ActivityExtension[] = []
|
||||
|
||||
let filteredMessages: DisplayActivityMessage[] = []
|
||||
let activityMessages: ActivityMessage[] = []
|
||||
let isLoading = false
|
||||
let allMessages: ActivityMessage[] = []
|
||||
let messages: ActivityMessage[] = []
|
||||
let refs: ActivityReference[] = []
|
||||
|
||||
let isMessagesLoading = false
|
||||
let isRefsLoading = true
|
||||
|
||||
let activityBox: HTMLElement | undefined
|
||||
let selectedMessageId: Ref<ActivityMessage> | undefined = undefined
|
||||
@ -163,16 +174,38 @@
|
||||
extensions = res
|
||||
})
|
||||
|
||||
// Load references from other spaces separately because they can have any different spaces
|
||||
$: if ((object.references ?? 0) > 0) {
|
||||
refsQuery.query(
|
||||
activity.class.ActivityReference,
|
||||
{ attachedTo: object._id, space: { $ne: getSpace(object) } },
|
||||
(res) => {
|
||||
refs = res
|
||||
isRefsLoading = false
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
createdOn: SortingOrder.Ascending
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
isRefsLoading = false
|
||||
refsQuery.unsubscribe()
|
||||
}
|
||||
|
||||
$: allMessages = sortActivityMessages(messages.concat(refs))
|
||||
|
||||
async function updateActivityMessages (objectId: Ref<Doc>, order: SortingOrder): Promise<void> {
|
||||
isLoading = true
|
||||
isMessagesLoading = true
|
||||
|
||||
const res = activityMessagesQuery.query(
|
||||
activity.class.ActivityMessage,
|
||||
{ attachedTo: objectId, space: isSpace(object) ? object._id : object.space },
|
||||
{ attachedTo: objectId, space: getSpace(object) },
|
||||
(result: ActivityMessage[]) => {
|
||||
void combineActivityMessages(result, order).then((messages) => {
|
||||
activityMessages = messages
|
||||
isLoading = false
|
||||
void combineActivityMessages(result, order).then((res) => {
|
||||
messages = res
|
||||
isMessagesLoading = false
|
||||
})
|
||||
},
|
||||
{
|
||||
@ -182,10 +215,11 @@
|
||||
}
|
||||
)
|
||||
if (!res) {
|
||||
isLoading = false
|
||||
isMessagesLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
$: isLoading = isMessagesLoading || isRefsLoading
|
||||
$: areMessagesLoaded = !isLoading && filteredMessages.length > 0
|
||||
|
||||
$: if (activityBox && areMessagesLoaded) {
|
||||
@ -206,7 +240,7 @@
|
||||
{/if}
|
||||
</span>
|
||||
<ActivityFilter
|
||||
messages={activityMessages}
|
||||
messages={allMessages}
|
||||
{object}
|
||||
on:update={(e) => {
|
||||
filteredMessages = e.detail
|
||||
|
@ -16,8 +16,8 @@
|
||||
import { Label, tooltip } from '@hcengineering/ui'
|
||||
import { DocNotifyContext } from '@hcengineering/notification'
|
||||
import activity, { ActivityMessage } from '@hcengineering/activity'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Doc, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Doc } from '@hcengineering/core'
|
||||
import { getDocLinkTitle, getDocTitle, ObjectIcon } from '@hcengineering/view-resources'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import contact from '@hcengineering/contact'
|
||||
@ -25,54 +25,44 @@
|
||||
import ActivityMessagePreview from './ActivityMessagePreview.svelte'
|
||||
|
||||
export let context: DocNotifyContext
|
||||
export let object: ActivityMessage
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const parentQuery = createQuery()
|
||||
|
||||
let parentMessage: ActivityMessage | undefined = undefined
|
||||
let title: string | undefined = undefined
|
||||
let object: Doc | undefined = undefined
|
||||
|
||||
$: parentQuery.query(activity.class.ActivityMessage, { _id: context.attachedTo as Ref<ActivityMessage> }, (res) => {
|
||||
parentMessage = res[0]
|
||||
})
|
||||
|
||||
$: parentMessage &&
|
||||
client.findOne(parentMessage.attachedToClass, { _id: parentMessage.attachedTo }).then((res) => {
|
||||
object = res
|
||||
})
|
||||
let doc: Doc | undefined = undefined
|
||||
|
||||
$: object &&
|
||||
getDocLinkTitle(client, object._id, object._class, object).then((res) => {
|
||||
client.findOne(object.attachedToClass, { _id: object.attachedTo, space: object.space }).then((res) => {
|
||||
doc = res
|
||||
})
|
||||
|
||||
$: doc &&
|
||||
getDocLinkTitle(client, doc._id, doc._class, doc).then((res) => {
|
||||
title = res
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if parentMessage}
|
||||
<span class="flex-presenter flex-gap-1 font-semi-bold">
|
||||
<Label label={(parentMessage?.replies ?? 0) > 0 ? activity.string.Thread : activity.string.Message} />
|
||||
{#if title}
|
||||
<span class="lower">
|
||||
<Label label={activity.string.In} />
|
||||
</span>
|
||||
{#if object}
|
||||
{#await getDocTitle(client, object._id, object._class, object) then tooltipLabel}
|
||||
<span
|
||||
class="flex-presenter flex-gap-0-5"
|
||||
use:tooltip={tooltipLabel ? { label: getEmbeddedLabel(tooltipLabel) } : undefined}
|
||||
>
|
||||
<ObjectIcon
|
||||
value={object}
|
||||
size={hierarchy.isDerived(object._class, contact.class.Person) ? 'tiny' : 'small'}
|
||||
/>
|
||||
{title}
|
||||
</span>
|
||||
{/await}
|
||||
{/if}
|
||||
<span class="flex-presenter flex-gap-1 font-semi-bold">
|
||||
<Label label={(object?.replies ?? 0) > 0 ? activity.string.Thread : activity.string.Message} />
|
||||
{#if title}
|
||||
<span class="lower">
|
||||
<Label label={activity.string.In} />
|
||||
</span>
|
||||
{#if doc}
|
||||
{#await getDocTitle(client, doc._id, doc._class, doc) then tooltipLabel}
|
||||
<span
|
||||
class="flex-presenter flex-gap-0-5"
|
||||
use:tooltip={tooltipLabel ? { label: getEmbeddedLabel(tooltipLabel) } : undefined}
|
||||
>
|
||||
<ObjectIcon value={doc} size={hierarchy.isDerived(doc._class, contact.class.Person) ? 'tiny' : 'small'} />
|
||||
{title}
|
||||
</span>
|
||||
{/await}
|
||||
{/if}
|
||||
</span>
|
||||
<span class="font-normal">
|
||||
<ActivityMessagePreview value={parentMessage} readonly type="content-only" />
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
</span>
|
||||
<span class="font-normal">
|
||||
<ActivityMessagePreview value={object} {doc} readonly type="content-only" />
|
||||
</span>
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
import activity from '../../plugin'
|
||||
|
||||
export let doc: Doc | undefined
|
||||
export let value: DisplayActivityMessage
|
||||
export let readonly = false
|
||||
export let type: ActivityMessagePreviewType = 'full'
|
||||
@ -44,7 +45,8 @@
|
||||
type,
|
||||
readonly,
|
||||
actions,
|
||||
space
|
||||
space,
|
||||
doc
|
||||
}}
|
||||
on:click
|
||||
/>
|
||||
|
@ -13,22 +13,10 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createQuery, MessageViewer } from '@hcengineering/presentation'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import activity, { ActivityReference } from '@hcengineering/activity'
|
||||
import { MessageViewer } from '@hcengineering/presentation'
|
||||
import { ActivityReference } from '@hcengineering/activity'
|
||||
|
||||
export let _id: Ref<ActivityReference> | undefined = undefined
|
||||
export let value: ActivityReference | undefined = undefined
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
$: if (value === undefined && _id !== undefined) {
|
||||
query.query(activity.class.ActivityReference, { _id }, (res) => {
|
||||
value = res.shift()
|
||||
})
|
||||
} else {
|
||||
query.unsubscribe()
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
|
@ -16,12 +16,12 @@
|
||||
import type { Doc } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { DocReferencePresenter } from '@hcengineering/view-resources'
|
||||
import activity from '../../plugin'
|
||||
|
||||
import { isActivityMessage } from '../../activityMessagesUtils'
|
||||
import view from '@hcengineering/view'
|
||||
import { Icon, Label } from '@hcengineering/ui'
|
||||
|
||||
import activity from '../../plugin'
|
||||
import { isActivityMessage } from '../../activityMessagesUtils'
|
||||
|
||||
export let value: Doc | undefined
|
||||
|
||||
const client = getClient()
|
||||
@ -31,7 +31,7 @@
|
||||
$: showParent = isActivityMessage(value)
|
||||
|
||||
$: isActivityMessage(value) &&
|
||||
client.findOne(value.attachedToClass, { _id: value.attachedTo }).then((res) => {
|
||||
client.findOne(value.attachedToClass, { _id: value.attachedTo, space: value.space }).then((res) => {
|
||||
parentObject = res
|
||||
})
|
||||
</script>
|
||||
|
@ -88,13 +88,17 @@
|
||||
attributeModel = model
|
||||
})
|
||||
|
||||
async function getParentMessage (_class: Ref<Class<Doc>>, _id: Ref<Doc>): Promise<ActivityMessage | undefined> {
|
||||
async function getParentMessage (
|
||||
_class: Ref<Class<Doc>>,
|
||||
_id: Ref<Doc>,
|
||||
space: Ref<Space>
|
||||
): Promise<ActivityMessage | undefined> {
|
||||
if (hierarchy.isDerived(_class, activity.class.ActivityMessage)) {
|
||||
return await client.findOne(activity.class.ActivityMessage, { _id: _id as Ref<ActivityMessage> })
|
||||
return await client.findOne(activity.class.ActivityMessage, { _id: _id as Ref<ActivityMessage>, space })
|
||||
}
|
||||
}
|
||||
|
||||
$: void getParentMessage(value.attachedToClass, value.attachedTo).then((res) => {
|
||||
$: void getParentMessage(value.attachedToClass, value.attachedTo, value.space).then((res) => {
|
||||
parentMessage = res as DisplayActivityMessage
|
||||
})
|
||||
|
||||
@ -150,6 +154,7 @@
|
||||
|
||||
const _id = parentMessage ? parentMessage.attachedTo : message.attachedTo
|
||||
const _class = parentMessage ? parentMessage.attachedToClass : message.attachedToClass
|
||||
const space = parentMessage ? parentMessage.space : message.space
|
||||
|
||||
if (doc !== undefined && doc._id === _id) {
|
||||
parentObject = doc
|
||||
@ -163,7 +168,7 @@
|
||||
return
|
||||
}
|
||||
|
||||
parentObjectQuery.query(_class, { _id }, (res) => {
|
||||
parentObjectQuery.query(_class, { _id, space }, (res) => {
|
||||
parentObject = res[0]
|
||||
})
|
||||
}
|
||||
|
@ -32,6 +32,7 @@
|
||||
import DocUpdateMessageContent from './DocUpdateMessageContent.svelte'
|
||||
import DocUpdateMessageAttributes from './DocUpdateMessageAttributes.svelte'
|
||||
|
||||
export let doc: Doc | undefined
|
||||
export let value: DisplayDocUpdateMessage
|
||||
export let readonly = false
|
||||
export let type: ActivityMessagePreviewType = 'full'
|
||||
@ -65,15 +66,20 @@
|
||||
attributeModel = model
|
||||
})
|
||||
|
||||
$: viewlet?.component && loadObject(value.objectId, value.objectClass)
|
||||
$: viewlet?.component && loadObject(value.objectId, value.objectClass, value.space, doc)
|
||||
|
||||
async function loadObject (_id: Ref<Doc>, _class: Ref<Class<Doc>>, space: Ref<Space>, doc?: Doc): Promise<void> {
|
||||
if (doc?._id === _id) {
|
||||
object = doc
|
||||
return
|
||||
}
|
||||
|
||||
async function loadObject (_id: Ref<Doc>, _class: Ref<Class<Doc>>): Promise<void> {
|
||||
const isObjectRemoved = await checkIsObjectRemoved(client, _id, _class)
|
||||
|
||||
if (isObjectRemoved) {
|
||||
object = await buildRemovedDoc(client, _id, _class)
|
||||
} else {
|
||||
objectQuery.query(_class, { _id }, (res) => {
|
||||
objectQuery.query(_class, { _id, space }, (res) => {
|
||||
object = res[0]
|
||||
})
|
||||
}
|
||||
|
@ -14,18 +14,19 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Reaction } from '@hcengineering/activity'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Ref, Space } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
|
||||
import activity from '../../plugin'
|
||||
|
||||
export let _id: Ref<Reaction>
|
||||
export let space: Ref<Space>
|
||||
export let value: Reaction | undefined = undefined
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
$: value === undefined &&
|
||||
query.query(activity.class.Reaction, { _id }, (res) => {
|
||||
query.query(activity.class.Reaction, { _id, space }, (res) => {
|
||||
value = res[0]
|
||||
})
|
||||
</script>
|
||||
|
@ -16,7 +16,7 @@
|
||||
import activity, { ActivityMessage, Reaction } from '@hcengineering/activity'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
|
||||
import { updateDocReactions } from '../../utils'
|
||||
import { getSpace, updateDocReactions } from '../../utils'
|
||||
import Reactions from './Reactions.svelte'
|
||||
|
||||
export let object: ActivityMessage | undefined
|
||||
@ -30,9 +30,13 @@
|
||||
$: hasReactions = object?.reactions && object.reactions > 0
|
||||
|
||||
$: if (object && hasReactions) {
|
||||
reactionsQuery.query(activity.class.Reaction, { attachedTo: object._id }, (res: Reaction[]) => {
|
||||
reactions = res
|
||||
})
|
||||
reactionsQuery.query(
|
||||
activity.class.Reaction,
|
||||
{ attachedTo: object._id, space: getSpace(object) },
|
||||
(res: Reaction[]) => {
|
||||
reactions = res
|
||||
}
|
||||
)
|
||||
} else {
|
||||
reactionsQuery.unsubscribe()
|
||||
}
|
||||
|
@ -37,7 +37,7 @@
|
||||
$: if (message && hasReactions) {
|
||||
reactionsQuery.query(
|
||||
activity.class.Reaction,
|
||||
{ attachedTo: message._id },
|
||||
{ attachedTo: message._id, space: message.space },
|
||||
(res: Reaction[]) => {
|
||||
reactions = res
|
||||
|
||||
|
@ -1,6 +1,13 @@
|
||||
import type { ActivityMessage, Reaction } from '@hcengineering/activity'
|
||||
import core, { getCurrentAccount, isOtherHour, type Doc, type Ref, type TxOperations } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import core, {
|
||||
getCurrentAccount,
|
||||
isOtherHour,
|
||||
type Doc,
|
||||
type Ref,
|
||||
type TxOperations,
|
||||
type Space
|
||||
} from '@hcengineering/core'
|
||||
import { getClient, isSpace } from '@hcengineering/presentation'
|
||||
import {
|
||||
EmojiPopup,
|
||||
closePopup,
|
||||
@ -58,7 +65,7 @@ export async function addReactionAction (
|
||||
const client = getClient()
|
||||
const reactions: Reaction[] =
|
||||
(message.reactions ?? 0) > 0
|
||||
? await client.findAll<Reaction>(activity.class.Reaction, { attachedTo: message._id })
|
||||
? await client.findAll<Reaction>(activity.class.Reaction, { attachedTo: message._id, space: message.space })
|
||||
: []
|
||||
const element = getEventPositionElement(ev)
|
||||
|
||||
@ -169,3 +176,7 @@ export function shouldScrollToActivity (): boolean {
|
||||
const loc = getCurrentResolvedLocation()
|
||||
return getMessageFromLoc(loc) !== undefined
|
||||
}
|
||||
|
||||
export function getSpace (doc: Doc): Ref<Space> {
|
||||
return isSpace(doc) ? doc._id : doc.space
|
||||
}
|
||||
|
@ -235,6 +235,10 @@ export interface UserMentionInfo extends AttachedDoc {
|
||||
content: string
|
||||
}
|
||||
|
||||
export type WithReferences<T extends Doc> = T & {
|
||||
references?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
@ -25,11 +25,10 @@ import {
|
||||
type Space,
|
||||
type Timestamp
|
||||
} from '@hcengineering/core'
|
||||
|
||||
import { derived, get, type Readable, writable } from 'svelte/store'
|
||||
import { type ActivityMessage } from '@hcengineering/activity'
|
||||
import activity, { type ActivityMessage, type ActivityReference } from '@hcengineering/activity'
|
||||
import attachment from '@hcengineering/attachment'
|
||||
import { combineActivityMessages } from '@hcengineering/activity-resources'
|
||||
import { combineActivityMessages, sortActivityMessages } from '@hcengineering/activity-resources'
|
||||
import { type ChatMessage } from '@hcengineering/chunter'
|
||||
import notification, { type DocNotifyContext } from '@hcengineering/notification'
|
||||
|
||||
@ -70,6 +69,7 @@ export class ChannelDataProvider implements IChannelDataProvider {
|
||||
|
||||
private readonly metadataQuery = createQuery(true)
|
||||
private readonly tailQuery = createQuery(true)
|
||||
private readonly refsQuery = createQuery(true)
|
||||
|
||||
private chatId: Ref<Doc> | undefined = undefined
|
||||
private readonly msgClass: Ref<Class<ActivityMessage>>
|
||||
@ -79,12 +79,14 @@ export class ChannelDataProvider implements IChannelDataProvider {
|
||||
public readonly metadataStore = writable<MessageMetadata[]>([])
|
||||
private readonly tailStore = writable<ActivityMessage[]>([])
|
||||
private readonly chunksStore = writable<Chunk[]>([])
|
||||
public readonly refsStore = writable<ActivityReference[]>([])
|
||||
|
||||
private readonly isInitialLoadingStore = writable(false)
|
||||
private readonly isInitialLoadedStore = writable(false)
|
||||
private readonly isTailLoading = writable(false)
|
||||
|
||||
readonly isTailLoaded = writable(false)
|
||||
readonly isRefsLoading = writable(false)
|
||||
|
||||
public datesStore = writable<Timestamp[]>([])
|
||||
public newTimestampStore = writable<Timestamp | undefined>(undefined)
|
||||
@ -92,8 +94,8 @@ export class ChannelDataProvider implements IChannelDataProvider {
|
||||
public isLoadingMoreStore = writable(false)
|
||||
|
||||
public isLoadingStore = derived(
|
||||
[this.isInitialLoadedStore, this.isTailLoading],
|
||||
([initialLoaded, tailLoading]) => !initialLoaded || tailLoading
|
||||
[this.isInitialLoadedStore, this.isTailLoading, this.isRefsLoading],
|
||||
([initialLoaded, tailLoading, isRefsLoading]) => !initialLoaded || tailLoading || isRefsLoading
|
||||
)
|
||||
|
||||
private readonly backwardNextStore = writable<Chunk | undefined>(undefined)
|
||||
@ -107,8 +109,9 @@ export class ChannelDataProvider implements IChannelDataProvider {
|
||||
|
||||
private nextChunkAdding = false
|
||||
|
||||
public messagesStore = derived([this.chunksStore, this.tailStore], ([chunks, tail]) => {
|
||||
return [...chunks.map(({ data }) => data).flat(), ...tail]
|
||||
public messagesStore = derived([this.chunksStore, this.tailStore, this.refsStore], ([chunks, tail, refs]) => {
|
||||
const data = chunks.map(({ data, to, from }) => mergeWithRefs(data, refs, from, to))
|
||||
return [...data.flat(), ...mergeWithRefs(tail, refs, tail[0]?.createdOn)]
|
||||
})
|
||||
|
||||
public canLoadNextForwardStore = derived([this.messagesStore, this.forwardNextStore], ([messages, forwardNext]) => {
|
||||
@ -123,18 +126,20 @@ export class ChannelDataProvider implements IChannelDataProvider {
|
||||
chatId: Ref<Doc>,
|
||||
_class: Ref<Class<ActivityMessage>>,
|
||||
selectedMsgId: Ref<ActivityMessage> | undefined,
|
||||
loadAll = false
|
||||
loadAll = false,
|
||||
withRefs = false
|
||||
) {
|
||||
this.chatId = chatId
|
||||
this.msgClass = _class
|
||||
this.selectedMsgId = selectedMsgId
|
||||
void this.loadData(loadAll)
|
||||
void this.loadData(loadAll, withRefs)
|
||||
}
|
||||
|
||||
public destroy (): void {
|
||||
this.clearData()
|
||||
this.metadataQuery.unsubscribe()
|
||||
this.tailQuery.unsubscribe()
|
||||
this.refsQuery.unsubscribe()
|
||||
}
|
||||
|
||||
public canLoadMore (mode: LoadMode, timestamp?: Timestamp): boolean {
|
||||
@ -168,11 +173,29 @@ export class ChannelDataProvider implements IChannelDataProvider {
|
||||
this.clearMessages()
|
||||
}
|
||||
|
||||
private async loadData (loadAll = false): Promise<void> {
|
||||
loadRefs (): void {
|
||||
// Load references from other spaces separately because they can have any different spaces
|
||||
this.refsQuery.query(
|
||||
activity.class.ActivityReference,
|
||||
{ attachedTo: this.chatId, space: { $ne: this.space } },
|
||||
(res) => {
|
||||
this.refsStore.set(res)
|
||||
this.isRefsLoading.set(false)
|
||||
},
|
||||
{ sort: { createdOn: SortingOrder.Ascending } }
|
||||
)
|
||||
}
|
||||
|
||||
private async loadData (loadAll = false, withRefs = false): Promise<void> {
|
||||
if (this.chatId === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
if (withRefs && this.msgClass === activity.class.ActivityMessage) {
|
||||
this.isRefsLoading.set(true)
|
||||
this.loadRefs()
|
||||
}
|
||||
|
||||
this.metadataQuery.query(
|
||||
this.msgClass,
|
||||
{ attachedTo: this.chatId, space: this.space },
|
||||
@ -622,3 +645,21 @@ export class ChannelDataProvider implements IChannelDataProvider {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function mergeWithRefs (
|
||||
messages: ActivityMessage[],
|
||||
refs: ActivityReference[],
|
||||
from?: Timestamp,
|
||||
to?: Timestamp
|
||||
): ActivityMessage[] {
|
||||
if (from === undefined) return messages
|
||||
if (refs.length === 0) return messages
|
||||
|
||||
const refsFiltered = refs.filter(
|
||||
({ createdOn }) => (createdOn ?? 0) >= from && (to === undefined || (createdOn ?? 0) <= to)
|
||||
)
|
||||
|
||||
if (refsFiltered.length === 0) return messages
|
||||
|
||||
return sortActivityMessages(messages.concat(refsFiltered))
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { Class, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import notification, { DocNotifyContext } from '@hcengineering/notification'
|
||||
import activity, { ActivityMessage, ActivityMessagesFilter } from '@hcengineering/activity'
|
||||
import activity, { ActivityMessage, ActivityMessagesFilter, WithReferences } from '@hcengineering/activity'
|
||||
import { getClient, isSpace } from '@hcengineering/presentation'
|
||||
import { getMessageFromLoc, messageInFocus } from '@hcengineering/activity-resources'
|
||||
import { location as locationStore } from '@hcengineering/ui'
|
||||
@ -57,6 +57,8 @@
|
||||
dataProvider = undefined
|
||||
})
|
||||
|
||||
let refsLoaded = false
|
||||
|
||||
$: isDocChannel = !hierarchy.isDerived(object._class, chunter.class.ChunterSpace)
|
||||
$: _class = isDocChannel ? activity.class.ActivityMessage : chunter.class.ChatMessage
|
||||
$: collection = isDocChannel ? 'comments' : 'messages'
|
||||
@ -78,11 +80,17 @@
|
||||
attachedTo: object._id,
|
||||
user: getCurrentAccount()._id
|
||||
}))
|
||||
|
||||
const hasRefs = ((object as WithReferences<Doc>).references ?? 0) > 0
|
||||
refsLoaded = hasRefs
|
||||
const space = isSpace(object) ? object._id : object.space
|
||||
dataProvider = new ChannelDataProvider(ctx, space, attachedTo, _class, selectedMessageId, loadAll)
|
||||
dataProvider = new ChannelDataProvider(ctx, space, attachedTo, _class, selectedMessageId, loadAll, hasRefs)
|
||||
}
|
||||
}
|
||||
|
||||
$: if (dataProvider && !refsLoaded && ((object as WithReferences<Doc>).references ?? 0) > 0) {
|
||||
dataProvider.loadRefs()
|
||||
refsLoaded = true
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if dataProvider}
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { getDocTitle } from '@hcengineering/view-resources'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Channel } from '@hcengineering/chunter'
|
||||
import { ActivityMessagesFilter } from '@hcengineering/activity'
|
||||
import { ActivityMessagesFilter, WithReferences } from '@hcengineering/activity'
|
||||
import contact from '@hcengineering/contact'
|
||||
|
||||
import Header from './Header.svelte'
|
||||
@ -27,7 +27,7 @@
|
||||
|
||||
export let _id: Ref<Doc>
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let object: Doc | undefined
|
||||
export let object: WithReferences<Doc> | undefined
|
||||
export let allowClose: boolean = false
|
||||
export let canOpen: boolean = false
|
||||
export let withAside: boolean = false
|
||||
@ -78,6 +78,6 @@
|
||||
on:close
|
||||
>
|
||||
{#if object}
|
||||
<PinnedMessages {_id} {_class} space={object.space} on:select />
|
||||
<PinnedMessages {_id} {_class} space={object.space} withRefs={(object.references ?? 0) > 0} on:select />
|
||||
{/if}
|
||||
</Header>
|
||||
|
@ -646,10 +646,7 @@
|
||||
return
|
||||
}
|
||||
|
||||
const lastMetadata = metadata[metadata.length - 1]
|
||||
const lastMessage = displayMessages[displayMessages.length - 1]
|
||||
|
||||
if (lastMetadata._id !== lastMessage._id) {
|
||||
if (!$isTailLoadedStore) {
|
||||
showScrollDownButton = true
|
||||
} else if (element != null) {
|
||||
const { scrollHeight, scrollTop, offsetHeight } = element
|
||||
|
@ -28,13 +28,16 @@
|
||||
export let space: Ref<Space>
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let _id: Ref<Doc>
|
||||
export let withRefs = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const pinnedQuery = createQuery()
|
||||
const pinnedThreadsQuery = createQuery()
|
||||
const pinnedRefsQuery = createQuery()
|
||||
|
||||
let pinnedMessagesCount = 0
|
||||
let pinnedThreadsCount = 0
|
||||
let refsCount = 0
|
||||
|
||||
$: channelSpace = getChannelSpace(_class, _id, space)
|
||||
$: pinnedQuery.query(
|
||||
@ -55,10 +58,21 @@
|
||||
{ projection: { _id: 1, space: 1, objectId: 1, isPinned: 1 } }
|
||||
)
|
||||
|
||||
$: if (withRefs) {
|
||||
pinnedRefsQuery.query(
|
||||
activity.class.ActivityReference,
|
||||
{ attachedTo: _id, isPinned: true, space: { $ne: channelSpace } },
|
||||
(res) => {
|
||||
refsCount = res.total
|
||||
},
|
||||
{ limit: 1, total: true }
|
||||
)
|
||||
}
|
||||
|
||||
function openMessagesPopup (ev: MouseEvent): void {
|
||||
showPopup(
|
||||
PinnedMessagesPopup,
|
||||
{ attachedTo: _id, attachedToClass: _class, space: channelSpace },
|
||||
{ attachedTo: _id, attachedToClass: _class, space: channelSpace, withRefs },
|
||||
eventToHTMLElement(ev),
|
||||
(result) => {
|
||||
if (result == null) return
|
||||
@ -67,7 +81,7 @@
|
||||
)
|
||||
}
|
||||
|
||||
$: count = pinnedMessagesCount + pinnedThreadsCount
|
||||
$: count = pinnedMessagesCount + pinnedThreadsCount + refsCount
|
||||
</script>
|
||||
|
||||
{#if count > 0}
|
||||
|
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import activity, { ActivityMessage } from '@hcengineering/activity'
|
||||
import activity, { ActivityMessage, ActivityReference } from '@hcengineering/activity'
|
||||
import { ActivityMessagePresenter, sortActivityMessages } from '@hcengineering/activity-resources'
|
||||
import { ActionIcon, IconClose } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@ -26,14 +26,17 @@
|
||||
export let attachedTo: Ref<Doc>
|
||||
export let attachedToClass: Ref<Class<Doc>>
|
||||
export let space: Ref<Space>
|
||||
export let withRefs = false
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
const pinnedQuery = createQuery()
|
||||
const pinnedThreadsQuery = createQuery()
|
||||
const pinnedRefsQuery = createQuery()
|
||||
|
||||
let pinnedMessages: ActivityMessage[] = []
|
||||
let pinnedThreads: ThreadMessage[] = []
|
||||
let pinnedRefs: ActivityReference[] = []
|
||||
|
||||
$: pinnedQuery.query(
|
||||
activity.class.ActivityMessage,
|
||||
@ -51,6 +54,16 @@
|
||||
}
|
||||
)
|
||||
|
||||
$: if (withRefs) {
|
||||
pinnedRefsQuery.query(
|
||||
activity.class.ActivityReference,
|
||||
{ attachedTo, isPinned: true, space: { $ne: space } },
|
||||
(res) => {
|
||||
pinnedRefs = res
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
$: if (pinnedMessages.length === 0 && pinnedThreads.length === 0) {
|
||||
dispatch('close', undefined)
|
||||
}
|
||||
@ -59,7 +72,10 @@
|
||||
await client.update(message, { isPinned: false })
|
||||
}
|
||||
|
||||
$: displayMessages = sortActivityMessages(pinnedMessages.concat(pinnedThreads), SortingOrder.Descending)
|
||||
$: displayMessages = sortActivityMessages(
|
||||
pinnedMessages.concat(pinnedThreads).concat(pinnedRefs),
|
||||
SortingOrder.Descending
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="antiPopup vScroll popup">
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { Person, PersonAccount } from '@hcengineering/contact'
|
||||
import { personAccountByIdStore, personByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Class, Doc, getCurrentAccount, Ref, WithLookup } from '@hcengineering/core'
|
||||
import { Class, Doc, getCurrentAccount, Ref, Space, WithLookup } from '@hcengineering/core'
|
||||
import { getClient, MessageViewer } from '@hcengineering/presentation'
|
||||
import { AttachmentDocList, AttachmentImageSize } from '@hcengineering/attachment-resources'
|
||||
import { getDocLinkTitle } from '@hcengineering/view-resources'
|
||||
@ -80,7 +80,7 @@
|
||||
$: person = account?.person !== undefined ? $personByIdStore.get(account.person) : undefined
|
||||
|
||||
$: value !== undefined &&
|
||||
getParentMessage(value.attachedToClass, value.attachedTo).then((res) => {
|
||||
getParentMessage(value.attachedToClass, value.attachedTo, value.space).then((res) => {
|
||||
parentMessage = res as DisplayActivityMessage
|
||||
})
|
||||
|
||||
@ -107,9 +107,13 @@
|
||||
stale = false
|
||||
}
|
||||
|
||||
async function getParentMessage (_class: Ref<Class<Doc>>, _id: Ref<Doc>): Promise<ActivityMessage | undefined> {
|
||||
async function getParentMessage (
|
||||
_class: Ref<Class<Doc>>,
|
||||
_id: Ref<Doc>,
|
||||
space: Ref<Space>
|
||||
): Promise<ActivityMessage | undefined> {
|
||||
if (hierarchy.isDerived(_class, activity.class.ActivityMessage)) {
|
||||
return await client.findOne<ActivityMessage>(_class, { _id: _id as Ref<ActivityMessage> })
|
||||
return await client.findOne<ActivityMessage>(_class, { _id: _id as Ref<ActivityMessage>, space })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,79 +27,67 @@
|
||||
import ThreadMessagePreview from '../threads/ThreadMessagePreview.svelte'
|
||||
|
||||
export let context: DocNotifyContext
|
||||
export let object: ChatMessage
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
let title: string | undefined = undefined
|
||||
let parentMessage: ChatMessage | undefined = undefined
|
||||
let object: Doc | undefined = undefined
|
||||
let channel: Doc | undefined = undefined
|
||||
|
||||
$: isThread = hierarchy.isDerived(context.attachedToClass, chunter.class.ThreadMessage)
|
||||
|
||||
$: void client
|
||||
.findOne(context.attachedToClass as Ref<Class<ChatMessage>>, { _id: context.attachedTo as Ref<ChatMessage> })
|
||||
.then((res) => {
|
||||
parentMessage = res
|
||||
})
|
||||
|
||||
$: loadObject(parentMessage, isThread)
|
||||
$: object &&
|
||||
getDocLinkTitle(client, object._id, object._class, object).then((res) => {
|
||||
$: loadChannel(object, isThread)
|
||||
$: channel &&
|
||||
getDocLinkTitle(client, channel._id, channel._class, channel).then((res) => {
|
||||
title = res
|
||||
})
|
||||
|
||||
function loadObject (parentMessage: ChatMessage | undefined, isThread: boolean): void {
|
||||
if (parentMessage === undefined) {
|
||||
object = undefined
|
||||
return
|
||||
}
|
||||
|
||||
const _class = isThread ? (parentMessage as ThreadMessage).objectClass : parentMessage.attachedToClass
|
||||
const _id = isThread ? (parentMessage as ThreadMessage).objectId : parentMessage.attachedTo
|
||||
|
||||
void client.findOne(_class, { _id }).then((res) => {
|
||||
object = res
|
||||
function loadChannel (object: ChatMessage, isThread: boolean): void {
|
||||
const _class = isThread ? (object as ThreadMessage).objectClass : object.attachedToClass
|
||||
const _id = isThread ? (object as ThreadMessage).objectId : object.attachedTo
|
||||
console.log({ _class, _id, isThread, object })
|
||||
void client.findOne(_class, { _id, ...(isThread ? { space: object.space } : {}) }).then((res) => {
|
||||
channel = res
|
||||
})
|
||||
}
|
||||
|
||||
function toThread (message: ChatMessage): ThreadMessage {
|
||||
return message as ThreadMessage
|
||||
}
|
||||
|
||||
function isAvatarIcon (_class: Ref<Class<Doc>>): boolean {
|
||||
return hierarchy.isDerived(_class, contact.class.Person) || hierarchy.isDerived(_class, chunter.class.DirectMessage)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if parentMessage}
|
||||
<span class="flex-presenter flex-gap-1 font-semi-bold">
|
||||
{#if isThread || (parentMessage.replies ?? 0) > 0}
|
||||
<Label label={chunter.string.Thread} />
|
||||
{:else}
|
||||
<Label label={chunter.string.Message} />
|
||||
{/if}
|
||||
{#if title && object}
|
||||
<span class="lower">
|
||||
<Label label={chunter.string.In} />
|
||||
</span>
|
||||
{#await getDocTitle(client, object._id, object._class, object) then tooltipLabel}
|
||||
<span
|
||||
class="flex-presenter flex-gap-0-5"
|
||||
use:tooltip={tooltipLabel ? { label: getEmbeddedLabel(tooltipLabel) } : undefined}
|
||||
>
|
||||
<ObjectIcon
|
||||
value={object}
|
||||
size={hierarchy.isDerived(object._class, contact.class.Person) ? 'tiny' : 'small'}
|
||||
/>
|
||||
<span class="overflow-label">
|
||||
{title}
|
||||
</span>
|
||||
<span class="flex-presenter flex-gap-1 font-semi-bold">
|
||||
{#if isThread || (object.replies ?? 0) > 0}
|
||||
<Label label={chunter.string.Thread} />
|
||||
{:else}
|
||||
<Label label={chunter.string.Message} />
|
||||
{/if}
|
||||
{#if title && channel}
|
||||
<span class="lower">
|
||||
<Label label={chunter.string.In} />
|
||||
</span>
|
||||
{#await getDocTitle(client, channel._id, channel._class, channel) then tooltipLabel}
|
||||
<span
|
||||
class="flex-presenter flex-gap-0-5"
|
||||
use:tooltip={tooltipLabel ? { label: getEmbeddedLabel(tooltipLabel) } : undefined}
|
||||
>
|
||||
<ObjectIcon value={channel} size={isAvatarIcon(channel._class) ? 'tiny' : 'small'} />
|
||||
<span class="overflow-label">
|
||||
{title}
|
||||
</span>
|
||||
{/await}
|
||||
{/if}
|
||||
</span>
|
||||
<span class="font-normal">
|
||||
{#if isThread}
|
||||
<ThreadMessagePreview value={toThread(parentMessage)} readonly type="content-only" />
|
||||
{:else}
|
||||
<ChatMessagePreview value={parentMessage} readonly type="content-only" />
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
</span>
|
||||
{/await}
|
||||
{/if}
|
||||
</span>
|
||||
<span class="font-normal">
|
||||
{#if isThread}
|
||||
<ThreadMessagePreview value={toThread(object)} readonly type="content-only" />
|
||||
{:else}
|
||||
<ChatMessagePreview value={object} readonly type="content-only" />
|
||||
{/if}
|
||||
</span>
|
||||
|
@ -20,10 +20,10 @@
|
||||
DocNotifyContext,
|
||||
InboxNotification
|
||||
} from '@hcengineering/notification'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient, isSpace, isSpaceClass } from '@hcengineering/presentation'
|
||||
import { getDocTitle, getDocIdentifier, Menu } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Class, Doc, IdMap, Ref, WithLookup } from '@hcengineering/core'
|
||||
import core, { Class, Doc, IdMap, Ref, WithLookup } from '@hcengineering/core'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import { personAccountByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Person, PersonAccount } from '@hcengineering/contact'
|
||||
@ -47,6 +47,22 @@
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const dispatch = createEventDispatcher()
|
||||
const query = createQuery()
|
||||
|
||||
let object: Doc | undefined = undefined
|
||||
|
||||
$: query.query(
|
||||
value.attachedToClass,
|
||||
{ _id: value.attachedTo, space: isSpaceClass(value.attachedToClass) ? core.space.Space : value.space },
|
||||
(res) => {
|
||||
object = res[0]
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
|
||||
$: if (object?._id !== value.attachedTo) {
|
||||
object = undefined
|
||||
}
|
||||
|
||||
let isActionMenuOpened = false
|
||||
let unreadCount = 0
|
||||
@ -56,13 +72,15 @@
|
||||
let idTitle: string | undefined
|
||||
let title: string | undefined
|
||||
|
||||
$: void getDocIdentifier(client, value.attachedTo, value.attachedToClass).then((res) => {
|
||||
idTitle = res
|
||||
})
|
||||
$: object &&
|
||||
getDocIdentifier(client, object._id, object._class, object).then((res) => {
|
||||
idTitle = res
|
||||
})
|
||||
|
||||
$: void getDocTitle(client, value.attachedTo, value.attachedToClass).then((res) => {
|
||||
title = res
|
||||
})
|
||||
$: object &&
|
||||
getDocTitle(client, object._id, object._class, object).then((res) => {
|
||||
title = res
|
||||
})
|
||||
|
||||
$: presenterMixin = hierarchy.classHierarchyMixin(
|
||||
value.attachedToClass,
|
||||
@ -184,73 +202,75 @@
|
||||
dispatch('click', { context: value })
|
||||
}}
|
||||
>
|
||||
<div class="header">
|
||||
<NotifyContextIcon {value} notifyCount={unreadCount} />
|
||||
{#if object}
|
||||
<div class="header">
|
||||
<NotifyContextIcon {value} notifyCount={unreadCount} {object} />
|
||||
|
||||
<div class="labels">
|
||||
{#if presenterMixin?.labelPresenter}
|
||||
<Component is={presenterMixin.labelPresenter} props={{ context: value }} />
|
||||
{:else}
|
||||
{#if idTitle}
|
||||
{idTitle}
|
||||
<div class="labels">
|
||||
{#if presenterMixin?.labelPresenter}
|
||||
<Component is={presenterMixin.labelPresenter} props={{ context: value, object }} />
|
||||
{:else}
|
||||
<Label label={hierarchy.getClass(value.attachedToClass).label} />
|
||||
{/if}
|
||||
<span class="title overflow-label clear-mins" {title}>
|
||||
{#if title}
|
||||
{title}
|
||||
{#if idTitle}
|
||||
{idTitle}
|
||||
{:else}
|
||||
<Label label={hierarchy.getClass(value.attachedToClass).label} />
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="actions clear-mins">
|
||||
<div class="flex-center">
|
||||
{#if archivingPromise !== undefined}
|
||||
<Spinner size="small" />
|
||||
{:else}
|
||||
<CheckBox checked={archived} kind="todo" size="medium" on:value={checkContext} />
|
||||
<span class="title overflow-label clear-mins" {title}>
|
||||
{#if title}
|
||||
{title}
|
||||
{:else}
|
||||
<Label label={hierarchy.getClass(value.attachedToClass).label} />
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<ButtonIcon
|
||||
icon={IconMoreV}
|
||||
size="small"
|
||||
kind="tertiary"
|
||||
inheritColor
|
||||
pressed={isActionMenuOpened}
|
||||
on:click={showMenu}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="notifications">
|
||||
{#each groupedNotifications.slice(0, maxNotifications) as group (getKey(group))}
|
||||
<div class="notification">
|
||||
<!-- use:tooltip={canShowTooltip(group)-->
|
||||
<!-- ? {-->
|
||||
<!-- component: MessagesPopup,-->
|
||||
<!-- props: { context: value, notifications: group }-->
|
||||
<!-- }-->
|
||||
<!-- : undefined}-->
|
||||
|
||||
<div class="embeddedMarker" />
|
||||
<InboxNotificationPresenter
|
||||
value={group[0]}
|
||||
{viewlets}
|
||||
space={value.space}
|
||||
on:click={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
dispatch('click', { context: value, notification: group[0] })
|
||||
}}
|
||||
/>
|
||||
<div class="actions clear-mins">
|
||||
<div class="flex-center">
|
||||
{#if archivingPromise !== undefined}
|
||||
<Spinner size="small" />
|
||||
{:else}
|
||||
<CheckBox checked={archived} kind="todo" size="medium" on:value={checkContext} />
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
<ButtonIcon
|
||||
icon={IconMoreV}
|
||||
size="small"
|
||||
kind="tertiary"
|
||||
inheritColor
|
||||
pressed={isActionMenuOpened}
|
||||
on:click={showMenu}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="notifications">
|
||||
{#each groupedNotifications.slice(0, maxNotifications) as group (getKey(group))}
|
||||
<div class="notification">
|
||||
<!-- use:tooltip={canShowTooltip(group)-->
|
||||
<!-- ? {-->
|
||||
<!-- component: MessagesPopup,-->
|
||||
<!-- props: { context: value, notifications: group }-->
|
||||
<!-- }-->
|
||||
<!-- : undefined}-->
|
||||
|
||||
<div class="embeddedMarker" />
|
||||
<InboxNotificationPresenter
|
||||
value={group[0]}
|
||||
{object}
|
||||
{viewlets}
|
||||
space={value.space}
|
||||
on:click={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
dispatch('click', { context: value, notification: group[0] })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@ -261,6 +281,7 @@
|
||||
cursor: pointer;
|
||||
padding: var(--spacing-1_5) var(--spacing-1);
|
||||
border-bottom: 1px solid var(--global-ui-BorderColor);
|
||||
min-height: 5.625rem;
|
||||
|
||||
.header {
|
||||
position: relative;
|
||||
|
@ -14,9 +14,9 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { DocNotifyContext } from '@hcengineering/notification'
|
||||
import { Doc } from '@hcengineering/core'
|
||||
import core, { Doc } from '@hcengineering/core'
|
||||
import { getDocLinkTitle, getDocTitle } from '@hcengineering/view-resources'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient, isSpaceClass } from '@hcengineering/presentation'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import NotifyContextIcon from './NotifyContextIcon.svelte'
|
||||
|
||||
@ -27,9 +27,13 @@
|
||||
|
||||
let object: Doc | undefined
|
||||
|
||||
$: objectQuery.query(value.attachedToClass, { _id: value.attachedTo }, (res) => {
|
||||
object = res[0]
|
||||
})
|
||||
$: objectQuery.query(
|
||||
value.attachedToClass,
|
||||
{ _id: value.attachedTo, space: isSpaceClass(value.attachedToClass) ? core.space.Space : value.space },
|
||||
(res) => {
|
||||
object = res[0]
|
||||
}
|
||||
)
|
||||
|
||||
async function getTitle (object: Doc) {
|
||||
if (object._class === chunter.class.DirectMessage) {
|
||||
@ -41,7 +45,7 @@
|
||||
|
||||
{#if object}
|
||||
<div class="flex-presenter">
|
||||
<NotifyContextIcon {value} size="small" />
|
||||
<NotifyContextIcon {value} {object} size="small" />
|
||||
<div class="mr-4" />
|
||||
|
||||
{#await getTitle(object) then title}
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import notification, { DocNotifyContext } from '@hcengineering/notification'
|
||||
import { Component, Icon, IconSize } from '@hcengineering/ui'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { classIcon } from '@hcengineering/view-resources'
|
||||
import view from '@hcengineering/view'
|
||||
import { Doc } from '@hcengineering/core'
|
||||
@ -25,21 +25,12 @@
|
||||
export let value: DocNotifyContext
|
||||
export let size: IconSize = 'medium'
|
||||
export let notifyCount: number = 0
|
||||
export let object: Doc | undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const query = createQuery()
|
||||
|
||||
let object: Doc | undefined = undefined
|
||||
|
||||
$: if (object?._id !== value.attachedTo) {
|
||||
object = undefined
|
||||
}
|
||||
$: iconMixin = hierarchy.classHierarchyMixin(value.attachedToClass, view.mixin.ObjectIcon)
|
||||
$: iconMixin &&
|
||||
query.query(value.attachedToClass, { _id: value.attachedTo }, (res) => {
|
||||
object = res[0]
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
$: void client
|
||||
.findAll(activity.class.Reaction, {
|
||||
space: message.space,
|
||||
_id: { $in: [message.objectId, ...(message?.previousMessages?.map((a) => a.objectId) ?? [])] as Ref<Reaction>[] }
|
||||
})
|
||||
.then((res) => {
|
||||
|
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Ref, Space, matchQuery } from '@hcengineering/core'
|
||||
import { Ref, Space, matchQuery, Doc } from '@hcengineering/core'
|
||||
import notification, {
|
||||
ActivityInboxNotification,
|
||||
ActivityNotificationViewlet,
|
||||
@ -30,6 +30,7 @@
|
||||
import { getActions } from '@hcengineering/view-resources'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
|
||||
export let object: Doc | undefined
|
||||
export let value: DisplayActivityInboxNotification
|
||||
export let viewlets: ActivityNotificationViewlet[] = []
|
||||
export let space: Ref<Space> | undefined = undefined
|
||||
@ -100,6 +101,6 @@
|
||||
on:click
|
||||
/>
|
||||
{:else}
|
||||
<ActivityMessagePreview value={displayMessage} {actions} {space} on:click />
|
||||
<ActivityMessagePreview value={displayMessage} {actions} {space} doc={object} on:click />
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -20,6 +20,7 @@
|
||||
import { ActivityNotificationViewlet, DisplayInboxNotification } from '@hcengineering/notification'
|
||||
|
||||
export let value: DisplayInboxNotification
|
||||
export let object: Doc | undefined
|
||||
export let viewlets: ActivityNotificationViewlet[] = []
|
||||
export let space: Ref<Space> | undefined = undefined
|
||||
|
||||
@ -30,5 +31,5 @@
|
||||
</script>
|
||||
|
||||
{#if objectPresenter}
|
||||
<Component is={objectPresenter.presenter} props={{ value, viewlets, space }} on:click />
|
||||
<Component is={objectPresenter.presenter} props={{ value, viewlets, space, object }} on:click />
|
||||
{/if}
|
||||
|
@ -54,13 +54,13 @@ export async function OnReactionChanged (originTx: Tx, control: TriggerControl):
|
||||
const txes = await createReactionNotifications(tx, control)
|
||||
|
||||
await control.apply(txes, true)
|
||||
return txes
|
||||
return []
|
||||
}
|
||||
|
||||
if (innerTx._class === core.class.TxRemoveDoc) {
|
||||
const txes = await removeReactionNotifications(tx, control)
|
||||
await control.apply(txes, true)
|
||||
return txes
|
||||
return []
|
||||
}
|
||||
|
||||
return []
|
||||
|
Loading…
Reference in New Issue
Block a user