add activityCenter.ts
This commit is contained in:
parent
81f2950c19
commit
45cd81eaab
|
@ -0,0 +1,125 @@
|
|||
// todo: rename to notifications (center?), inbox, or keep same as other platforms
|
||||
import type { ChatMessage } from './chat'
|
||||
// import type { Client } from './client'
|
||||
|
||||
// todo?: union
|
||||
// todo?: rename to Activity
|
||||
type Notification = {
|
||||
// fixme?: specify message type (message_reply)
|
||||
type: 'message'
|
||||
value: ChatMessage
|
||||
}
|
||||
|
||||
type ActivityCenterLatest = {
|
||||
notifications: Notification[]
|
||||
// todo?: rename count to mentionsAndRepliesCount
|
||||
unreadChats: Map<string, { count: number }> // id, count (mentions, replies)
|
||||
}
|
||||
|
||||
// todo?: rename to NotificationCenter
|
||||
export class ActivityCenter {
|
||||
// todo?: use client.account for mentions and replies, or in chat.ts
|
||||
// #client: Client
|
||||
|
||||
#notifications: Set<Notification>
|
||||
#callbacks: Set<(latest: ActivityCenterLatest) => void>
|
||||
|
||||
constructor(/* client: Client */) {
|
||||
// this.#client = client
|
||||
|
||||
this.#notifications = new Set()
|
||||
this.#callbacks = new Set()
|
||||
}
|
||||
|
||||
// todo?: rename to latest, change
|
||||
public getLatest = (): ActivityCenterLatest => {
|
||||
const notifications: Notification[] = []
|
||||
const unreadChats: Map<string, { count: number }> = new Map()
|
||||
|
||||
for (const notification of this.#notifications.values()) {
|
||||
// todo?: switch
|
||||
if (notification.type === 'message') {
|
||||
const chatUuid = notification.value.chatUuid
|
||||
|
||||
const chat = unreadChats.get(chatUuid)
|
||||
if (chat) {
|
||||
// fixme!: isReply || isMention
|
||||
const shouldIncrement = false
|
||||
if (shouldIncrement) {
|
||||
chat.count++
|
||||
}
|
||||
} else {
|
||||
unreadChats.set(chatUuid, { count: 0 })
|
||||
}
|
||||
}
|
||||
|
||||
notifications.push(notification)
|
||||
}
|
||||
|
||||
// todo?: reverse order
|
||||
notifications.sort((a, b) => {
|
||||
if (a.value.clock < b.value.clock) {
|
||||
return -1
|
||||
}
|
||||
|
||||
if (a.value.clock > b.value.clock) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
})
|
||||
|
||||
// fixme!?: do not display regular messages, only mentions and replies
|
||||
// todo?: group notifications (all, unreads, mentions, replies, _chats.{id,count})
|
||||
return { notifications, unreadChats }
|
||||
}
|
||||
|
||||
public addMessageNotification = (value: ChatMessage) => {
|
||||
this.#notifications.add({ type: 'message', value })
|
||||
|
||||
this.emitLatest()
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all notifications.
|
||||
*/
|
||||
removeNotifications = () => {
|
||||
this.#notifications.clear()
|
||||
|
||||
this.emitLatest()
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes chat message notifications from the Activity Center. For example,
|
||||
* on only opening or after scrolling to the end.
|
||||
*/
|
||||
public removeChatNotifications = (chatUuid: string) => {
|
||||
// todo?: add chatUuid to "readChats" Set instead and resolve in getNotifications
|
||||
// triggered by following emit, and clear the set afterwards
|
||||
for (const notification of this.#notifications) {
|
||||
if (notification.type !== 'message') {
|
||||
continue
|
||||
}
|
||||
|
||||
if (notification.value.chatUuid === chatUuid) {
|
||||
this.#notifications.delete(notification)
|
||||
}
|
||||
}
|
||||
|
||||
this.emitLatest()
|
||||
}
|
||||
|
||||
private emitLatest = () => {
|
||||
const latest = this.getLatest()
|
||||
|
||||
this.#callbacks.forEach(callback => callback(latest))
|
||||
}
|
||||
|
||||
public onChange = (callback: (latest: ActivityCenterLatest) => void) => {
|
||||
this.#callbacks.add(callback)
|
||||
|
||||
return () => {
|
||||
this.#callbacks.delete(callback)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,7 +35,9 @@ export type ChatMessage = ChatMessageProto & {
|
|||
|
||||
type FetchedMessage = { messageId: string; timestamp?: Date }
|
||||
|
||||
// todo?: add isMuted prop, use as condition to add a message/notification to activity center or not
|
||||
export class Chat {
|
||||
// todo: use #
|
||||
private readonly client: Client
|
||||
#clock: bigint
|
||||
|
||||
|
@ -51,6 +53,7 @@ export class Chat {
|
|||
#pinEvents: Map<string, Pick<ChatMessage, 'clock' | 'pinned'>>
|
||||
#reactEvents: Map<string, Pick<ChatMessage, 'clock' | 'reactions'>>
|
||||
#deleteEvents: Map<string, Pick<ChatMessage, 'clock' | 'signer'>>
|
||||
#isActive: boolean
|
||||
#fetchingMessages?: boolean
|
||||
#previousFetchedStartTime?: Date
|
||||
#oldestFetchedMessage?: FetchedMessage
|
||||
|
@ -81,6 +84,7 @@ export class Chat {
|
|||
this.#pinEvents = new Map()
|
||||
this.#reactEvents = new Map()
|
||||
this.#deleteEvents = new Map()
|
||||
this.#isActive = false
|
||||
this.messageCallbacks = new Set()
|
||||
}
|
||||
|
||||
|
@ -142,6 +146,7 @@ export class Chat {
|
|||
return this.#messages.get(id)
|
||||
}
|
||||
|
||||
// todo?: delete
|
||||
public onChange = (callback: (description: CommunityChat) => void) => {
|
||||
this.chatCallbacks.add(callback)
|
||||
|
||||
|
@ -158,9 +163,16 @@ export class Chat {
|
|||
callback: (messages: ChatMessage[]) => void
|
||||
): (() => void) => {
|
||||
this.messageCallbacks.add(callback)
|
||||
// todo?: set from ui, think use case without an ui
|
||||
this.#isActive = true
|
||||
// todo?!: only if in `unreadChats`, keep "unreads" separate from `notifications`
|
||||
// todo?: only if at the bottom and all unread messages are in view
|
||||
// todo?: call from ui
|
||||
this.client.activityCenter.removeChatNotifications(this.uuid)
|
||||
|
||||
return () => {
|
||||
this.messageCallbacks.delete(callback)
|
||||
this.#isActive = false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -305,6 +317,11 @@ export class Chat {
|
|||
|
||||
// callback
|
||||
this.emitMessages()
|
||||
|
||||
// todo?: if not muted
|
||||
if (!this.#isActive) {
|
||||
this.client.activityCenter.addMessageNotifications(newMessage)
|
||||
}
|
||||
}
|
||||
|
||||
public handleEditedMessage = (
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
|
||||
import { ApplicationMetadataMessage } from '../protos/application-metadata-message'
|
||||
import { Account } from './account'
|
||||
import { ActivityCenter } from './activityCenter'
|
||||
import { Community } from './community/community'
|
||||
import { handleWakuMessage } from './community/handle-waku-message'
|
||||
|
||||
|
@ -37,6 +38,7 @@ class Client {
|
|||
*/
|
||||
#wakuDisconnectionTimer: ReturnType<typeof setInterval>
|
||||
|
||||
public activityCenter: ActivityCenter
|
||||
public account?: Account
|
||||
public community: Community
|
||||
|
||||
|
@ -50,6 +52,9 @@ class Client {
|
|||
this.wakuMessages = new Set()
|
||||
this.#wakuDisconnectionTimer = wakuDisconnectionTimer
|
||||
|
||||
// Activity Center
|
||||
this.activityCenter = new ActivityCenter(/* this */)
|
||||
|
||||
// Community
|
||||
this.community = new Community(this, options.publicKey)
|
||||
}
|
||||
|
|
|
@ -267,6 +267,9 @@ export class Community {
|
|||
this.contentTopic,
|
||||
this.symmetricKey
|
||||
)
|
||||
|
||||
// todo?:
|
||||
// this.client.activityCenter.addJoiningRequestNotification(...)
|
||||
}
|
||||
|
||||
public isOwner = (
|
||||
|
|
Loading…
Reference in New Issue