mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-12 04:37:44 +02:00
TSK-1341 Remove last view (#3115)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
@@ -22,9 +22,8 @@
|
||||
export let size: IconSize = 'small'
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
$: lastView = $lastViews[object._id]
|
||||
$: subscribed = lastView !== undefined && lastView !== -1
|
||||
const store = notificationClient.docUpdatesStore
|
||||
$: subscribed = $store.get(object._id) !== undefined
|
||||
</script>
|
||||
|
||||
{#if subscribed}
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
<script lang="ts">
|
||||
import attachment, { Attachment } from '@hcengineering/attachment'
|
||||
import type { ChunterMessage, Message } from '@hcengineering/chunter'
|
||||
import core, { Ref, Space, Timestamp, WithLookup } from '@hcengineering/core'
|
||||
import { LastView } from '@hcengineering/notification'
|
||||
import core, { Doc, Ref, Space, Timestamp, WithLookup } from '@hcengineering/core'
|
||||
import { DocUpdates } from '@hcengineering/notification'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { location as locationStore } from '@hcengineering/ui'
|
||||
@@ -69,11 +69,11 @@
|
||||
}
|
||||
})
|
||||
|
||||
let messages: WithLookup<Message>[] | undefined
|
||||
let messages: WithLookup<Message>[] = []
|
||||
const query = createQuery()
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const docUpdates = notificationClient.docUpdatesStore
|
||||
|
||||
$: updateQuery(space)
|
||||
|
||||
@@ -90,8 +90,8 @@
|
||||
},
|
||||
(res) => {
|
||||
messages = res
|
||||
newMessagesPos = newMessagesStart(messages)
|
||||
notificationClient.updateLastView(space, chunter.class.ChunterSpace)
|
||||
newMessagesPos = newMessagesStart(messages, $docUpdates)
|
||||
notificationClient.read(space)
|
||||
},
|
||||
{
|
||||
lookup: {
|
||||
@@ -102,22 +102,22 @@
|
||||
)
|
||||
}
|
||||
|
||||
function newMessagesStart (messages: Message[]): number {
|
||||
function newMessagesStart (messages: Message[], docUpdates: Map<Ref<Doc>, DocUpdates>): number {
|
||||
if (space === undefined) return -1
|
||||
const lastView = ($lastViews as any)[space]
|
||||
if (lastView === undefined || lastView === -1) return -1
|
||||
const docUpdate = docUpdates.get(space)
|
||||
const lastView = docUpdate?.txes?.[0]?.[1]
|
||||
if (docUpdate === undefined || lastView === undefined) return -1
|
||||
for (let index = 0; index < messages.length; index++) {
|
||||
const message = messages[index]
|
||||
if (message.createOn > lastView) return index
|
||||
if (message.createOn >= lastView) return index
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
$: markUnread($lastViews)
|
||||
function markUnread (lastViews: LastView) {
|
||||
if (messages === undefined) return
|
||||
const newPos = newMessagesStart(messages)
|
||||
if (newPos !== -1 || newMessagesPos === -1) {
|
||||
$: markUnread(messages, $docUpdates)
|
||||
function markUnread (messages: Message[], docUpdates: Map<Ref<Doc>, DocUpdates>) {
|
||||
const newPos = newMessagesStart(messages, docUpdates)
|
||||
if (newPos !== -1) {
|
||||
newMessagesPos = newPos
|
||||
}
|
||||
}
|
||||
@@ -165,7 +165,7 @@
|
||||
}
|
||||
|
||||
let showFixed: boolean | undefined
|
||||
let selectedDate: Timestamp | undefined = messages ? getDay(messages[0].createOn) : undefined
|
||||
let selectedDate: Timestamp | undefined = undefined
|
||||
function handleScroll () {
|
||||
const upperVisible = getFirstVisible()
|
||||
if (upperVisible) {
|
||||
|
||||
@@ -18,15 +18,7 @@
|
||||
import type { ChunterSpace, Message, ThreadMessage } from '@hcengineering/chunter'
|
||||
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import core, {
|
||||
FindOptions,
|
||||
generateId,
|
||||
getCurrentAccount,
|
||||
IdMap,
|
||||
Ref,
|
||||
SortingOrder,
|
||||
TxFactory
|
||||
} from '@hcengineering/core'
|
||||
import core, { FindOptions, IdMap, Ref, SortingOrder, generateId, getCurrentAccount } from '@hcengineering/core'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
@@ -97,7 +89,7 @@
|
||||
} else {
|
||||
comments = res.reverse()
|
||||
}
|
||||
notificationClient.updateLastView(id, chunter.class.Message)
|
||||
notificationClient.read(id)
|
||||
},
|
||||
options
|
||||
)
|
||||
@@ -130,8 +122,7 @@
|
||||
if (parent === undefined) return
|
||||
const { message, attachments } = event.detail
|
||||
const me = getCurrentAccount()._id
|
||||
const txFactory = new TxFactory(me)
|
||||
const tx = txFactory.createTxCreateDoc<ThreadMessage>(
|
||||
await client.createDoc(
|
||||
chunter.class.ThreadMessage,
|
||||
parent.space,
|
||||
{
|
||||
@@ -145,9 +136,6 @@
|
||||
},
|
||||
commentId
|
||||
)
|
||||
tx.attributes.createOn = tx.modifiedOn
|
||||
await notificationClient.updateLastView(_id, chunter.class.Message, tx.modifiedOn, true)
|
||||
await client.tx(tx)
|
||||
|
||||
// Create an backlink to document
|
||||
await createBacklinks(client, parent._id, parent._class, commentId, message)
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
import attachment, { Attachment } from '@hcengineering/attachment'
|
||||
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
|
||||
import type { ChunterMessage, Message, ThreadMessage } from '@hcengineering/chunter'
|
||||
import core, { Ref, Space, generateId, getCurrentAccount } from '@hcengineering/core'
|
||||
import { LastView } from '@hcengineering/notification'
|
||||
import core, { Doc, Ref, Space, generateId, getCurrentAccount } from '@hcengineering/core'
|
||||
import { DocUpdates } from '@hcengineering/notification'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { IconClose, Label, getCurrentResolvedLocation, navigate } from '@hcengineering/ui'
|
||||
@@ -59,7 +59,7 @@
|
||||
})
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const docUpdates = notificationClient.docUpdatesStore
|
||||
|
||||
const lookup = {
|
||||
_id: { attachments: attachment.class.Attachment, reactions: chunter.class.Reaction },
|
||||
@@ -101,8 +101,8 @@
|
||||
},
|
||||
(res) => {
|
||||
comments = res
|
||||
newMessagesPos = newMessagesStart(comments, $lastViews)
|
||||
notificationClient.updateLastView(id, chunter.class.Message)
|
||||
newMessagesPos = newMessagesStart(comments, $docUpdates)
|
||||
notificationClient.read(id)
|
||||
},
|
||||
{
|
||||
lookup
|
||||
@@ -159,23 +159,25 @@
|
||||
}
|
||||
let comments: ThreadMessage[] = []
|
||||
|
||||
function newMessagesStart (comments: ThreadMessage[], lastViews: LastView): number {
|
||||
const lastView = (lastViews as any)[_id]
|
||||
if (lastView === undefined || lastView === -1) return -1
|
||||
function newMessagesStart (comments: ThreadMessage[], docUpdates: Map<Ref<Doc>, DocUpdates>): number {
|
||||
const docUpdate = docUpdates.get(_id)
|
||||
const lastView = docUpdate?.txes?.[0]?.[1]
|
||||
if (docUpdate === undefined || lastView === undefined) return -1
|
||||
for (let index = 0; index < comments.length; index++) {
|
||||
const comment = comments[index]
|
||||
if (comment.createOn > lastView) return index
|
||||
if (comment.createOn >= lastView) return index
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
$: markUnread($lastViews)
|
||||
function markUnread (lastViews: LastView) {
|
||||
const newPos = newMessagesStart(comments, lastViews)
|
||||
if (newPos !== -1 || newMessagesPos === -1) {
|
||||
$: markUnread(comments, $docUpdates)
|
||||
function markUnread (comments: ThreadMessage[], docUpdates: Map<Ref<Doc>, DocUpdates>) {
|
||||
const newPos = newMessagesStart(comments, docUpdates)
|
||||
if (newPos !== -1) {
|
||||
newMessagesPos = newPos
|
||||
}
|
||||
}
|
||||
|
||||
let newMessagesPos: number = -1
|
||||
let loading = false
|
||||
</script>
|
||||
|
||||
@@ -67,12 +67,12 @@ export { CommentPopup, CommentsPresenter }
|
||||
|
||||
async function MarkUnread (object: Message): Promise<void> {
|
||||
const client = NotificationClientImpl.getClient()
|
||||
await client.updateLastView(object.space, chunter.class.ChunterSpace, object.createOn - 1, true)
|
||||
await client.forceRead(object.space, chunter.class.ChunterSpace)
|
||||
}
|
||||
|
||||
async function MarkCommentUnread (object: ThreadMessage): Promise<void> {
|
||||
const client = NotificationClientImpl.getClient()
|
||||
await client.updateLastView(object.attachedTo, object.attachedToClass, object.createOn - 1, true)
|
||||
await client.forceRead(object.attachedTo, object.attachedToClass)
|
||||
}
|
||||
|
||||
async function SubscribeMessage (object: Message): Promise<void> {
|
||||
@@ -117,14 +117,20 @@ async function UnsubscribeMessage (object: ChunterMessage): Promise<void> {
|
||||
}
|
||||
}
|
||||
)
|
||||
await notificationClient.unsubscribe(object.attachedTo)
|
||||
const docUpdate = notificationClient.docUpdatesMap.get(object.attachedTo)
|
||||
if (docUpdate !== undefined) {
|
||||
await client.remove(docUpdate)
|
||||
}
|
||||
} else {
|
||||
await client.updateMixin(object._id, object._class, object.space, notification.mixin.Collaborators, {
|
||||
$pull: {
|
||||
collaborators: acc._id
|
||||
}
|
||||
})
|
||||
await notificationClient.unsubscribe(object._id)
|
||||
const docUpdate = notificationClient.docUpdatesMap.get(object._id)
|
||||
if (docUpdate !== undefined) {
|
||||
await client.remove(docUpdate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import type { Channel, ChannelProvider } from '@hcengineering/contact'
|
||||
import contact from '@hcengineering/contact'
|
||||
import type { AttachedData, Doc, Ref } from '@hcengineering/core'
|
||||
import notification, { LastView } from '@hcengineering/notification'
|
||||
import notification, { DocUpdates } from '@hcengineering/notification'
|
||||
import { Asset, IntlString, getResource } from '@hcengineering/platform'
|
||||
import presentation from '@hcengineering/presentation'
|
||||
import {
|
||||
@@ -50,8 +50,8 @@
|
||||
export let focusIndex = -1
|
||||
export let restricted: Ref<ChannelProvider>[] = []
|
||||
|
||||
let lastViews: Writable<LastView | undefined> = writable()
|
||||
getResource(notification.function.GetNotificationClient).then((res) => (lastViews = res().getLastViews()))
|
||||
let docUpdates: Writable<Map<Ref<Doc>, DocUpdates>> = writable(new Map())
|
||||
getResource(notification.function.GetNotificationClient).then((res) => (docUpdates = res().docUpdatesStore))
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
interface Item {
|
||||
@@ -70,11 +70,11 @@
|
||||
function getProvider (
|
||||
item: AttachedData<Channel>,
|
||||
map: Map<Ref<ChannelProvider>, ChannelProvider>,
|
||||
lastViews: LastView | undefined
|
||||
docUpdates: Map<Ref<Doc>, DocUpdates>
|
||||
): Item | undefined {
|
||||
const provider = map.get(item.provider)
|
||||
if (provider) {
|
||||
const notification = (item as Channel)._id !== undefined ? isNew(item as Channel, lastViews) : false
|
||||
const notification = (item as Channel)._id !== undefined ? isNew(item as Channel, docUpdates) : false
|
||||
return {
|
||||
label: provider.label,
|
||||
icon: provider.icon as Asset,
|
||||
@@ -92,16 +92,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
function isNew (item: Channel, lastViews: LastView | undefined): boolean {
|
||||
if (item.lastMessage === undefined) return false
|
||||
const lastView =
|
||||
(item as Channel)._id !== undefined && lastViews !== undefined ? lastViews[(item as Channel)._id] : undefined
|
||||
return lastView ? lastView < item.lastMessage : (item.items ?? 0) > 0
|
||||
function isNew (item: Channel, docUpdates: Map<Ref<Doc>, DocUpdates>): boolean {
|
||||
const docUpdate = docUpdates.get(item._id)
|
||||
return docUpdate ? docUpdate.txes.length > 0 : (item.items ?? 0) > 0
|
||||
}
|
||||
|
||||
async function update (
|
||||
value: AttachedData<Channel>[] | Channel | null,
|
||||
lastViews: LastView | undefined,
|
||||
docUpdates: Map<Ref<Doc>, DocUpdates>,
|
||||
channelProviders: ChannelProvider[]
|
||||
) {
|
||||
if (value == null) {
|
||||
@@ -112,13 +110,13 @@
|
||||
const map = getChannelProviders(channelProviders)
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
const provider = getProvider(item, map, lastViews)
|
||||
const provider = getProvider(item, map, docUpdates)
|
||||
if (provider !== undefined) {
|
||||
result.push(provider)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const provider = getProvider(value, map, lastViews)
|
||||
const provider = getProvider(value, map, docUpdates)
|
||||
if (provider !== undefined) {
|
||||
result.push(provider)
|
||||
}
|
||||
@@ -127,7 +125,7 @@
|
||||
updateMenu(displayItems, channelProviders)
|
||||
}
|
||||
|
||||
$: if (value) update(value, $lastViews, $channelProviders)
|
||||
$: if (value) update(value, $docUpdates, $channelProviders)
|
||||
|
||||
let displayItems: Item[] = []
|
||||
let actions: Action[] = []
|
||||
@@ -148,7 +146,7 @@
|
||||
icon: pr.icon ?? contact.icon.SocialEdit,
|
||||
label: pr.label,
|
||||
action: async () => {
|
||||
const provider = getProvider({ provider: pr._id, value: '' }, getChannelProviders(providers), $lastViews)
|
||||
const provider = getProvider({ provider: pr._id, value: '' }, getChannelProviders(providers), $docUpdates)
|
||||
if (provider !== undefined) {
|
||||
if (_displayItems.filter((it) => it.provider === pr._id).length === 0) {
|
||||
displayItems = [..._displayItems, provider]
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
<script lang="ts">
|
||||
import type { Channel, ChannelProvider } from '@hcengineering/contact'
|
||||
import type { AttachedData, Doc, Ref } from '@hcengineering/core'
|
||||
import notification, { LastView } from '@hcengineering/notification'
|
||||
import { Asset, getResource, IntlString } from '@hcengineering/platform'
|
||||
import notification, { DocUpdates } from '@hcengineering/notification'
|
||||
import { Asset, IntlString, getResource } from '@hcengineering/platform'
|
||||
import type { AnyComponent } from '@hcengineering/ui'
|
||||
import { Button } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -31,8 +31,8 @@
|
||||
export let reverse: boolean = false
|
||||
export let integrations: Set<Ref<Doc>> = new Set<Ref<Doc>>()
|
||||
|
||||
let lastViews: Writable<LastView | undefined> = writable()
|
||||
getResource(notification.function.GetNotificationClient).then((res) => (lastViews = res().getLastViews()))
|
||||
let docUpdates: Writable<Map<Ref<Doc>, DocUpdates>> = writable(new Map())
|
||||
getResource(notification.function.GetNotificationClient).then((res) => (docUpdates = res().docUpdatesStore))
|
||||
|
||||
interface Item {
|
||||
label: IntlString
|
||||
@@ -48,11 +48,11 @@
|
||||
function getProvider (
|
||||
item: AttachedData<Channel>,
|
||||
map: Map<Ref<ChannelProvider>, ChannelProvider>,
|
||||
lastViews: LastView | undefined
|
||||
docUpdates: Map<Ref<Doc>, DocUpdates>
|
||||
): any | undefined {
|
||||
const provider = map.get(item.provider)
|
||||
if (provider) {
|
||||
const notification = (item as Channel)._id !== undefined ? isNew(item as Channel, lastViews) : false
|
||||
const notification = (item as Channel)._id !== undefined ? isNew(item as Channel, docUpdates) : false
|
||||
return {
|
||||
label: provider.label,
|
||||
icon: provider.icon as Asset,
|
||||
@@ -66,16 +66,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
function isNew (item: Channel, lastViews: LastView | undefined): boolean {
|
||||
if (item.lastMessage === undefined) return false
|
||||
const lastView =
|
||||
(item as Channel)._id !== undefined && lastViews !== undefined ? lastViews[(item as Channel)._id] : undefined
|
||||
return lastView ? lastView < item.lastMessage : (item.items ?? 0) > 0
|
||||
function isNew (item: Channel, docUpdates: Map<Ref<Doc>, DocUpdates>): boolean {
|
||||
const docUpdate = docUpdates.get(item._id)
|
||||
return docUpdate ? docUpdate.txes.length > 0 : (item.items ?? 0) > 0
|
||||
}
|
||||
|
||||
async function update (
|
||||
value: AttachedData<Channel>[] | Channel | null,
|
||||
lastViews: LastView | undefined,
|
||||
docUpdates: Map<Ref<Doc>, DocUpdates>,
|
||||
channels: ChannelProvider[]
|
||||
) {
|
||||
if (value === null) {
|
||||
@@ -86,13 +84,13 @@
|
||||
const map = getChannelProviders(channels)
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
const provider = getProvider(item, map, lastViews)
|
||||
const provider = getProvider(item, map, docUpdates)
|
||||
if (provider !== undefined) {
|
||||
result.push(provider)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const provider = getProvider(value, map, lastViews)
|
||||
const provider = getProvider(value, map, docUpdates)
|
||||
if (provider !== undefined) {
|
||||
result.push(provider)
|
||||
}
|
||||
@@ -100,7 +98,7 @@
|
||||
displayItems = result
|
||||
}
|
||||
|
||||
$: if (value) update(value, $lastViews, $channelProviders)
|
||||
$: if (value) update(value, $docUpdates, $channelProviders)
|
||||
|
||||
let displayItems: Item[] = []
|
||||
let divHTML: HTMLElement
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
export let embedded = false
|
||||
|
||||
let lastId: Ref<Doc> = _id
|
||||
let lastClass: Ref<Class<Doc>> = _class
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
@@ -79,17 +78,15 @@
|
||||
function read (_id: Ref<Doc>) {
|
||||
if (lastId !== _id) {
|
||||
const prev = lastId
|
||||
const prevClass = lastClass
|
||||
lastId = _id
|
||||
lastClass = _class
|
||||
notificationClient.then((client) => client.updateLastView(prev, prevClass))
|
||||
notificationClient.then((client) => client.read(prev))
|
||||
}
|
||||
}
|
||||
|
||||
const currentUser = getCurrentAccount() as EmployeeAccount
|
||||
|
||||
onDestroy(async () => {
|
||||
notificationClient.then((client) => client.updateLastView(_id, _class))
|
||||
notificationClient.then((client) => client.read(_id))
|
||||
})
|
||||
|
||||
let requests: DocumentRequest[] = []
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
{ attachedTo: channelId },
|
||||
(res) => {
|
||||
messages = res
|
||||
notificationClient.updateLastView(channelId, channel._class, undefined, true)
|
||||
notificationClient.read(channelId)
|
||||
},
|
||||
{ sort: { sendOn: SortingOrder.Descending } }
|
||||
)
|
||||
@@ -68,7 +68,7 @@
|
||||
messages: convertMessages(object, channel, selectedMessages, $employeeAccountByIdStore, $employeeByIdStore)
|
||||
}
|
||||
)
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
await notificationClient.read(channel._id)
|
||||
clear()
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
async function selectHandler (e: CustomEvent): Promise<void> {
|
||||
currentMessage = e.detail
|
||||
if (channel !== undefined) {
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
await notificationClient.read(channel._id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
},
|
||||
objectId
|
||||
)
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
await notificationClient.read(channel._id)
|
||||
objectId = generateId()
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
.map((m) => m.trim())
|
||||
.filter((m) => m.length)
|
||||
})
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
await notificationClient.read(channel._id)
|
||||
for (const attachment of attachments) {
|
||||
await client.addCollection(
|
||||
attachmentP.class.Attachment,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"string": {
|
||||
"LastView": "Last View",
|
||||
"Notification": "Notification",
|
||||
"Notifications": "Notifications",
|
||||
"NoNotifications": "No notifications",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"string": {
|
||||
"LastView": "Последний просмотр",
|
||||
"Notification": "Увдомление",
|
||||
"Notifications": "Уведомления",
|
||||
"NoNotifications": "Нет уведомлений",
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { Doc, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import {
|
||||
NotificationProvider,
|
||||
NotificationSetting,
|
||||
@@ -26,14 +26,10 @@
|
||||
import { getCurrentLocation, showPanel } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import notification from '../plugin'
|
||||
import { NotificationClientImpl } from '../utils'
|
||||
|
||||
const query = createQuery()
|
||||
const settingQuery = createQuery()
|
||||
const providersQuery = createQuery()
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const lastViewId: Ref<Doc> = ((getCurrentAccount() as EmployeeAccount).employee + 'notification') as Ref<Doc>
|
||||
|
||||
let settingsReceived = false
|
||||
let settings: Map<Ref<NotificationType>, NotificationSetting> = new Map<Ref<NotificationType>, NotificationSetting>()
|
||||
@@ -129,16 +125,6 @@
|
||||
alreadyShown.clear()
|
||||
}, 5000)
|
||||
|
||||
const lastView = ($lastViews as any)[lastViewId]
|
||||
if ((lastView ?? notifyInstance.modifiedOn) > 0) {
|
||||
await notificationClient.updateLastView(
|
||||
lastViewId,
|
||||
contact.class.Employee,
|
||||
notifyInstance.modifiedOn,
|
||||
lastView === undefined
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
const notification = new Notification(getCurrentLocation().path[1], {
|
||||
tag: notifyInstance._id,
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
const store = notificationClient.docUpdatesStore
|
||||
$: docUpdate = $store.get(value._id)
|
||||
|
||||
$: hasNotification = (docUpdate?.txes?.length ?? 0) > 0
|
||||
$: hasNotification = (docUpdate?.txes?.length ?? 0) > 0 && docUpdate?.hidden !== true
|
||||
</script>
|
||||
|
||||
{#if hasNotification}
|
||||
|
||||
@@ -14,44 +14,35 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import core, { Account, Class, Doc, getCurrentAccount, Ref, Timestamp } from '@hcengineering/core'
|
||||
import notification, { DocUpdates, LastView, NotificationClient } from '@hcengineering/notification'
|
||||
import { Account, Class, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import notification, { Collaborators, DocUpdates, NotificationClient } from '@hcengineering/notification'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { get, writable, Writable } from 'svelte/store'
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export class NotificationClientImpl implements NotificationClient {
|
||||
protected static _instance: NotificationClientImpl | undefined = undefined
|
||||
private readonly lastViewsStore = writable<LastView>()
|
||||
readonly docUpdatesStore = writable<Map<Ref<Doc>, DocUpdates>>(new Map())
|
||||
docUpdatesMap: Map<Ref<Doc>, DocUpdates> = new Map()
|
||||
readonly docUpdates = writable<DocUpdates[]>([])
|
||||
|
||||
private readonly docUpdatesQuery = createQuery(true)
|
||||
|
||||
private readonly lastViewQuery = createQuery()
|
||||
private readonly user: Ref<Account>
|
||||
|
||||
private constructor () {
|
||||
this.user = getCurrentAccount()._id
|
||||
this.lastViewQuery.query(notification.class.LastView, { user: this.user }, (result) => {
|
||||
this.lastViewsStore.set(result[0])
|
||||
if (result[0] === undefined) {
|
||||
const client = getClient()
|
||||
const u = client.txFactory.createTxCreateDoc(notification.class.LastView, notification.space.Notifications, {
|
||||
user: this.user
|
||||
})
|
||||
u.space = core.space.DerivedTx
|
||||
void client.tx(u)
|
||||
}
|
||||
})
|
||||
this.docUpdatesQuery.query(
|
||||
notification.class.DocUpdates,
|
||||
{
|
||||
user: this.user
|
||||
},
|
||||
(result) => {
|
||||
this.docUpdatesStore.set(new Map(result.map((p) => [p.attachedTo, p])))
|
||||
this.docUpdates.set(result)
|
||||
this.docUpdatesMap = new Map(result.map((p) => [p.attachedTo, p]))
|
||||
this.docUpdatesStore.set(this.docUpdatesMap)
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -67,46 +58,49 @@ export class NotificationClientImpl implements NotificationClient {
|
||||
return NotificationClientImpl._instance
|
||||
}
|
||||
|
||||
getLastViews (): Writable<LastView> {
|
||||
return this.lastViewsStore
|
||||
}
|
||||
|
||||
async updateLastView (
|
||||
_id: Ref<Doc>,
|
||||
_class: Ref<Class<Doc>>,
|
||||
time?: Timestamp,
|
||||
force: boolean = false
|
||||
): Promise<void> {
|
||||
async read (_id: Ref<Doc>): Promise<void> {
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const mixin = hierarchy.classHierarchyMixin(_class, notification.mixin.TrackedDoc)
|
||||
if (mixin === undefined) return
|
||||
const lastView = time ?? new Date().getTime()
|
||||
const obj = get(this.lastViewsStore)
|
||||
if (obj !== undefined) {
|
||||
const current = obj[_id] as Timestamp | undefined
|
||||
if (current !== undefined || force) {
|
||||
if (current === -1 && !force) return
|
||||
if (force || (current ?? 0) < lastView) {
|
||||
const u = client.txFactory.createTxUpdateDoc(obj._class, obj.space, obj._id, {
|
||||
[_id]: lastView
|
||||
})
|
||||
u.space = core.space.DerivedTx
|
||||
await client.tx(u)
|
||||
}
|
||||
}
|
||||
const docUpdate = this.docUpdatesMap.get(_id)
|
||||
if (docUpdate !== undefined) {
|
||||
await client.update(docUpdate, { txes: [] })
|
||||
}
|
||||
}
|
||||
|
||||
async unsubscribe (_id: Ref<Doc>): Promise<void> {
|
||||
async forceRead (_id: Ref<Doc>, _class: Ref<Class<Doc>>): Promise<void> {
|
||||
const client = getClient()
|
||||
const obj = get(this.lastViewsStore)
|
||||
if (obj !== undefined) {
|
||||
const u = client.txFactory.createTxUpdateDoc(obj._class, obj.space, obj._id, {
|
||||
[_id]: -1
|
||||
})
|
||||
u.space = core.space.DerivedTx
|
||||
await client.tx(u)
|
||||
const docUpdate = this.docUpdatesMap.get(_id)
|
||||
if (docUpdate !== undefined) {
|
||||
await client.update(docUpdate, { txes: [] })
|
||||
} else {
|
||||
const doc = await client.findOne(_class, { _id })
|
||||
if (doc !== undefined) {
|
||||
const hiearachy = client.getHierarchy()
|
||||
const collab = hiearachy.as<Doc, Collaborators>(doc, notification.mixin.Collaborators)
|
||||
if (collab.collaborators === undefined) {
|
||||
await client.createMixin<Doc, Collaborators>(
|
||||
collab._id,
|
||||
collab._class,
|
||||
collab.space,
|
||||
notification.mixin.Collaborators,
|
||||
{
|
||||
collaborators: [this.user]
|
||||
}
|
||||
)
|
||||
} else if (!collab.collaborators.includes(this.user)) {
|
||||
await client.updateMixin(collab._id, collab._class, collab.space, notification.mixin.Collaborators, {
|
||||
$push: {
|
||||
collaborators: this.user
|
||||
}
|
||||
})
|
||||
}
|
||||
await client.createDoc(notification.class.DocUpdates, doc.space, {
|
||||
attachedTo: _id,
|
||||
attachedToClass: _class,
|
||||
user: this.user,
|
||||
hidden: true,
|
||||
txes: []
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,14 +34,6 @@ import { Writable } from './types'
|
||||
import { Preference } from '@hcengineering/preference'
|
||||
export * from './types'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface LastView extends Doc {
|
||||
user: Ref<Account>
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@@ -135,20 +127,6 @@ export interface NotificationSetting extends Preference {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface SpaceLastEdit extends Class<Doc> {
|
||||
lastEditField: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface AnotherUserNotifications extends Class<Doc> {
|
||||
fields: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@@ -156,11 +134,6 @@ export interface ClassCollaborators extends Class<Doc> {
|
||||
fields: string[] // Ref<Account> | Ref<Employee> | Ref<Account>[] | Ref<Employee>[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface TrackedDoc extends Class<Doc> {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@@ -183,8 +156,8 @@ export interface DocUpdates extends Doc {
|
||||
attachedTo: Ref<Doc>
|
||||
attachedToClass: Ref<Class<Doc>>
|
||||
hidden: boolean
|
||||
lastTx: Ref<TxCUD<Doc>>
|
||||
lastTxTime: Timestamp
|
||||
lastTx?: Ref<TxCUD<Doc>>
|
||||
lastTxTime?: Timestamp
|
||||
txes: [Ref<TxCUD<Doc>>, Timestamp][]
|
||||
}
|
||||
|
||||
@@ -198,9 +171,9 @@ export const notificationId = 'notification' as Plugin
|
||||
*/
|
||||
export interface NotificationClient {
|
||||
docUpdatesStore: Writable<Map<Ref<Doc>, DocUpdates>>
|
||||
getLastViews: () => Writable<LastView>
|
||||
updateLastView: (_id: Ref<Doc>, _class: Ref<Class<Doc>>, time?: Timestamp, force?: boolean) => Promise<void>
|
||||
unsubscribe: (_id: Ref<Doc>) => Promise<void>
|
||||
docUpdates: Writable<DocUpdates[]>
|
||||
read: (_id: Ref<Doc>) => Promise<void>
|
||||
forceRead: (_id: Ref<Doc>, _class: Ref<Class<Doc>>, space: Ref<Space>) => Promise<void>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,15 +186,11 @@ export type NotificationClientFactoy = () => NotificationClient
|
||||
*/
|
||||
const notification = plugin(notificationId, {
|
||||
mixin: {
|
||||
SpaceLastEdit: '' as Ref<Mixin<SpaceLastEdit>>,
|
||||
AnotherUserNotifications: '' as Ref<Mixin<AnotherUserNotifications>>,
|
||||
ClassCollaborators: '' as Ref<Mixin<ClassCollaborators>>,
|
||||
Collaborators: '' as Ref<Mixin<Collaborators>>,
|
||||
TrackedDoc: '' as Ref<Mixin<TrackedDoc>>,
|
||||
NotificationObjectPresenter: '' as Ref<Mixin<NotificationObjectPresenter>>
|
||||
},
|
||||
class: {
|
||||
LastView: '' as Ref<Class<LastView>>,
|
||||
Notification: '' as Ref<Class<Notification>>,
|
||||
EmailNotification: '' as Ref<Class<EmailNotification>>,
|
||||
NotificationType: '' as Ref<Class<NotificationType>>,
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
const notificationClient = getResource(notification.function.GetNotificationClient).then((res) => res())
|
||||
|
||||
onDestroy(async () => {
|
||||
notificationClient.then((client) => client.updateLastView(_id, recruit.class.Vacancy))
|
||||
notificationClient.then((client) => client.read(_id))
|
||||
})
|
||||
|
||||
const client = getClient()
|
||||
@@ -57,7 +57,7 @@
|
||||
const prev = lastId
|
||||
lastId = _id
|
||||
if (prev) {
|
||||
notificationClient.then((client) => client.updateLastView(prev, recruit.class.Vacancy))
|
||||
notificationClient.then((client) => client.read(prev))
|
||||
}
|
||||
query.query(recruit.class.Vacancy, { _id }, (result) => {
|
||||
object = result[0] as Required<Vacancy>
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
(res) => {
|
||||
messages = res.reverse()
|
||||
if (channel !== undefined) {
|
||||
notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
notificationClient.read(channel._id)
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -171,7 +171,7 @@
|
||||
}
|
||||
)
|
||||
if (channel !== undefined) {
|
||||
await notificationClient.updateLastView(channel._id, channel._class, channel.modifiedOn, true)
|
||||
await notificationClient.read(channel._id)
|
||||
}
|
||||
clear()
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
export let embedded = false
|
||||
|
||||
let lastId: Ref<Doc> = _id
|
||||
let lastClass: Ref<Class<Doc>> = _class
|
||||
const queryClient = createQuery()
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
@@ -66,15 +65,13 @@
|
||||
function read (_id: Ref<Doc>) {
|
||||
if (lastId !== _id) {
|
||||
const prev = lastId
|
||||
const prevClass = lastClass
|
||||
lastId = _id
|
||||
lastClass = _class
|
||||
notificationClient.then((client) => client.updateLastView(prev, prevClass))
|
||||
notificationClient.then((client) => client.read(prev))
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(async () => {
|
||||
notificationClient.then((client) => client.updateLastView(_id, _class))
|
||||
notificationClient.then((client) => client.read(_id))
|
||||
})
|
||||
|
||||
$: _id &&
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
export let _class: Ref<Class<IssueTemplate>>
|
||||
|
||||
let lastId: Ref<Doc> = _id
|
||||
let lastClass: Ref<Class<Doc>> = _class
|
||||
const query = createQuery()
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
@@ -65,15 +64,13 @@
|
||||
function read (_id: Ref<Doc>) {
|
||||
if (lastId !== _id) {
|
||||
const prev = lastId
|
||||
const prevClass = lastClass
|
||||
lastId = _id
|
||||
lastClass = _class
|
||||
notificationClient.then((client) => client.updateLastView(prev, prevClass))
|
||||
notificationClient.then((client) => client.read(prev))
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(async () => {
|
||||
notificationClient.then((client) => client.updateLastView(_id, _class))
|
||||
notificationClient.then((client) => client.read(_id))
|
||||
})
|
||||
|
||||
$: _id &&
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
|
||||
let realObjectClass: Ref<Class<Doc>> = _class
|
||||
let lastId: Ref<Doc> = _id
|
||||
let lastClass: Ref<Class<Doc>> = _class
|
||||
let object: Doc
|
||||
|
||||
const client = getClient()
|
||||
@@ -55,15 +54,13 @@
|
||||
function read (_id: Ref<Doc>) {
|
||||
if (lastId !== _id) {
|
||||
const prev = lastId
|
||||
const prevClass = lastClass
|
||||
lastId = _id
|
||||
lastClass = _class
|
||||
notificationClient.then((client) => client.updateLastView(prev, prevClass))
|
||||
notificationClient.then((client) => client.read(prev))
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(async () => {
|
||||
notificationClient.then((client) => client.updateLastView(_id, _class))
|
||||
notificationClient.then((client) => client.read(_id))
|
||||
})
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import type { Class, Doc, Ref, Space } from '@hcengineering/core'
|
||||
import core from '@hcengineering/core'
|
||||
import notification, { LastView } from '@hcengineering/notification'
|
||||
import { DocUpdates } from '@hcengineering/notification'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import preference from '@hcengineering/preference'
|
||||
@@ -98,20 +98,13 @@
|
||||
}
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const docUpdates = notificationClient.docUpdatesStore
|
||||
const hierarchy = client.getHierarchy()
|
||||
$: clazz = hierarchy.getClass(model.spaceClass)
|
||||
$: lastEditMixin = hierarchy.as(clazz, notification.mixin.SpaceLastEdit)
|
||||
|
||||
function isChanged (space: Space, lastViews: LastView): boolean {
|
||||
const field = lastEditMixin?.lastEditField
|
||||
const lastView = lastViews[space._id]
|
||||
if (lastView === undefined || lastView === -1) return false
|
||||
if (field === undefined) return false
|
||||
const value = (space as any)[field]
|
||||
if (isNaN(value)) return false
|
||||
|
||||
return lastView < value
|
||||
function isChanged (space: Space, docUpdates: Map<Ref<Doc>, DocUpdates>): boolean {
|
||||
const update = docUpdates.get(space._id)
|
||||
if (update === undefined) return false
|
||||
return update.txes.length > 0 && update.hidden !== true
|
||||
}
|
||||
|
||||
function getParentActions (): Action[] {
|
||||
@@ -147,7 +140,7 @@
|
||||
icon={classIcon(client, space._class)}
|
||||
selected={deselect ? false : currentSpace === space._id}
|
||||
actions={() => getActions(space)}
|
||||
bold={isChanged(space, $lastViews)}
|
||||
bold={isChanged(space, $docUpdates)}
|
||||
/>
|
||||
{/await}
|
||||
</NavLink>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import type { Doc, Ref, Space } from '@hcengineering/core'
|
||||
import core from '@hcengineering/core'
|
||||
import notification, { LastView } from '@hcengineering/notification'
|
||||
import { DocUpdates } from '@hcengineering/notification'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { IntlString, getResource } from '@hcengineering/platform'
|
||||
import preference from '@hcengineering/preference'
|
||||
@@ -74,20 +74,12 @@
|
||||
}
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const docUpdates = notificationClient.docUpdatesStore
|
||||
|
||||
function isChanged (space: Space, lastViews: LastView): boolean {
|
||||
const clazz = hierarchy.getClass(space._class)
|
||||
const lastEditMixin = hierarchy.as(clazz, notification.mixin.SpaceLastEdit)
|
||||
const field = lastEditMixin?.lastEditField
|
||||
const lastView = lastViews[space._id]
|
||||
if (lastView === undefined || lastView === -1) return false
|
||||
if (field === undefined) return false
|
||||
const value = (space as any)[field]
|
||||
if (isNaN(value)) return false
|
||||
|
||||
return lastView < value
|
||||
function isChanged (space: Space, docUpdates: Map<Ref<Doc>, DocUpdates>): boolean {
|
||||
const update = docUpdates.get(space._id)
|
||||
if (update === undefined) return false
|
||||
return update.txes.length > 0 && update.hidden !== true
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -102,7 +94,7 @@
|
||||
icon={classIcon(client, space._class)}
|
||||
selected={currentSpace === space._id}
|
||||
actions={() => getActions(space)}
|
||||
bold={isChanged(space, $lastViews)}
|
||||
bold={isChanged(space, $docUpdates)}
|
||||
/>
|
||||
</NavLink>
|
||||
{/await}
|
||||
|
||||
Reference in New Issue
Block a user