From 13b6459c95244c9c7eaa430b3d2047db2ee68fc3 Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Fri, 16 Sep 2022 09:57:32 +0700 Subject: [PATCH] Tracker assign notification (#2269) Signed-off-by: Andrey Sobolev --- models/notification/src/index.ts | 2 +- models/server-lead/src/index.ts | 5 + models/server-recruit/src/index.ts | 2 +- models/task/src/index.ts | 9 + packages/platform/src/status.ts | 2 +- packages/theme/styles/popups.scss | 4 +- packages/ui/src/components/ActionIcon.svelte | 3 +- packages/ui/src/components/Icon.svelte | 5 +- packages/ui/src/components/icons/Back.svelte | 15 +- .../src/components/TxView.svelte | 9 +- .../src/components/EventPresenter.svelte | 7 +- .../activity/ReminderViewlet.svelte | 8 +- plugins/notification-assets/lang/en.json | 6 +- plugins/notification-assets/lang/ru.json | 6 +- plugins/notification-resources/package.json | 3 +- .../components/BrowserNotificatator.svelte | 47 +++- .../src/components/NotificationView.svelte | 93 ++++++-- .../src/components/NotificationsPopup.svelte | 58 ++++- plugins/notification-resources/src/plugin.ts | 6 +- plugins/task/package.json | 3 +- plugins/task/src/index.ts | 4 + plugins/tracker-resources/src/plugin.ts | 4 +- plugins/tracker/src/index.ts | 4 +- .../src/components/filter/ObjectFilter.svelte | 3 +- rush.json | 4 +- server-plugins/lead-resources/package.json | 4 +- server-plugins/lead-resources/src/index.ts | 77 +++++- server-plugins/lead/package.json | 4 +- server-plugins/lead/src/index.ts | 8 +- server-plugins/recruit-resources/package.json | 3 +- server-plugins/recruit-resources/src/index.ts | 220 +++++++++++------- server-plugins/recruit/src/index.ts | 2 +- server-plugins/task-resources/package.json | 4 +- server-plugins/task-resources/src/index.ts | 89 ++++++- server-plugins/tracker-resources/package.json | 6 +- server-plugins/tracker-resources/src/index.ts | 41 +++- 36 files changed, 599 insertions(+), 171 deletions(-) diff --git a/models/notification/src/index.ts b/models/notification/src/index.ts index e630099cba..9244fba8f9 100644 --- a/models/notification/src/index.ts +++ b/models/notification/src/index.ts @@ -146,7 +146,7 @@ export function createModel (builder: Builder): void { core.space.Model, { label: notification.string.BrowserNotification, - default: false + default: true }, notification.ids.BrowserNotification ) diff --git a/models/server-lead/src/index.ts b/models/server-lead/src/index.ts index bf7c0a07e4..d245da5be4 100644 --- a/models/server-lead/src/index.ts +++ b/models/server-lead/src/index.ts @@ -19,6 +19,7 @@ import core from '@anticrm/core' import lead from '@anticrm/lead' import view from '@anticrm/view' import serverLead from '@anticrm/server-lead' +import serverCore from '@anticrm/server-core' export function createModel (builder: Builder): void { builder.mixin(lead.class.Lead, core.class.Class, view.mixin.HTMLPresenter, { @@ -28,4 +29,8 @@ export function createModel (builder: Builder): void { builder.mixin(lead.class.Lead, core.class.Class, view.mixin.TextPresenter, { presenter: serverLead.function.LeadTextPresenter }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverLead.trigger.OnLeadUpdate + }) } diff --git a/models/server-recruit/src/index.ts b/models/server-recruit/src/index.ts index fb6b467f4b..566dd73da1 100644 --- a/models/server-recruit/src/index.ts +++ b/models/server-recruit/src/index.ts @@ -39,6 +39,6 @@ export function createModel (builder: Builder): void { }) builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverRecruit.trigger.OnVacancyUpdate + trigger: serverRecruit.trigger.OnRecruitUpdate }) } diff --git a/models/task/src/index.ts b/models/task/src/index.ts index bd0e9d5895..683b35d633 100644 --- a/models/task/src/index.ts +++ b/models/task/src/index.ts @@ -565,4 +565,13 @@ export function createModel (builder: Builder): void { }, task.action.ArchiveState ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + label: task.string.Assigned + }, + task.ids.AssigneedNotification + ) } diff --git a/packages/platform/src/status.ts b/packages/platform/src/status.ts index 47cdf105d0..24fc52a8c6 100644 --- a/packages/platform/src/status.ts +++ b/packages/platform/src/status.ts @@ -36,7 +36,7 @@ export enum Severity { * Status of an operation * @public */ -export class Status

{ +export class Status

= {}> { readonly severity: Severity readonly code: StatusCode

readonly params: P diff --git a/packages/theme/styles/popups.scss b/packages/theme/styles/popups.scss index 62f7ca206a..fbde2f8fc8 100644 --- a/packages/theme/styles/popups.scss +++ b/packages/theme/styles/popups.scss @@ -418,8 +418,8 @@ width: max-content; height: max-content; padding-bottom: 0.5rem; - min-width: 32rem; - max-width: 32rem; + min-width: 42rem; + max-width: 42rem; min-height: 22rem; max-height: 22rem; background: var(--popup-bg-color); diff --git a/packages/ui/src/components/ActionIcon.svelte b/packages/ui/src/components/ActionIcon.svelte index 65851711bc..851c1e1460 100644 --- a/packages/ui/src/components/ActionIcon.svelte +++ b/packages/ui/src/components/ActionIcon.svelte @@ -23,6 +23,7 @@ export let labelProps: any = undefined export let direction: TooltipAlignment | undefined = undefined export let icon: Asset | AnySvelteComponent + export let iconProps: any | undefined = undefined export let size: 'small' | 'medium' | 'large' export let action: (ev: MouseEvent) => Promise | void = async () => {} export let invisible: boolean = false @@ -35,7 +36,7 @@ on:click|stopPropagation|preventDefault={action} >

- +
diff --git a/packages/ui/src/components/Icon.svelte b/packages/ui/src/components/Icon.svelte index f4feb8182b..d4bc835ab5 100644 --- a/packages/ui/src/components/Icon.svelte +++ b/packages/ui/src/components/Icon.svelte @@ -19,6 +19,7 @@ export let icon: Asset | AnySvelteComponent export let size: IconSize export let fill = 'currentColor' + export let iconProps: any | undefined = undefined function isAsset (icon: Asset | AnySvelteComponent): boolean { return typeof icon === 'string' @@ -36,6 +37,6 @@ -{:else} - +{:else if typeof icon !== 'string'} + {/if} diff --git a/packages/ui/src/components/icons/Back.svelte b/packages/ui/src/components/icons/Back.svelte index ee1c593aaa..5db2cc6e70 100644 --- a/packages/ui/src/components/icons/Back.svelte +++ b/packages/ui/src/components/icons/Back.svelte @@ -16,8 +16,17 @@ - - - +{#if kind === 'strong'} + + + +{:else if kind === 'curve'} + + + +{/if} diff --git a/plugins/activity-resources/src/components/TxView.svelte b/plugins/activity-resources/src/components/TxView.svelte index d074b178be..5fda4e5d80 100644 --- a/plugins/activity-resources/src/components/TxView.svelte +++ b/plugins/activity-resources/src/components/TxView.svelte @@ -42,6 +42,7 @@ export let viewlets: Map export let showIcon: boolean = true export let isNew: boolean = false + export let showDocument = false let ptx: DisplayTx | undefined @@ -212,8 +213,8 @@ {/if}
1}> - {#each value.added as value} - + {#each value.added as cvalue} + {/each}
@@ -231,8 +232,8 @@ {/if}
1}> - {#each value.removed as value} - + {#each value.removed as cvalue} + {/each}
diff --git a/plugins/calendar-resources/src/components/EventPresenter.svelte b/plugins/calendar-resources/src/components/EventPresenter.svelte index 593161c967..9389b844ff 100644 --- a/plugins/calendar-resources/src/components/EventPresenter.svelte +++ b/plugins/calendar-resources/src/components/EventPresenter.svelte @@ -18,17 +18,20 @@ import view from '@anticrm/view' export let value: Event + export let inline: boolean = false function click (): void { showPanel(view.component.EditDoc, value._id, value._class, 'content') } -
+
{#if value}
{value.title}
- + {#if !inline} + + {/if} {/if}
diff --git a/plugins/calendar-resources/src/components/activity/ReminderViewlet.svelte b/plugins/calendar-resources/src/components/activity/ReminderViewlet.svelte index 89446a5718..0393fec168 100644 --- a/plugins/calendar-resources/src/components/activity/ReminderViewlet.svelte +++ b/plugins/calendar-resources/src/components/activity/ReminderViewlet.svelte @@ -37,11 +37,13 @@ {#await getEvent(tx.objectId) then event} {#if event} { click(event) - }}>{event.title}  + }} + >{event.title} + +   {/if} {/await} diff --git a/plugins/notification-assets/lang/en.json b/plugins/notification-assets/lang/en.json index 74064664d1..67448d896c 100644 --- a/plugins/notification-assets/lang/en.json +++ b/plugins/notification-assets/lang/en.json @@ -9,6 +9,10 @@ "PlatformNotification": "in platform", "Track": "Track", "DontTrack": "Don't track", - "BrowserNotification": "in browser" + "BrowserNotification": "in browser", + "Remove": "Delete notification", + "RemoveAll": "Delete all notifications", + "MarkAllAsRead": "Mark all notifications as read", + "MarkAsRead": "Mark as read" } } \ No newline at end of file diff --git a/plugins/notification-assets/lang/ru.json b/plugins/notification-assets/lang/ru.json index 37d64f395f..16efc694d5 100644 --- a/plugins/notification-assets/lang/ru.json +++ b/plugins/notification-assets/lang/ru.json @@ -9,6 +9,10 @@ "PlatformNotification": "в системе", "Track": "Отслеживать", "DontTrack": "Не отслеживать", - "BrowserNotification": "в браузере" + "BrowserNotification": "в браузере", + "Remove": "Удалить нотификацию", + "RemoveAll": "Удалить все нотификации", + "MarkAllAsRead": "Отметить все нотификации как прочитанные", + "MarkAsRead": "Отметить нотификация прочитанной" } } \ No newline at end of file diff --git a/plugins/notification-resources/package.json b/plugins/notification-resources/package.json index e092653da3..aab630a7bd 100644 --- a/plugins/notification-resources/package.json +++ b/plugins/notification-resources/package.json @@ -39,6 +39,7 @@ "@anticrm/activity-resources": "~0.6.0", "@anticrm/activity": "~0.6.0", "@anticrm/contact": "~0.6.5", - "@anticrm/core": "~0.6.16" + "@anticrm/core": "~0.6.16", + "@anticrm/view": "~0.6.0" } } diff --git a/plugins/notification-resources/src/components/BrowserNotificatator.svelte b/plugins/notification-resources/src/components/BrowserNotificatator.svelte index 665734f26d..13ca6422d1 100644 --- a/plugins/notification-resources/src/components/BrowserNotificatator.svelte +++ b/plugins/notification-resources/src/components/BrowserNotificatator.svelte @@ -23,6 +23,7 @@ NotificationType } from '@anticrm/notification' import { createQuery } from '@anticrm/presentation' + import { getCurrentLocation } from '@anticrm/ui' import notification from '../plugin' import { NotificationClientImpl } from '../utils' @@ -39,7 +40,7 @@ let settings: Map, NotificationSetting> = new Map, NotificationSetting>() let provider: NotificationProvider | undefined - const enabled = 'Notification' in window && Notification.permission !== 'denied' + $: enabled = 'Notification' in window && Notification?.permission !== 'denied' $: enabled && providersQuery.query( @@ -66,6 +67,8 @@ } ) + const alreadyShown = new Set>() + $: enabled && settingsReceived && provider !== undefined && @@ -76,7 +79,12 @@ status: NotificationStatus.New }, (res) => { - process(res) + process(res.reverse()) + }, + { + sort: { + modifiedOn: 1 + } } ) @@ -93,25 +101,40 @@ const enabled = setting?.enabled ?? provider?.default if (!enabled) return if ((setting?.modifiedOn ?? notification.modifiedOn) < 0) return - if (Notification.permission === 'granted') { + + if (Notification?.permission !== 'granted') { + await Notification?.requestPermission() + } + + if (Notification?.permission === 'granted') { await notify(text, notification) - } else if (Notification.permission !== 'denied') { - const permission = await Notification.requestPermission() - if (permission === 'granted') { - await notify(text, notification) - } } } - async function notify (text: string, notification: PlatformNotification): Promise { + let clearTimer: number | undefined + + async function notify (text: string, notifyInstance: PlatformNotification): Promise { + if (alreadyShown.has(notifyInstance._id)) { + return + } + alreadyShown.add(notifyInstance._id) + + if (clearTimer) { + clearTimeout(clearTimer) + } + + clearTimer = setTimeout(() => { + alreadyShown.clear() + }, 5000) + const lastView = $lastViews.get(lastViewId) - if ((lastView ?? notification.modifiedOn) > 0) { + if ((lastView ?? notifyInstance.modifiedOn) > 0) { // eslint-disable-next-line - new Notification(text, { tag: notification._id }) + new Notification(getCurrentLocation().path[1], { tag: notifyInstance._id, icon: '/favicon.png', body: text }) await notificationClient.updateLastView( lastViewId, contact.class.Employee, - notification.modifiedOn, + notifyInstance.modifiedOn, lastView === undefined ) } diff --git a/plugins/notification-resources/src/components/NotificationView.svelte b/plugins/notification-resources/src/components/NotificationView.svelte index 72f2d89e9f..83e9603900 100644 --- a/plugins/notification-resources/src/components/NotificationView.svelte +++ b/plugins/notification-resources/src/components/NotificationView.svelte @@ -16,46 +16,82 @@ -{#await getDisplayTx(notification) then displayTx} - {#if displayTx} - -
{ - read(notification) - }} - > +{#if displayTx} + {@const isNew = notification.status === NotificationStatus.New} + +
+
+
+
+
+
+ +
+
+ { + client.remove(notification) + }} + /> + { + changeState(notification, isNew ? NotificationStatus.Read : NotificationStatus.New) + }} + /> +
+
+
- {/if} -{/await} +
+{/if} diff --git a/plugins/notification-resources/src/components/NotificationsPopup.svelte b/plugins/notification-resources/src/components/NotificationsPopup.svelte index 31ddedf098..6315896f6f 100644 --- a/plugins/notification-resources/src/components/NotificationsPopup.svelte +++ b/plugins/notification-resources/src/components/NotificationsPopup.svelte @@ -17,16 +17,17 @@ import activity, { TxViewlet } from '@anticrm/activity' import { activityKey, ActivityKey } from '@anticrm/activity-resources' import { EmployeeAccount } from '@anticrm/contact' - import { getCurrentAccount, SortingOrder } from '@anticrm/core' - import type { Notification } from '@anticrm/notification' - import { createQuery } from '@anticrm/presentation' - import { Scroller } from '@anticrm/ui' + import core, { getCurrentAccount, WithLookup } from '@anticrm/core' + import { Notification, NotificationStatus } from '@anticrm/notification' + import { createQuery, getClient } from '@anticrm/presentation' + import { ActionIcon, IconCheck, IconDelete, Scroller } from '@anticrm/ui' import Label from '@anticrm/ui/src/components/Label.svelte' import notification from '../plugin' import NotificationView from './NotificationView.svelte' const query = createQuery() - let notifications: Notification[] = [] + let notifications: WithLookup[] = [] + const client = getClient() $: query.query( notification.class.Notification, @@ -37,7 +38,13 @@ notifications = res }, { - sort: { status: SortingOrder.Ascending, modifiedOn: SortingOrder.Descending } + sort: { + '$lookup.tx.modifiedOn': -1 + }, + limit: 30, + lookup: { + tx: core.class.TxCUD + } } ) @@ -47,16 +54,51 @@ $: descriptors.query(activity.class.TxViewlet, {}, (result) => { viewlets = new Map(result.map((r) => [activityKey(r.objectClass, r.txClass), r])) }) + + const deleteNotifications = async () => { + const allNotifications = await client.findAll(notification.class.Notification, { + attachedTo: (getCurrentAccount() as EmployeeAccount).employee + }) + for (const n of allNotifications) { + await client.remove(n) + } + } + const markAsReadNotifications = async () => { + const allNotifications = await client.findAll(notification.class.Notification, { + attachedTo: (getCurrentAccount() as EmployeeAccount).employee + }) + for (const n of allNotifications) { + if (n.status !== NotificationStatus.Read) { + await client.updateDoc(n._class, n.space, n._id, { + status: NotificationStatus.Read + }) + } + } + }
-
+
+
+ + +
{#if notifications.length > 0}
- {#each notifications as n (n._id)} + {#each notifications as n} {/each}
diff --git a/plugins/notification-resources/src/plugin.ts b/plugins/notification-resources/src/plugin.ts index 2ddcde5058..445512174c 100644 --- a/plugins/notification-resources/src/plugin.ts +++ b/plugins/notification-resources/src/plugin.ts @@ -23,6 +23,10 @@ export default mergeIds(notificationId, notification, { string: { NoNotifications: '' as IntlString, Track: '' as IntlString, - DontTrack: '' as IntlString + DontTrack: '' as IntlString, + Remove: '' as IntlString, + RemoveAll: '' as IntlString, + MarkAsRead: '' as IntlString, + MarkAllAsRead: '' as IntlString } }) diff --git a/plugins/task/package.json b/plugins/task/package.json index 14c93b3fbe..6f24bd5f20 100644 --- a/plugins/task/package.json +++ b/plugins/task/package.json @@ -31,6 +31,7 @@ "@anticrm/contact": "~0.6.5", "@anticrm/view": "~0.6.0", "@anticrm/ui": "~0.6.0", - "lexorank": "~1.0.4" + "lexorank": "~1.0.4", + "@anticrm/notification": "~0.6.0" } } diff --git a/plugins/task/src/index.ts b/plugins/task/src/index.ts index 256e6949f4..9f1a71aadd 100644 --- a/plugins/task/src/index.ts +++ b/plugins/task/src/index.ts @@ -32,6 +32,7 @@ import { plugin } from '@anticrm/platform' import type { AnyComponent } from '@anticrm/ui' import { ViewletDescriptor } from '@anticrm/view' import { genRanks } from './utils' +import { NotificationType } from '@anticrm/notification' /** * @public @@ -270,6 +271,9 @@ const task = plugin(taskId, { KanbanTemplateEditor: '' as AnyComponent, KanbanTemplateSelector: '' as AnyComponent, TodoItemsPopup: '' as AnyComponent + }, + ids: { + AssigneedNotification: '' as Ref } }) diff --git a/plugins/tracker-resources/src/plugin.ts b/plugins/tracker-resources/src/plugin.ts index 05c93bebca..c16927f6ca 100644 --- a/plugins/tracker-resources/src/plugin.ts +++ b/plugins/tracker-resources/src/plugin.ts @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. // +import { Client, Doc, Ref } from '@anticrm/core' import type { IntlString, Resource } from '@anticrm/platform' import { mergeIds } from '@anticrm/platform' -import tracker, { trackerId } from '../../tracker/lib' import { AnyComponent } from '@anticrm/ui' -import { Client, Doc, Ref } from '@anticrm/core' +import tracker, { trackerId } from '../../tracker/lib' export default mergeIds(trackerId, tracker, { string: { diff --git a/plugins/tracker/src/index.ts b/plugins/tracker/src/index.ts index 3db33eb93a..6efcdf6b40 100644 --- a/plugins/tracker/src/index.ts +++ b/plugins/tracker/src/index.ts @@ -15,11 +15,11 @@ import { Employee } from '@anticrm/contact' import type { AttachedDoc, Class, Doc, Markup, Ref, RelatedDocument, Space, Timestamp, Type } from '@anticrm/core' -import { Action, ActionCategory } from '@anticrm/view' import type { Asset, IntlString, Plugin, Resource } from '@anticrm/platform' import { plugin } from '@anticrm/platform' -import { AnyComponent, Location } from '@anticrm/ui' import type { TagCategory } from '@anticrm/tags' +import { AnyComponent, Location } from '@anticrm/ui' +import { Action, ActionCategory } from '@anticrm/view' /** * @public diff --git a/plugins/view-resources/src/components/filter/ObjectFilter.svelte b/plugins/view-resources/src/components/filter/ObjectFilter.svelte index a9a61c4508..1868ffd677 100644 --- a/plugins/view-resources/src/components/filter/ObjectFilter.svelte +++ b/plugins/view-resources/src/components/filter/ObjectFilter.svelte @@ -13,7 +13,7 @@ // limitations under the License. -->