import type { IShare, ISubscription } from '@wovin/core' import type { AgentStateClass } from './AgentState' import { comparer, flow, observable, runInAction } from '@wovin/core/mobx' import { Logger } from 'besonders-logger' import { notifyToast } from '../../ui/utils-ui' import { stateDB } from '../local-state' import { useAgent } from './AgentState' const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line unused-imports/no-unused-vars export class AgentPubSub { mixed = this as unknown as AgentStateClass readonly shares = observable.array([] as IShare[], { deep: true, equals: comparer.structural }) readonly subscriptions = observable.array([] as ISubscription[], { deep: true, equals: comparer.structural }) get sharesMap() { return new Map(this.shares.map(share => [share.id, share])) } get subscriptionsMap() { return new Map(this.subscriptions.map(sub => [sub.id, sub])) } get mostRecentShare() { if (!this.shares.length) return null return this.shares[this.shares.length - 1] } getDirectoryHandle = flow(function* (this: AgentStateClass) { return (yield stateDB.directoryHandles.get(this.ag))?.directoryHandle }) setDirectoryHandle = flow(function* (this: AgentStateClass, directoryHandle: FileSystemDirectoryHandle) { LOG(`Set Handle:`, directoryHandle) // (i) dexie does some type checking, so we do it first & await yield stateDB.directoryHandles.put({ ag: this.ag, directoryHandle }) }) addShare = flow(function* (this: AgentStateClass, share: IShare) { LOG(`Adding share:`, share) // (i) dexie does some type checking, so we do it first & await yield stateDB.shares.add(share) this.reloadPubSub() }) addSub = flow(function* (this: AgentStateClass, sub: ISubscription) { LOG(`Adding subscription:`, sub) yield stateDB.subscriptions.add(sub) this.reloadPubSub() }) updateSub = flow(function* (this: AgentStateClass, id: string, changes: Partial>) { const sub = this.subscriptions.find(p => p.id === id) if (!sub) throw ERROR(`[updateSub] id not found:`, id, changes) LOG(`Updating subscription:`, id, changes, sub) yield stateDB.subscriptions.update(id, changes) Object.assign(sub, changes) // this.reloadPubSub() }) updateShare = flow(function* (this: AgentStateClass, id: string, changes: Partial>) { const share = this.shares.find(p => p.id === id) if (!share) throw ERROR(`[updateShare] id not found:`, id, changes) const { publishedBy } = share const { ag } = useAgent() if (ag !== publishedBy) { WARN(`[updateShare] last push was a different agent`, { publishedBy, ag }) notifyToast( `This share was last pushed by a different agent (${publishedBy} --> ${ag}) if you did not change your agent string, something may be up with your DID situation`, 'warning', ) changes.publishedBy = ag } LOG(`Updating share:`, id, changes, share) void stateDB.shares.update(id, changes) Object.assign(share, changes) // this.reloadPubSub() }) deleteShare = flow(function* (this: AgentStateClass, shareID: string) { const share = this.shares.find(p => p.id === shareID) LOG(`Deleting share:`, shareID, share) if (!share) throw ERROR(`Trying to delete non-existing share:`, shareID) // yield stateDB.shares.update(shareID, { isDeleted: true }) yield stateDB.shares.delete(shareID) this.reloadPubSub() }) deleteSub = flow(function* (this: AgentStateClass, subID: string) { const sub = this.subscriptions.find(p => p.id === subID) LOG(`Deleting subscription:`, subID, sub) if (!sub) throw ERROR(`Trying to delete non-existing subscription:`, subID) // yield stateDB.subscriptions.update(subID, { isDeleted: true }) yield stateDB.subscriptions.delete(subID) this.reloadPubSub() }) reloadPubSub = async () => { const shares = (await stateDB.shares.orderBy(/* 'lastPush', */ 'createdAt').toArray()).filter(p => !p.isDeleted) const subscriptions = (await stateDB.subscriptions.orderBy(/* 'lastPull', */ 'createdAt').toArray()).filter(p => !p.isDeleted) runInAction(() => { this.shares.replace(shares) this.subscriptions.replace(subscriptions) }) DEBUG(`Loaded from IDB:`, { shares: this.shares, subs: this.subscriptions, stateDB }) } }