From 210d3eebc6dc5ee0f5e8dab5c0f90b5ea58682f7 Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Thu, 6 Apr 2023 20:25:58 +0600 Subject: [PATCH] TSK-1051 TSK-1052 TSK-1061 Inbox actions (#2914) Signed-off-by: Denis Bykhov --- models/notification/package.json | 2 + models/notification/src/index.ts | 54 +++++++++++++++++- models/notification/src/migration.ts | 14 +++++ models/notification/src/plugin.ts | 27 +++++++-- plugins/notification-assets/assets/icons.svg | 8 +++ plugins/notification-assets/lang/en.json | 2 + plugins/notification-assets/lang/ru.json | 2 + plugins/notification-assets/src/index.ts | 1 + .../src/components/Inbox.svelte | 3 +- .../src/components/NotificationView.svelte | 8 ++- plugins/notification-resources/src/index.ts | 10 +++- plugins/notification-resources/src/plugin.ts | 1 - plugins/notification-resources/src/utils.ts | 56 ++++++++++++++++++- plugins/notification/src/index.ts | 9 ++- plugins/view-assets/lang/ru.json | 2 +- .../src/components/ActionsPopup.svelte | 13 +++-- .../notification-resources/src/index.ts | 4 +- 17 files changed, 193 insertions(+), 23 deletions(-) diff --git a/models/notification/package.json b/models/notification/package.json index 5ace97396b..2e6011cbea 100644 --- a/models/notification/package.json +++ b/models/notification/package.json @@ -30,6 +30,8 @@ "@hcengineering/ui": "^0.6.3", "@hcengineering/platform": "^0.6.8", "@hcengineering/model-core": "^0.6.0", + "@hcengineering/model-view": "^0.6.0", + "@hcengineering/view": "^0.6.2", "@hcengineering/workbench": "^0.6.2", "@hcengineering/notification": "^0.6.8", "@hcengineering/setting": "^0.6.3" diff --git a/models/notification/src/index.ts b/models/notification/src/index.ts index 1dfb3e2308..18b725dfab 100644 --- a/models/notification/src/index.ts +++ b/models/notification/src/index.ts @@ -36,6 +36,7 @@ import setting from '@hcengineering/setting' import workbench from '@hcengineering/workbench' import notification from './plugin' import { AnyComponent } from '@hcengineering/ui' +import view, { createAction } from '@hcengineering/model-view' export const DOMAIN_NOTIFICATION = 'notification' as Domain @@ -143,9 +144,12 @@ export class TDocUpdates extends TDoc implements DocUpdates { @Index(IndexKind.Indexed) attachedTo!: Ref + @Index(IndexKind.Indexed) + hidden!: boolean + attachedToClass!: Ref> - lastTx?: Ref> - lastTxTime?: Timestamp + lastTx!: Ref> + lastTxTime!: Timestamp txes!: [Ref>, Timestamp][] } @@ -240,6 +244,52 @@ export function createModel (builder: Builder): void { }, notification.app.Notification ) + + createAction( + builder, + { + action: notification.actionImpl.MarkAsUnread, + actionProps: {}, + label: notification.string.MarkAsUnread, + icon: notification.icon.Track, + input: 'focus', + visibilityTester: notification.function.HasntNotifications, + category: notification.category.Notification, + target: notification.class.DocUpdates, + context: { mode: 'context', application: notification.app.Notification, group: 'edit' } + }, + notification.action.MarkAsUnread + ) + + createAction( + builder, + { + action: notification.actionImpl.Hide, + actionProps: {}, + label: notification.string.Hide, + icon: notification.icon.Hide, + input: 'focus', + category: notification.category.Notification, + target: notification.class.DocUpdates, + context: { mode: 'context', application: notification.app.Notification, group: 'edit' } + }, + notification.action.Hide + ) + + createAction( + builder, + { + action: notification.actionImpl.Unsubscribe, + actionProps: {}, + label: notification.string.DontTrack, + icon: view.icon.Delete, + input: 'focus', + category: notification.category.Notification, + target: notification.class.DocUpdates, + context: { mode: 'context', application: notification.app.Notification, group: 'edit' } + }, + notification.action.Unsubscribe + ) } export { notificationOperation } from './migration' diff --git a/models/notification/src/migration.ts b/models/notification/src/migration.ts index 2f92e3fec5..91b24985a2 100644 --- a/models/notification/src/migration.ts +++ b/models/notification/src/migration.ts @@ -184,11 +184,25 @@ async function fillCollaborators (client: MigrationClient): Promise { } } +async function fillDocUpdatesHidder (client: MigrationClient): Promise { + await client.update( + DOMAIN_NOTIFICATION, + { + _class: notification.class.DocUpdates, + hidden: { $exists: false } + }, + { + hidden: false + } + ) +} + export const notificationOperation: MigrateOperation = { async migrate (client: MigrationClient): Promise { await fillNotificationText(client) await migrateLastView(client) await fillCollaborators(client) + await fillDocUpdatesHidder(client) }, async upgrade (client: MigrationUpgradeClient): Promise { await createSpace(client) diff --git a/models/notification/src/plugin.ts b/models/notification/src/plugin.ts index 91b1ca14b1..896b4a416e 100644 --- a/models/notification/src/plugin.ts +++ b/models/notification/src/plugin.ts @@ -14,11 +14,12 @@ // limitations under the License. // -import { Ref } from '@hcengineering/core' -import { Application } from '@hcengineering/workbench' +import { Doc, Ref } from '@hcengineering/core' import notification, { notificationId } from '@hcengineering/notification' -import { IntlString, mergeIds } from '@hcengineering/platform' +import { IntlString, Resource, mergeIds } from '@hcengineering/platform' import { AnyComponent } from '@hcengineering/ui' +import { Action, ActionCategory, ViewAction } from '@hcengineering/view' +import { Application } from '@hcengineering/workbench' export default mergeIds(notificationId, notification, { string: { @@ -29,12 +30,30 @@ export default mergeIds(notificationId, notification, { PlatformNotification: '' as IntlString, BrowserNotification: '' as IntlString, EmailNotification: '' as IntlString, - Collaborators: '' as IntlString + Collaborators: '' as IntlString, + Hide: '' as IntlString, + MarkAsUnread: '' as IntlString }, app: { Notification: '' as Ref }, component: { NotificationSettings: '' as AnyComponent + }, + function: { + HasntNotifications: '' as Resource<(doc?: Doc | Doc[]) => Promise> + }, + category: { + Notification: '' as Ref + }, + action: { + Unsubscribe: '' as Ref, + Hide: '' as Ref, + MarkAsUnread: '' as Ref + }, + actionImpl: { + Unsubscribe: '' as ViewAction, + Hide: '' as ViewAction, + MarkAsUnread: '' as ViewAction } }) diff --git a/plugins/notification-assets/assets/icons.svg b/plugins/notification-assets/assets/icons.svg index 2c9291539d..3cc1ed2110 100644 --- a/plugins/notification-assets/assets/icons.svg +++ b/plugins/notification-assets/assets/icons.svg @@ -10,4 +10,12 @@ + + + + diff --git a/plugins/notification-assets/lang/en.json b/plugins/notification-assets/lang/en.json index e9b79dd8eb..9367612c2d 100644 --- a/plugins/notification-assets/lang/en.json +++ b/plugins/notification-assets/lang/en.json @@ -16,6 +16,8 @@ "RemoveAll": "Delete all notifications", "MarkAllAsRead": "Mark all notifications as read", "MarkAsRead": "Mark as read", + "MarkAsUnread": "Mark as unread", + "Hide": "Hide", "Inbox": "Inbox", "Collaborators": "Collaborators" } diff --git a/plugins/notification-assets/lang/ru.json b/plugins/notification-assets/lang/ru.json index c85025a14a..8a391f32d2 100644 --- a/plugins/notification-assets/lang/ru.json +++ b/plugins/notification-assets/lang/ru.json @@ -16,6 +16,8 @@ "RemoveAll": "Удалить все нотификации", "MarkAllAsRead": "Отметить все нотификации как прочитанные", "MarkAsRead": "Отметить нотификация прочитанной", + "MarkAsUnread": "Отметить непрочитанным", + "Hide": "Скрыть", "Inbox": "Inbox", "Collaborators": "Участники" } diff --git a/plugins/notification-assets/src/index.ts b/plugins/notification-assets/src/index.ts index 360f51cfda..1c986b467d 100644 --- a/plugins/notification-assets/src/index.ts +++ b/plugins/notification-assets/src/index.ts @@ -20,6 +20,7 @@ const icons = require('../assets/icons.svg') as string // eslint-disable-line loadMetadata(notification.icon, { Notifications: `${icons}#notification`, Track: `${icons}#track`, + Hide: `${icons}#hide`, DontTrack: `${icons}#donttrack` }) diff --git a/plugins/notification-resources/src/components/Inbox.svelte b/plugins/notification-resources/src/components/Inbox.svelte index 45479f3631..cf22c8117c 100644 --- a/plugins/notification-resources/src/components/Inbox.svelte +++ b/plugins/notification-resources/src/components/Inbox.svelte @@ -34,7 +34,8 @@ $: query.query( notification.class.DocUpdates, { - user: getCurrentAccount()._id + user: getCurrentAccount()._id, + hidden: false }, (res) => { docs = res diff --git a/plugins/notification-resources/src/components/NotificationView.svelte b/plugins/notification-resources/src/components/NotificationView.svelte index 78bed3db99..f528cdd583 100644 --- a/plugins/notification-resources/src/components/NotificationView.svelte +++ b/plugins/notification-resources/src/components/NotificationView.svelte @@ -21,8 +21,9 @@ import notification, { DocUpdates } from '@hcengineering/notification' import { getResource } from '@hcengineering/platform' import { createQuery, getClient } from '@hcengineering/presentation' - import { AnySvelteComponent, Label, TimeSince } from '@hcengineering/ui' + import { AnySvelteComponent, Label, TimeSince, getEventPositionElement, showPopup } from '@hcengineering/ui' import view from '@hcengineering/view' + import { Menu } from '@hcengineering/view-resources' import { createEventDispatcher } from 'svelte' import TxView from './TxView.svelte' @@ -65,6 +66,10 @@ $: docQuery.query(value.attachedToClass, { _id: value.attachedTo }, (res) => ([doc] = res)) $: newTxes = value.txes.length + + function showMenu (e: MouseEvent) { + showPopup(Menu, { object: value, baseMenuClass: value._class }, getEventPositionElement(e)) + } @@ -72,6 +77,7 @@
dispatch('click', { _id: value.attachedTo, _class: value.attachedToClass })} >
diff --git a/plugins/notification-resources/src/index.ts b/plugins/notification-resources/src/index.ts index 6d6ce36e65..c4742a7e5d 100644 --- a/plugins/notification-resources/src/index.ts +++ b/plugins/notification-resources/src/index.ts @@ -18,7 +18,7 @@ import { Resources } from '@hcengineering/platform' import Inbox from './components/Inbox.svelte' import NotificationSettings from './components/NotificationSettings.svelte' import NotificationPresenter from './components/NotificationPresenter.svelte' -import { NotificationClientImpl } from './utils' +import { NotificationClientImpl, hasntNotifications, hide, markAsUnread, unsubscribe } from './utils' export * from './utils' @@ -31,6 +31,12 @@ export default async (): Promise => ({ NotificationSettings }, function: { - GetNotificationClient: NotificationClientImpl.getClient + GetNotificationClient: NotificationClientImpl.getClient, + HasntNotifications: hasntNotifications + }, + actionImpl: { + Unsubscribe: unsubscribe, + Hide: hide, + MarkAsUnread: markAsUnread } }) diff --git a/plugins/notification-resources/src/plugin.ts b/plugins/notification-resources/src/plugin.ts index bbf378dc3c..f26a14df4b 100644 --- a/plugins/notification-resources/src/plugin.ts +++ b/plugins/notification-resources/src/plugin.ts @@ -23,7 +23,6 @@ export default mergeIds(notificationId, notification, { string: { NoNotifications: '' as IntlString, Track: '' as IntlString, - DontTrack: '' as IntlString, Remove: '' as IntlString, RemoveAll: '' as IntlString, MarkAsRead: '' as IntlString, diff --git a/plugins/notification-resources/src/utils.ts b/plugins/notification-resources/src/utils.ts index bc326deb61..0e56e8eca7 100644 --- a/plugins/notification-resources/src/utils.ts +++ b/plugins/notification-resources/src/utils.ts @@ -15,7 +15,7 @@ // import core, { Account, Class, Doc, getCurrentAccount, Ref, Timestamp } from '@hcengineering/core' -import notification, { LastView, NotificationClient } from '@hcengineering/notification' +import notification, { DocUpdates, LastView, NotificationClient } from '@hcengineering/notification' import { createQuery, getClient } from '@hcengineering/presentation' import { get, writable, Writable } from 'svelte/store' @@ -98,3 +98,57 @@ export class NotificationClientImpl implements NotificationClient { } } } + +/** + * @public + */ +export async function hasntNotifications (object: DocUpdates): Promise { + if (object._class !== notification.class.DocUpdates) return false + return object.txes.length === 0 +} + +/** + * @public + */ +export async function unsubscribe (object: DocUpdates): Promise { + const me = getCurrentAccount()._id + const client = getClient() + const hierarchy = client.getHierarchy() + const target = await client.findOne(object.attachedToClass, { _id: object.attachedTo }) + if (target !== undefined) { + if (hierarchy.hasMixin(target, notification.mixin.Collaborators)) { + const collab = hierarchy.as(target, notification.mixin.Collaborators) + if (collab.collaborators.includes(me)) { + await client.updateMixin(collab._id, collab._class, collab.space, notification.mixin.Collaborators, { + $pull: { + collaborators: me + } + }) + } + } + } + await client.remove(object) +} + +/** + * @public + */ +export async function hide (object: DocUpdates): Promise { + const client = getClient() + await client.update(object, { + hidden: true + }) +} + +/** + * @public + */ +export async function markAsUnread (object: DocUpdates): Promise { + const client = getClient() + if (object.txes.length > 0) return + if (object.lastTx !== undefined && object.lastTxTime !== undefined) { + await client.update(object, { + txes: [[object.lastTx, object.lastTxTime]] + }) + } +} diff --git a/plugins/notification/src/index.ts b/plugins/notification/src/index.ts index 782a67fe19..9ff757c425 100644 --- a/plugins/notification/src/index.ts +++ b/plugins/notification/src/index.ts @@ -136,8 +136,9 @@ export interface DocUpdates extends Doc { user: Ref attachedTo: Ref attachedToClass: Ref> - lastTx?: Ref> - lastTxTime?: Timestamp + hidden: boolean + lastTx: Ref> + lastTxTime: Timestamp txes: [Ref>, Timestamp][] } @@ -199,7 +200,8 @@ const notification = plugin(notificationId, { icon: { Notifications: '' as Asset, Track: '' as Asset, - DontTrack: '' as Asset + DontTrack: '' as Asset, + Hide: '' as Asset }, space: { Notifications: '' as Ref @@ -207,6 +209,7 @@ const notification = plugin(notificationId, { string: { Notification: '' as IntlString, Notifications: '' as IntlString, + DontTrack: '' as IntlString, Inbox: '' as IntlString }, function: { diff --git a/plugins/view-assets/lang/ru.json b/plugins/view-assets/lang/ru.json index 2cf76eaef6..59ba4560ff 100644 --- a/plugins/view-assets/lang/ru.json +++ b/plugins/view-assets/lang/ru.json @@ -16,7 +16,7 @@ "Open": "Открыть", "Assignees": "Исполнители", "Labels": "Метки", - "ActionPlaceholder": "введите для филтрации...", + "ActionPlaceholder": "введите для фильтрации...", "General": "Общее", "Navigation": "Навигация", "Editor": "Редактор", diff --git a/plugins/view-resources/src/components/ActionsPopup.svelte b/plugins/view-resources/src/components/ActionsPopup.svelte index e3ca39dcb0..9c83833cb3 100644 --- a/plugins/view-resources/src/components/ActionsPopup.svelte +++ b/plugins/view-resources/src/components/ActionsPopup.svelte @@ -52,7 +52,6 @@ } ) - let visibleActions: WithLookup[] = [] let supportedActions: WithLookup[] = [] let filteredActions: WithLookup[] = [] @@ -70,16 +69,15 @@ } } } - visibleActions = resultActions + return resultActions } - $: filterVisibleActions(actions, getSelection($focusStore, $selectionStore)) const client = getClient() - $: { - let fActions: WithLookup[] = visibleActions - + async function getSupportedActions (actions: WithLookup[]) { const docs = getSelection($focusStore, $selectionStore) + let fActions: WithLookup[] = actions + for (const d of docs) { fActions = filterActions(client, d, fActions) } @@ -91,10 +89,13 @@ (it.$lookup?.category?.visible ?? true) && (it.context.application === viewContext.application || it.context.application === undefined) ) + fActions = await filterVisibleActions(fActions, docs) // Sort by category. supportedActions = fActions.sort((a, b) => a.category.localeCompare(b.category)) } + $: getSupportedActions(actions) + async function filterSearchActions (actions: WithLookup[], search: string): Promise { const res: WithLookup[] = [] search = search.trim().toLowerCase() diff --git a/server-plugins/notification-resources/src/index.ts b/server-plugins/notification-resources/src/index.ts index 7742b8027f..98fa3e55be 100644 --- a/server-plugins/notification-resources/src/index.ts +++ b/server-plugins/notification-resources/src/index.ts @@ -514,7 +514,8 @@ async function createCollabDocInfo ( res.push( control.txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, { lastTx: txId ?? tx._id, - lastTxTime: tx.modifiedOn + lastTxTime: tx.modifiedOn, + hidden: false }) ) } @@ -525,6 +526,7 @@ async function createCollabDocInfo ( user: target, attachedTo: objectId, attachedToClass: objectClass, + hidden: false, lastTx: txId ?? tx._id, lastTxTime: tx.modifiedOn, txes: [[txId ?? tx._id, tx.modifiedOn]]