From ae8348ebb44db83a74190c9c0ff55b6f0f8adeb6 Mon Sep 17 00:00:00 2001 From: Kristina Date: Tue, 22 Jul 2025 10:02:06 +0400 Subject: [PATCH] Chat and inbox ui updates (#9580) Signed-off-by: Kristina Fefelova --- models/card/src/index.ts | 30 ++++- .../src/components/CardTagsColored.svelte | 12 +- .../components/EditCardTableOfContents.svelte | 70 +++++++++- .../ChatNavigationCategoryList.svelte | 4 +- .../src/components/CardMessagesSection.svelte | 13 ++ .../src/components/MessagesList.svelte | 54 ++++++-- .../src/components/InboxApplication.svelte | 41 +----- .../src/components/InboxCard.svelte | 2 +- .../src/components/InboxHeader.svelte | 61 +++++++++ .../src/components/InboxViewSettings.svelte | 120 ++++++++++++++++++ .../components/ReactionNotification.svelte | 2 +- .../components/preview/PreviewTemplate.svelte | 14 +- plugins/inbox-resources/src/settings.ts | 85 +++++++++++++ 13 files changed, 437 insertions(+), 71 deletions(-) create mode 100644 plugins/inbox-resources/src/components/InboxHeader.svelte create mode 100644 plugins/inbox-resources/src/components/InboxViewSettings.svelte create mode 100644 plugins/inbox-resources/src/settings.ts diff --git a/models/card/src/index.ts b/models/card/src/index.ts index b6c9380364..2a83d69b7e 100644 --- a/models/card/src/index.ts +++ b/models/card/src/index.ts @@ -175,9 +175,11 @@ const listConfig: (BuildModelKey | string)[] = [ { key: '', displayProps: { grow: true } }, { key: '', - presenter: view.component.RolePresenter, + presenter: card.component.CardTagsColored, label: card.string.Tags, - props: { fullSize: true }, + props: { + showType: false + }, displayProps: { key: 'tags', fixed: 'right' } }, { @@ -230,10 +232,10 @@ const favoritesViewletConfig: (BuildModelKey | string)[] = [ }, key: '$lookup.attachedTo', label: card.string.Tags, - presenter: view.component.RolePresenter, props: { - fullSize: true - } + showType: false + }, + presenter: card.component.CardTagsColored }, { key: '$lookup.attachedTo', @@ -285,7 +287,14 @@ export function createSystemType ( config: [ { key: '', props: { shrink: true } }, '_class', - { key: '', presenter: view.component.RolePresenter, label: card.string.Tags, props: { fullSize: true } }, + { + key: '', + presenter: card.component.CardTagsColored, + label: card.string.Tags, + props: { + showType: false + } + }, { key: '', presenter: card.component.LabelsPresenter, @@ -519,7 +528,14 @@ export function createModel (builder: Builder): void { config: [ '', '_class', - { key: '', presenter: view.component.RolePresenter, label: card.string.Tags, props: { fullSize: true } }, + { + key: '', + presenter: card.component.CardTagsColored, + label: card.string.Tags, + props: { + showType: false + } + }, 'modifiedOn' ] }, diff --git a/plugins/card-resources/src/components/CardTagsColored.svelte b/plugins/card-resources/src/components/CardTagsColored.svelte index ccaf924420..b78c0f342a 100644 --- a/plugins/card-resources/src/components/CardTagsColored.svelte +++ b/plugins/card-resources/src/components/CardTagsColored.svelte @@ -18,6 +18,8 @@ import CardTagColored from './CardTagColored.svelte' export let value: Card + export let showTags: boolean = true + export let showType: boolean = true const client = getClient() const hierarchy = client.getHierarchy() @@ -37,9 +39,13 @@
- - {#if tags.length > 0} -
+ {#if showType} + + {/if} + {#if tags.length > 0 && showTags} + {#if showType} +
+ {/if} {#each tags as tag} {/each} diff --git a/plugins/card-resources/src/components/EditCardTableOfContents.svelte b/plugins/card-resources/src/components/EditCardTableOfContents.svelte index b2e6c44839..62b8b68893 100644 --- a/plugins/card-resources/src/components/EditCardTableOfContents.svelte +++ b/plugins/card-resources/src/components/EditCardTableOfContents.svelte @@ -15,7 +15,7 @@
@@ -274,6 +310,14 @@ {/each}
+ {#if toc.length > 0 && (bottomOffset > 400 || canScrollDown())} + {@const lastToc = toc[toc.length - 1]} + {#await getDownButtonTitle(lastToc, $languageStore) then title} +
+ +
+ {/await} + {/if}
@@ -316,4 +360,26 @@ align-items: flex-start; align-self: stretch; } + + .down-button { + position: absolute; + width: 100%; + display: flex; + justify-content: center; + bottom: 0.5rem; + animation: 0.5s fadeIn; + animation-fill-mode: forwards; + visibility: hidden; + left: 0; + right: 0; + } + + @keyframes fadeIn { + 99% { + visibility: hidden; + } + 100% { + visibility: visible; + } + } diff --git a/plugins/chat-resources/src/components/ChatNavigationCategoryList.svelte b/plugins/chat-resources/src/components/ChatNavigationCategoryList.svelte index bae8be1dfc..bd87f4f72f 100644 --- a/plugins/chat-resources/src/components/ChatNavigationCategoryList.svelte +++ b/plugins/chat-resources/src/components/ChatNavigationCategoryList.svelte @@ -54,9 +54,9 @@ }, key: '', label: card.string.Tags, - presenter: view.component.RolePresenter, + presenter: card.component.CardTagsColored, props: { - fullSize: true + showType: false } }, { diff --git a/plugins/communication-resources/src/components/CardMessagesSection.svelte b/plugins/communication-resources/src/components/CardMessagesSection.svelte index e94f5cbdda..1bfa5b90ab 100644 --- a/plugins/communication-resources/src/components/CardMessagesSection.svelte +++ b/plugins/communication-resources/src/components/CardMessagesSection.svelte @@ -33,6 +33,8 @@ const dispatch = createEventDispatcher() + let list: MessagesList | undefined = undefined + let position: MessagesNavigationAnchors = navigation === MessagesNavigationAnchors.LatestMessages ? MessagesNavigationAnchors.LatestMessages @@ -49,6 +51,16 @@ position = id } + export function scrollDown (): void { + shouldScrollToStart = false + position = MessagesNavigationAnchors.LatestMessages + list?.scrollDown() + } + + export function canScrollDown (): boolean { + return list?.canScrollDown() ?? false + } + onMount(() => { if (active && navigation === MessagesNavigationAnchors.LatestMessages) { position = MessagesNavigationAnchors.LatestMessages @@ -60,6 +72,7 @@ {#if scrollDiv != null && isContextLoaded && contentDiv != null}
initialLastView let queryDef = getBaseQuery() + export function scrollDown (): void { + shouldScrollToEnd = true + position = MessagesNavigationAnchors.LatestMessages + + reinit(position, true) + } + + export function canScrollDown (): boolean { + return window !== undefined && window.hasNextPage() + } $: if ( (context?.lastView?.getTime() ?? 0) >= (context?.lastUpdate?.getTime() ?? 0) && (notifications?.length ?? 0) > 0 && @@ -156,8 +167,8 @@ } } - function reinit (position: MessagesNavigationAnchors): void { - if (prevPosition === position) { + function reinit (position: MessagesNavigationAnchors, force = false): void { + if (prevPosition === position && !force) { return } prevPosition = position @@ -191,7 +202,10 @@ limit } } - const order = unread ? SortingOrder.Ascending : SortingOrder.Descending + initialLastView = context?.lastView + initialLastUpdate = context?.lastUpdate + const unread = initialLastView != null && initialLastUpdate != null && initialLastUpdate > initialLastView + const order = unread && !shouldScrollToEnd ? SortingOrder.Ascending : SortingOrder.Descending return { card: card._id, replies: true, @@ -200,7 +214,7 @@ links: true, order, limit, - from: unread && initialLastView != null ? initialLastView : undefined + from: unread && !shouldScrollToEnd && initialLastView != null ? initialLastView : undefined } } @@ -231,12 +245,12 @@ return bottomOffset <= 200 } - function loadMore (): void { + function loadMore (direction: 'up' | 'down'): void { if (window === undefined || !isScrollInitialized) return - if (shouldLoadPrevPage() && window.hasPrevPage()) { + if (shouldLoadPrevPage() && window.hasPrevPage() && direction === 'up') { void loadPrevPage() - } else if (shouldLoadNextPage() && window.hasNextPage()) { + } else if (shouldLoadNextPage() && window.hasNextPage() && direction === 'down') { void loadNextPage() } } @@ -312,13 +326,19 @@ } } let rafId: any | null = null + let lastScrollTop: number = 0 + function handleScroll (): void { if (rafId !== null) return rafId = requestAnimationFrame(() => { + const top = scrollDiv.scrollTop + const direction = top > lastScrollTop ? 'down' : 'up' + + lastScrollTop = top bottomOffset = getBottomOffset() topOffset = getTopOffset() updateShouldScrollToNew() - loadMore() + loadMore(direction) checkPositionOnScroll() void readAll() rafId = null @@ -531,11 +551,15 @@ const separatorIndex = initialLastView !== undefined - ? messages.findIndex(({ created, creator }) => !me.socialIds.includes(creator) && created > initialLastView) + ? messages.findIndex( + ({ created, creator }) => + initialLastView != null && !me.socialIds.includes(creator) && created > initialLastView + ) : -1 - if (separatorIndex === -1) { + if (separatorIndex === -1 || shouldScrollToEnd) { await tick() // Wait for the DOM to update + shouldScrollToEnd = false scrollToBottom(true) shouldScrollToNew = true atBottom = true @@ -551,7 +575,9 @@ if (separatorDiv != null) { await tick() // Wait for the DOM to update scrollToWithOffset(scrollDiv, separatorDiv, 80) - isScrollInitialized = true + setTimeout(() => { + isScrollInitialized = true + }, 10) updateShouldScrollToNew() bottomOffset = getBottomOffset() topOffset = getTopOffset() diff --git a/plugins/inbox-resources/src/components/InboxApplication.svelte b/plugins/inbox-resources/src/components/InboxApplication.svelte index a2da278f49..0d99e7c98d 100644 --- a/plugins/inbox-resources/src/components/InboxApplication.svelte +++ b/plugins/inbox-resources/src/components/InboxApplication.svelte @@ -23,25 +23,20 @@ Location, restoreLocation, Component, - Label, - ModernButton, - IconDelete, closePanel, getCurrentLocation } from '@hcengineering/ui' import { onDestroy } from 'svelte' - import { getClient, getCommunicationClient } from '@hcengineering/presentation' + import { getClient } from '@hcengineering/presentation' import { inboxId } from '@hcengineering/inbox' import view from '@hcengineering/view' import { NotificationContext } from '@hcengineering/communication-types' - import { SortingOrder } from '@hcengineering/core' import InboxNavigation from './InboxNavigation.svelte' import { getCardIdFromLocation, navigateToCard } from '../location' - import inbox from '../plugin' + import InboxHeader from './InboxHeader.svelte' const client = getClient() - const communicationClient = getCommunicationClient() let replacedPanelElement: HTMLElement let card: Card | undefined = undefined @@ -95,20 +90,6 @@ $: $deviceInfo.replacedPanel = replacedPanelElement onDestroy(() => ($deviceInfo.replacedPanel = undefined)) - - let clearing = false - async function clearInbox (): Promise { - if (clearing) return - try { - clearing = true - const contexts = await communicationClient.findNotificationContexts({ order: SortingOrder.Ascending }) - await Promise.all(contexts.map((context) => communicationClient.removeNotificationContext(context.id))) - clearing = false - } catch (e) { - clearing = false - console.error(e) - } - }
@@ -120,19 +101,7 @@ class:fly={$deviceInfo.navigator.float} >
-
- -
- -
-
+
@@ -175,8 +144,4 @@ position: relative; } } - - .inbox-header { - border-bottom: 1px solid var(--theme-navpanel-border); - } diff --git a/plugins/inbox-resources/src/components/InboxCard.svelte b/plugins/inbox-resources/src/components/InboxCard.svelte index 4cdb36a21b..6faa878c74 100644 --- a/plugins/inbox-resources/src/components/InboxCard.svelte +++ b/plugins/inbox-resources/src/components/InboxCard.svelte @@ -84,7 +84,7 @@ flex-direction: column; position: relative; cursor: pointer; - padding: 1rem 0.5rem 0.5rem; + padding: 0.5rem; border-bottom: 1px solid var(--global-ui-BorderColor); min-height: 5.625rem; diff --git a/plugins/inbox-resources/src/components/InboxHeader.svelte b/plugins/inbox-resources/src/components/InboxHeader.svelte new file mode 100644 index 0000000000..32f7de63e6 --- /dev/null +++ b/plugins/inbox-resources/src/components/InboxHeader.svelte @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + +
+ +
+ + +
+
+ + diff --git a/plugins/inbox-resources/src/components/InboxViewSettings.svelte b/plugins/inbox-resources/src/components/InboxViewSettings.svelte new file mode 100644 index 0000000000..ff8b8c0e1d --- /dev/null +++ b/plugins/inbox-resources/src/components/InboxViewSettings.svelte @@ -0,0 +1,120 @@ + + + + + + + +
{ + dispatch('changeContent') + }} + on:keydown={onKeydown} +> +