diff --git a/changelog.md b/changelog.md index 047d18f2a9..89db372a9a 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,10 @@ ## 0.6.28 (upcoming) +Core: + +- Show activity line last view + Tracker: - Issue state history. diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index cbe608d92b..34d419f067 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -9997,7 +9997,7 @@ packages: dev: false file:projects/activity-resources.tgz_1e3963ebf0ceeb25b2fa6a1cc87e253c: - resolution: {integrity: sha512-3qOFOo1wgN0U2L66jIpfdizhweVg+Bt+CblBFjdpQD8anPqMX5Bpm4LbCqQfACtY+XSl92anmiCEYfvQbTL0rw==, tarball: file:projects/activity-resources.tgz} + resolution: {integrity: sha512-GdQlfWQ1T4zhtHShJ/qNtQS6pLovUO9tX3Vn67eqX5qNYDFp0Taf47+DTbYBSd6ZrmtMWhusl8T4LsRQgj5tuw==, tarball: file:projects/activity-resources.tgz} id: file:projects/activity-resources.tgz name: '@rush-temp/activity-resources' version: 0.0.0 @@ -12371,7 +12371,7 @@ packages: dev: false file:projects/notification.tgz: - resolution: {integrity: sha512-T0MHErZvhtTwdclos98GglKOGizrwTcrfU6u0s0Oio37PzD3jzOSvaJkgzePqt0sFee5XY8qXlLm7xLvVW9zPw==, tarball: file:projects/notification.tgz} + resolution: {integrity: sha512-6BJ+zX3Wj70Wk6KKBPvfV6oBMh5/9fijekSDAzhnLKRhJPwuIBD5tCRJcqz4FJn8ZwqhjBMzB1op0rxJa2wYvg==, tarball: file:projects/notification.tgz} name: '@rush-temp/notification' version: 0.0.0 dependencies: @@ -12385,6 +12385,7 @@ packages: eslint-plugin-node: 11.1.0_eslint@7.32.0 eslint-plugin-promise: 5.2.0_eslint@7.32.0 prettier: 2.6.2 + svelte: 3.48.0 typescript: 4.7.2 transitivePeerDependencies: - eslint-import-resolver-typescript diff --git a/plugins/activity-resources/package.json b/plugins/activity-resources/package.json index 3ed319a323..ac04431229 100644 --- a/plugins/activity-resources/package.json +++ b/plugins/activity-resources/package.json @@ -39,6 +39,7 @@ "@anticrm/chunter": "~0.6.1", "@anticrm/text-editor": "~0.6.0", "@anticrm/contact": "~0.6.5", + "@anticrm/notification": "~0.6.0", "@anticrm/view": "~0.6.0", "@anticrm/view-resources": "~0.6.0" } diff --git a/plugins/activity-resources/src/components/Activity.svelte b/plugins/activity-resources/src/components/Activity.svelte index 45f8f898db..b3cbe53ba4 100644 --- a/plugins/activity-resources/src/components/Activity.svelte +++ b/plugins/activity-resources/src/components/Activity.svelte @@ -1,27 +1,29 @@ <!-- -// Copyright © 2020, 2021 Anticrm Platform Contributors. -// Copyright © 2021 Hardcore Engineering Inc. -// +// Copyright © 2022 Hardcore Engineering Inc. +// // Licensed under the Eclipse Public License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. You may // obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// +// // See the License for the specific language governing permissions and // limitations under the License. --> <script lang="ts"> import activity, { TxViewlet } from '@anticrm/activity' import chunter from '@anticrm/chunter' - import core, { Doc, SortingOrder } from '@anticrm/core' + import core, { Doc, Ref, SortingOrder } from '@anticrm/core' + import { getResource } from '@anticrm/platform' import { createQuery, getClient } from '@anticrm/presentation' + import notification from '@anticrm/notification' import { Component, Grid, IconActivity, Label, Scroller } from '@anticrm/ui' import { ActivityKey, activityKey, DisplayTx, newActivity } from '../activity' import TxView from './TxView.svelte' import { filterCollectionTxes } from './utils' + import { Writable } from 'svelte/store' export let object: Doc export let integrate: boolean = false @@ -34,6 +36,10 @@ const attrs = client.getHierarchy().getAllAttributes(object._class) const activityQuery = newActivity(client, attrs) + getResource(notification.function.GetNotificationClient).then((res) => { + lastViews = res().getLastViews() + }) + let lastViews: Writable<Map<Ref<Doc>, number>> | undefined let viewlets: Map<ActivityKey, TxViewlet> @@ -46,18 +52,34 @@ $: viewlets = new Map(allViewlets.map((r) => [activityKey(r.objectClass, r.txClass), r])) - $: activityQuery.update( - object, - (result) => { - txes = filterCollectionTxes(result) - }, - SortingOrder.Descending, - new Map( - allViewlets - .filter((tx) => tx.txClass === core.class.TxCreateDoc) - .map((it) => [it.objectClass, it.editable ?? false]) + function updateTxes (object: Doc): void { + activityQuery.update( + object, + (result) => { + txes = filterCollectionTxes(result) + }, + SortingOrder.Descending, + new Map( + allViewlets + .filter((tx) => tx.txClass === core.class.TxCreateDoc) + .map((it) => [it.objectClass, it.editable ?? false]) + ) ) - ) + } + + $: updateTxes(object) + + $: newTxPos = newTx(txes, $lastViews) + + function newTx (txes: DisplayTx[], lastViews: Map<Ref<Doc>, number> | undefined): number { + const lastView = lastViews?.get(object._id) + if (lastView === undefined || lastView === -1) return -1 + for (let index = 0; index < txes.length; index++) { + const tx = txes[index] + if (tx.tx.modifiedOn <= lastView) return index - 1 + } + return -1 + } </script> {#if !integrate || transparent} @@ -74,8 +96,8 @@ <div class="p-10 select-text" id={activity.string.Activity}> {#if txes} <Grid column={1} rowGap={1.5}> - {#each txes as tx} - <TxView {tx} {viewlets} /> + {#each txes as tx, i} + <TxView {tx} {viewlets} isNew={newTxPos === i} /> {/each} </Grid> {/if} @@ -104,8 +126,8 @@ <div class="p-activity select-text" id={activity.string.Activity}> {#if txes} <Grid column={1} rowGap={1.5}> - {#each txes as tx} - <TxView {tx} {viewlets} /> + {#each txes as tx, i} + <TxView {tx} {viewlets} isNew={newTxPos === i} /> {/each} </Grid> {/if} diff --git a/plugins/activity-resources/src/components/TxView.svelte b/plugins/activity-resources/src/components/TxView.svelte index ae60b95ee2..2bf18e940d 100644 --- a/plugins/activity-resources/src/components/TxView.svelte +++ b/plugins/activity-resources/src/components/TxView.svelte @@ -41,6 +41,7 @@ export let tx: DisplayTx export let viewlets: Map<ActivityKey, TxViewlet> export let showIcon: boolean = true + export let isNew: boolean = false let ptx: DisplayTx | undefined @@ -152,7 +153,7 @@ </div> {/if} - <div class="flex-grow flex-col clear-mins"> + <div class="flex-grow flex-col clear-mins" class:isNew> <div class="flex-between"> <div class="flex-row-center flex-grow label"> <div class="bold"> @@ -317,6 +318,11 @@ min-width: 0; } + .isNew { + padding-bottom: 0.25rem; + border-bottom: 1px solid var(--highlight-red); + } + .showIcon { &::after, &::before { diff --git a/plugins/notification/package.json b/plugins/notification/package.json index 317c151066..72bc4cd01f 100644 --- a/plugins/notification/package.json +++ b/plugins/notification/package.json @@ -28,6 +28,7 @@ "dependencies": { "@anticrm/platform": "~0.6.6", "@anticrm/core": "~0.6.16", - "@anticrm/ui": "~0.6.0" + "@anticrm/ui": "~0.6.0", + "svelte": "^3.47" } } diff --git a/plugins/notification/src/index.ts b/plugins/notification/src/index.ts index 917f857492..98318c3596 100644 --- a/plugins/notification/src/index.ts +++ b/plugins/notification/src/index.ts @@ -1,6 +1,5 @@ // -// Copyright © 2020, 2021 Anticrm Platform Contributors. -// Copyright © 2021, 2022 Hardcore Engineering Inc. +// Copyright © 2022 Hardcore Engineering Inc. // // Licensed under the Eclipse Public License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. You may @@ -18,6 +17,7 @@ import type { Account, AttachedDoc, Class, Doc, Mixin, Ref, Space, Timestamp, Tx import type { Asset, IntlString, Plugin, Resource } from '@anticrm/platform' import { plugin } from '@anticrm/platform' import { AnyComponent } from '@anticrm/ui' +import { Writable } from 'svelte/store' /** * @public @@ -107,6 +107,7 @@ export const notificationId = 'notification' as Plugin * @public */ export interface NotificationClient { + getLastViews: () => Writable<Map<Ref<Doc>, Timestamp>> updateLastView: (_id: Ref<Doc>, _class: Ref<Class<Doc>>, time?: Timestamp, force?: boolean) => Promise<void> unsubscribe: (_id: Ref<Doc>) => Promise<void> }