import type { Component } from 'vue' import { createContext } from '.' export interface PopupNotificationButton { label: string action: () => void | Promise icon?: Component color?: 'brand' | 'red' | 'orange' | 'green' | 'blue' | 'standard' keepOpen?: boolean } export type PopupNotificationProgressType = 'percentage' | 'bytes' | 'count' export interface PopupNotificationProgressItem { id: string title: string text?: string iconUrl?: string | null progress: number waiting: boolean showProgress?: boolean wrapText?: boolean progressType?: PopupNotificationProgressType progressCurrent?: number progressTotal?: number onDismiss?: () => void | Promise buttons?: PopupNotificationButton[] } export type PopupNotificationToastType = | 'friend-request' | 'server-invite' | 'instance-invite' | 'instance-download' | 'instance-ready' export interface PopupNotificationToast { type: PopupNotificationToastType actorName?: string | null actorAvatarUrl?: string | null entityName?: string entityIconUrl?: string | null statusText?: string progress?: number waiting?: boolean showProgress?: boolean progressType?: PopupNotificationProgressType progressCurrent?: number progressTotal?: number onAccept?: () => void | Promise onDecline?: () => void | Promise onDismiss?: () => void | Promise onLaunch?: () => void | Promise onOpenActor?: () => void | Promise onOpenInstance?: () => void | Promise } export interface PopupNotification { id: string | number createdAt?: number title: string titleLogo?: Component bodyComponent?: Component bodyProps?: Record text?: string iconUrl?: string | null type?: 'error' | 'warning' | 'success' | 'info' | 'download' progress?: number waiting?: boolean progressItems?: PopupNotificationProgressItem[] buttons?: PopupNotificationButton[] toast?: PopupNotificationToast onClick?: () => void | Promise autoCloseMs?: number | null timer?: NodeJS.Timeout /** Hidden from the toast stack but retained in notification history. */ collapsed?: boolean } export abstract class AbstractPopupNotificationManager { protected readonly DEFAULT_AUTO_CLOSE_MS = 30 * 1000 abstract getNotifications(): PopupNotification[] protected abstract addNotificationToStorage(notification: PopupNotification): void protected abstract removeNotificationFromStorage(id: string | number): void protected abstract clearAllNotificationsFromStorage(): void addPopupNotification = ( notification: Omit, ): PopupNotification => { const newNotification: PopupNotification = { ...notification, id: Date.now() + Math.random(), createdAt: Date.now(), } this.setNotificationTimer(newNotification) this.addNotificationToStorage(newNotification) return newNotification } removeNotification = (id: string | number): void => { const notifications = this.getNotifications() const notification = notifications.find((n) => n.id === id) if (notification) { this.clearNotificationTimer(notification) this.removeNotificationFromStorage(id) } } clearAllNotifications = (): void => { this.getNotifications().forEach((n) => this.clearNotificationTimer(n)) this.clearAllNotificationsFromStorage() } setNotificationTimer = (notification: PopupNotification): void => { if (!notification) return this.clearNotificationTimer(notification) if (notification.autoCloseMs === null) return const delay = notification.autoCloseMs ?? this.DEFAULT_AUTO_CLOSE_MS notification.timer = setTimeout(() => { this.collapseNotification(notification.id) }, delay) } collapseNotification = (id: string | number): void => { const notification = this.getNotifications().find((n) => n.id === id) if (notification) { this.clearNotificationTimer(notification) notification.collapsed = true } } expandNotification = (id: string | number): void => { const notification = this.getNotifications().find((n) => n.id === id) if (notification) { notification.collapsed = false this.setNotificationTimer(notification) } } stopNotificationTimer = (notification: PopupNotification): void => { this.clearNotificationTimer(notification) } private clearNotificationTimer(notification: PopupNotification): void { if (notification.timer) { clearTimeout(notification.timer) notification.timer = undefined } } } export const [injectPopupNotificationManager, providePopupNotificationManager] = createContext('root', 'popupNotificationManager')