From c375aa41838bc30c72562b8010e9f8be69c43344 Mon Sep 17 00:00:00 2001 From: Kristina Fefelova Date: Tue, 23 Apr 2024 18:24:52 +0400 Subject: [PATCH 1/3] UBERF-6677: add user online/offline status Signed-off-by: Kristina Fefelova --- packages/theme/styles/_lumia-colors.scss | 6 ++ .../src/components/ChannelHeader.svelte | 2 +- .../src/components/ChannelMembers.svelte | 2 +- .../src/components/DirectIcon.svelte | 20 ++++- .../src/components/chat/ChannelAside.svelte | 3 +- .../chat/create/CreateDirectChat.svelte | 3 +- .../chat/navigator/ChatNavItem.svelte | 2 +- .../chat/navigator/ChatNavSection.svelte | 1 + .../src/components/chat/types.ts | 1 + .../src/components/Avatar.svelte | 36 +++++++- .../src/components/SelectUsersPopup.svelte | 9 +- .../src/components/UserDetails.svelte | 20 ++++- .../src/components/UsersList.svelte | 4 +- .../src/components/UserStatus.svelte | 85 +++++++++++++++++++ plugins/view-resources/src/index.ts | 4 +- server/ws/src/server.ts | 4 +- 16 files changed, 183 insertions(+), 19 deletions(-) create mode 100644 plugins/view-resources/src/components/UserStatus.svelte diff --git a/packages/theme/styles/_lumia-colors.scss b/packages/theme/styles/_lumia-colors.scss index dc2cb7f7ae..f6f2b1ffb7 100644 --- a/packages/theme/styles/_lumia-colors.scss +++ b/packages/theme/styles/_lumia-colors.scss @@ -122,6 +122,9 @@ --love-active-call-color-2: #F47758; --love-active-call-transform: scaleY(0.25) scaleX(0.4); --love-active-call-filter: blur(17px); + + --global-online-color: #49a26b; + --global-offline-color: #d1d5de; } /* Light Theme */ @@ -213,4 +216,7 @@ --love-active-call-color-2: #e34748; --love-active-call-filter: blur(10px); --love-active-call-transform: scaleY(0.3) scaleX(0.42); + + --global-online-color: #49a26b; + --global-offline-color: #5A667E; } diff --git a/plugins/chunter-resources/src/components/ChannelHeader.svelte b/plugins/chunter-resources/src/components/ChannelHeader.svelte index b601edd911..3802f07f6c 100644 --- a/plugins/chunter-resources/src/components/ChannelHeader.svelte +++ b/plugins/chunter-resources/src/components/ChannelHeader.svelte @@ -66,7 +66,7 @@ bind:filters {object} icon={getObjectIcon(_class)} - iconProps={{ value: object }} + iconProps={{ value: object, showStatus: true, background: 'var(--theme-comp-header-color)' }} label={title} intlLabel={chunter.string.Channel} {description} diff --git a/plugins/chunter-resources/src/components/ChannelMembers.svelte b/plugins/chunter-resources/src/components/ChannelMembers.svelte index 2a86c8dc52..5796ec86ed 100644 --- a/plugins/chunter-resources/src/components/ChannelMembers.svelte +++ b/plugins/chunter-resources/src/components/ChannelMembers.svelte @@ -50,7 +50,7 @@ {#each persons as person, index}
- + {#if !disableRemoveFor.includes(person._id)}
{#if persons.length === 0 && value} @@ -48,7 +55,14 @@ {/if} {#if persons.length === 1} - + {/if} {#if persons.length > 1 && size === 'medium'} diff --git a/plugins/chunter-resources/src/components/chat/ChannelAside.svelte b/plugins/chunter-resources/src/components/chat/ChannelAside.svelte index 4c3947502d..1ead45d395 100644 --- a/plugins/chunter-resources/src/components/chat/ChannelAside.svelte +++ b/plugins/chunter-resources/src/components/chat/ChannelAside.svelte @@ -103,7 +103,8 @@ { okLabel: presentation.string.Add, disableDeselectFor: disabledRemoveFor, - selected: members + selected: members, + showStatus: true }, 'top', (result?: Ref[]) => { diff --git a/plugins/chunter-resources/src/components/chat/create/CreateDirectChat.svelte b/plugins/chunter-resources/src/components/chat/create/CreateDirectChat.svelte index 0fd678409e..fdfa5cda42 100644 --- a/plugins/chunter-resources/src/components/chat/create/CreateDirectChat.svelte +++ b/plugins/chunter-resources/src/components/chat/create/CreateDirectChat.svelte @@ -115,7 +115,8 @@ { okLabel: presentation.string.Next, skipCurrentAccount: true, - selected: employeeIds + selected: employeeIds, + showStatus: true }, 'top', (result?: Ref[]) => { diff --git a/plugins/chunter-resources/src/components/chat/navigator/ChatNavItem.svelte b/plugins/chunter-resources/src/components/chat/navigator/ChatNavItem.svelte index 7a798e377e..06d1530093 100644 --- a/plugins/chunter-resources/src/components/chat/navigator/ChatNavItem.svelte +++ b/plugins/chunter-resources/src/components/chat/navigator/ChatNavItem.svelte @@ -120,7 +120,7 @@ isSecondary={item.isSecondary} iconSize={item.iconSize} {isSelected} - iconProps={{ value: item.object }} + iconProps={{ ...item.iconProps, value: item.object }} {notificationsCount} title={item.title} description={item.description} diff --git a/plugins/chunter-resources/src/components/chat/navigator/ChatNavSection.svelte b/plugins/chunter-resources/src/components/chat/navigator/ChatNavSection.svelte index df39746123..2709ad133c 100644 --- a/plugins/chunter-resources/src/components/chat/navigator/ChatNavSection.svelte +++ b/plugins/chunter-resources/src/components/chat/navigator/ChatNavSection.svelte @@ -80,6 +80,7 @@ title: (await getChannelName(object._id, object._class, object)) ?? (await translate(titleIntl, {})), description: isDocChat && !isPerson ? await getDocTitle(client, object._id, object._class, object) : undefined, icon: icon ?? getObjectIcon(_class), + iconProps: { showStatus: true, background: 'var(--global-surface-01-BackgroundColor)' }, iconSize, withIconBackground: !isDirect && !isPerson, isSecondary: isDocChat && !isPerson diff --git a/plugins/chunter-resources/src/components/chat/types.ts b/plugins/chunter-resources/src/components/chat/types.ts index 0622e05947..f959aae816 100644 --- a/plugins/chunter-resources/src/components/chat/types.ts +++ b/plugins/chunter-resources/src/components/chat/types.ts @@ -36,6 +36,7 @@ export interface ChatNavItemModel { description?: string icon: Asset | AnySvelteComponent | undefined iconSize?: IconSize + iconProps: Record isSecondary: boolean withIconBackground: boolean } diff --git a/plugins/contact-resources/src/components/Avatar.svelte b/plugins/contact-resources/src/components/Avatar.svelte index aceca3b95f..812e541ca9 100644 --- a/plugins/contact-resources/src/components/Avatar.svelte +++ b/plugins/contact-resources/src/components/Avatar.svelte @@ -46,6 +46,9 @@ themeStore } from '@hcengineering/ui' import { onMount } from 'svelte' + import { UserStatus } from '@hcengineering/view-resources' + import { Account } from '@hcengineering/core' + import AvatarIcon from './icons/Avatar.svelte' export let avatar: string | null | undefined = undefined @@ -56,6 +59,9 @@ export let variant: 'circle' | 'roundedRect' | 'none' = 'roundedRect' export let borderColor: number | undefined = undefined export let standby: boolean = false + export let showStatus = false + export let account: Ref | undefined = undefined + export let background: string | undefined = undefined export function pulse (): void { if (element) element.animate(pulsating, { duration: 150, easing: 'ease-out' }) @@ -136,6 +142,24 @@ fontSize = element.clientWidth * 0.6 } }) + + function getStatusSize (avaSize: IconSize): 'small' | 'medium' { + switch (avaSize) { + case 'inline': + case 'tiny': + case 'card': + case 'x-small': + return 'small' + case 'small': + case 'medium': + case 'large': + case 'x-large': + case '2x-large': + return 'medium' + default: + return 'small' + } + } {#if size === 'full' && !url && name && displayName && displayName !== ''} @@ -185,6 +209,11 @@
{/if} + {#if showStatus && account} + + + + {/if}
{/if} @@ -192,7 +221,6 @@ .avatar-container { flex-shrink: 0; position: relative; - overflow: hidden; background-color: var(--theme-button-default); pointer-events: none; @@ -364,4 +392,10 @@ font-size: inherit; } } + + .status { + position: absolute; + bottom: -0.125ren; + right: -0.25rem; + } diff --git a/plugins/contact-resources/src/components/SelectUsersPopup.svelte b/plugins/contact-resources/src/components/SelectUsersPopup.svelte index ddcc8dccdc..ff1f3fe5b8 100644 --- a/plugins/contact-resources/src/components/SelectUsersPopup.svelte +++ b/plugins/contact-resources/src/components/SelectUsersPopup.svelte @@ -32,21 +32,22 @@ export let selected: Ref[] = [] export let skipCurrentAccount = false export let disableDeselectFor: Ref[] = [] + export let showStatus = false const dispatch = createEventDispatcher() let search: string = '' let selectedIds: Ref[] = selected - function handleCancel () { + function handleCancel (): void { dispatch('close') } - function okAction () { + function okAction (): void { dispatch('close', selectedIds) } - function handleSelectionChanged (event: CustomEvent) { + function handleSelectionChanged (event: CustomEvent): void { selectedIds = event.detail ?? [] } @@ -86,8 +87,10 @@ {search} {groupBy} selected={selectedIds} + {showStatus} {disableDeselectFor} {skipCurrentAccount} + background="var(--theme-popup-color)" on:select={handleSelectionChanged} /> diff --git a/plugins/contact-resources/src/components/UserDetails.svelte b/plugins/contact-resources/src/components/UserDetails.svelte index 1cef5c0161..5ecdd8a764 100644 --- a/plugins/contact-resources/src/components/UserDetails.svelte +++ b/plugins/contact-resources/src/components/UserDetails.svelte @@ -15,21 +15,37 @@
- +
{getName(hierarchy, person)}
diff --git a/plugins/contact-resources/src/components/UsersList.svelte b/plugins/contact-resources/src/components/UsersList.svelte index b6c5e9582c..dbbaca8854 100644 --- a/plugins/contact-resources/src/components/UsersList.svelte +++ b/plugins/contact-resources/src/components/UsersList.svelte @@ -30,6 +30,8 @@ export let selected: Ref[] = [] export let skipCurrentAccount = false export let disableDeselectFor: Ref[] = [] + export let showStatus = false + export let background: string | undefined = undefined const dispatch = createEventDispatcher() const query = createQuery() @@ -114,7 +116,7 @@ handleSelection(persons, index) }} > - + + + +
+
+
+ + diff --git a/plugins/view-resources/src/index.ts b/plugins/view-resources/src/index.ts index 8dfc79101c..2aa394deef 100644 --- a/plugins/view-resources/src/index.ts +++ b/plugins/view-resources/src/index.ts @@ -93,6 +93,7 @@ import ViewletContentView from './components/ViewletContentView.svelte' import ViewletSettingButton from './components/ViewletSettingButton.svelte' import DocReferencePresenter from './components/DocReferencePresenter.svelte' import ObjectIcon from './components/ObjectIcon.svelte' +import UserStatus from './components/UserStatus.svelte' import { afterResult, @@ -205,7 +206,8 @@ export { ViewletContentView, ViewletSettingButton, DocReferencePresenter, - ObjectIcon + ObjectIcon, + UserStatus } function PositionElementAlignment (e?: Event): PopupAlignment | undefined { diff --git a/server/ws/src/server.ts b/server/ws/src/server.ts index cb4cd5ae64..f1e061eaa0 100644 --- a/server/ws/src/server.ts +++ b/server/ws/src/server.ts @@ -23,8 +23,6 @@ import core, { versionToString, type BaseWorkspaceInfo, type MeasureContext, - type Ref, - type Space, type Tx, type TxWorkspaceEvent, type WorkspaceId @@ -524,7 +522,7 @@ class TSessionManager implements SessionManager { const status = (await session.findAll(ctx, core.class.UserStatus, { user: user._id }, { limit: 1 }))[0] const txFactory = new TxFactory(user._id, true) if (status === undefined) { - const tx = txFactory.createTxCreateDoc(core.class.UserStatus, user._id as string as Ref, { + const tx = txFactory.createTxCreateDoc(core.class.UserStatus, core.space.Space, { online, user: user._id }) From 72a8d136c742f42a0485efca88dbf31e809c7118 Mon Sep 17 00:00:00 2001 From: Kristina Fefelova Date: Tue, 23 Apr 2024 20:18:42 +0400 Subject: [PATCH 2/3] Fix typo Signed-off-by: Kristina Fefelova --- plugins/contact-resources/src/components/Avatar.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/contact-resources/src/components/Avatar.svelte b/plugins/contact-resources/src/components/Avatar.svelte index 812e541ca9..0a0a1901f8 100644 --- a/plugins/contact-resources/src/components/Avatar.svelte +++ b/plugins/contact-resources/src/components/Avatar.svelte @@ -395,7 +395,7 @@ .status { position: absolute; - bottom: -0.125ren; + bottom: -0.125rem; right: -0.25rem; } From fdf9f8f0a430b143b716e1263af12c89ac7df990 Mon Sep 17 00:00:00 2001 From: Kristina Fefelova Date: Tue, 23 Apr 2024 20:41:22 +0400 Subject: [PATCH 3/3] User global store for statuses Signed-off-by: Kristina Fefelova --- .../src/components/Avatar.svelte | 2 +- .../src/components/UserStatus.svelte | 24 +++++++------------ plugins/contact-resources/src/utils.ts | 16 +++++++++++-- plugins/view-resources/src/index.ts | 4 +--- 4 files changed, 25 insertions(+), 21 deletions(-) rename plugins/{view-resources => contact-resources}/src/components/UserStatus.svelte (79%) diff --git a/plugins/contact-resources/src/components/Avatar.svelte b/plugins/contact-resources/src/components/Avatar.svelte index 0a0a1901f8..d561452494 100644 --- a/plugins/contact-resources/src/components/Avatar.svelte +++ b/plugins/contact-resources/src/components/Avatar.svelte @@ -46,10 +46,10 @@ themeStore } from '@hcengineering/ui' import { onMount } from 'svelte' - import { UserStatus } from '@hcengineering/view-resources' import { Account } from '@hcengineering/core' import AvatarIcon from './icons/Avatar.svelte' + import UserStatus from './UserStatus.svelte' export let avatar: string | null | undefined = undefined export let name: string | null | undefined = undefined diff --git a/plugins/view-resources/src/components/UserStatus.svelte b/plugins/contact-resources/src/components/UserStatus.svelte similarity index 79% rename from plugins/view-resources/src/components/UserStatus.svelte rename to plugins/contact-resources/src/components/UserStatus.svelte index 0568ebdabc..fc423c1158 100644 --- a/plugins/view-resources/src/components/UserStatus.svelte +++ b/plugins/contact-resources/src/components/UserStatus.svelte @@ -13,33 +13,27 @@ // limitations under the License. -->
diff --git a/plugins/contact-resources/src/utils.ts b/plugins/contact-resources/src/utils.ts index d156b3f70a..855aed6cfa 100644 --- a/plugins/contact-resources/src/utils.ts +++ b/plugins/contact-resources/src/utils.ts @@ -28,7 +28,7 @@ import { type Person, type PersonAccount } from '@hcengineering/contact' -import { +import core, { getCurrentAccount, toIdMap, type Class, @@ -38,7 +38,9 @@ import { type ObjQueryType, type Ref, type Timestamp, - type TxOperations + type TxOperations, + type Account, + type UserStatus } from '@hcengineering/core' import notification, { type DocNotifyContext, type InboxNotification } from '@hcengineering/notification' import { getEmbeddedLabel, getResource, translate } from '@hcengineering/platform' @@ -294,6 +296,8 @@ export const channelProviders = writable([]) export const personAccountPersonByIdStore = writable>(new Map()) +export const statusByUserStore = writable, UserStatus>>(new Map()) + export const personByIdStore = derived([personAccountPersonByIdStore, employeeByIdStore], (vals) => { const m1 = Array.from(vals[0].entries()) const m2 = Array.from(vals[1].entries()) @@ -340,6 +344,14 @@ function fillStores (): void { fillStores() +const userStatusesQuery = createQuery(true) + +export function loadUsersStatus (): void { + userStatusesQuery.query(core.class.UserStatus, {}, (res) => { + statusByUserStore.set(new Map(res.map((it) => [it.user, it]))) + }) +} + export function getAvatarTypeDropdownItems (hasGravatar: boolean, imageOnly?: boolean): TabItem[] { if (imageOnly === true) { return [ diff --git a/plugins/view-resources/src/index.ts b/plugins/view-resources/src/index.ts index 2aa394deef..8dfc79101c 100644 --- a/plugins/view-resources/src/index.ts +++ b/plugins/view-resources/src/index.ts @@ -93,7 +93,6 @@ import ViewletContentView from './components/ViewletContentView.svelte' import ViewletSettingButton from './components/ViewletSettingButton.svelte' import DocReferencePresenter from './components/DocReferencePresenter.svelte' import ObjectIcon from './components/ObjectIcon.svelte' -import UserStatus from './components/UserStatus.svelte' import { afterResult, @@ -206,8 +205,7 @@ export { ViewletContentView, ViewletSettingButton, DocReferencePresenter, - ObjectIcon, - UserStatus + ObjectIcon } function PositionElementAlignment (e?: Event): PopupAlignment | undefined {