fix(ws): broadcast realtime page rename/icon change (#72)
handleMessage became a no-op and PageWsListener intentionally ignored PAGE_UPDATED, so a rename/icon change (client operation:updateOne) was no longer rebroadcast -> other clients saw stale title/icon in the sidebar+breadcrumbs until a reload (create/duplicate/restore were covered; updateOne regressed). Add a server-authoritative onPageUpdated handler: PageService.update detects a real title/icon change (DTO carries the field AND value differs; no-op/content- only saves excluded) and attaches a treeUpdate snapshot to PAGE_UPDATED; the listener broadcasts a tree updateOne via the restriction-aware emitTreeEvent (so a restricted page's title never leaks). Content-only saves attach nothing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,16 @@ import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||
import { SpaceMemberRepo } from '@docmost/db/repos/space/space-member.repo';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { EventName } from '../../../common/events/event.contants';
|
||||
import { TreeUpdateSnapshot } from '../../listeners/page.listener';
|
||||
|
||||
/**
|
||||
* Optional extras for the PAGE_UPDATED event emitted by updatePage(s). Lets the
|
||||
* caller attach a tree snapshot for a title/icon change so the WS listener can
|
||||
* broadcast an `updateOne` without re-reading the DB.
|
||||
*/
|
||||
export interface UpdatePageEventOpts {
|
||||
treeUpdate?: TreeUpdateSnapshot;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class PageRepo {
|
||||
@@ -138,14 +148,16 @@ export class PageRepo {
|
||||
updatablePage: UpdatablePage,
|
||||
pageId: string,
|
||||
trx?: KyselyTransaction,
|
||||
opts?: UpdatePageEventOpts,
|
||||
) {
|
||||
return this.updatePages(updatablePage, [pageId], trx);
|
||||
return this.updatePages(updatablePage, [pageId], trx, opts);
|
||||
}
|
||||
|
||||
async updatePages(
|
||||
updatePageData: UpdatablePage,
|
||||
pageIds: string[],
|
||||
trx?: KyselyTransaction,
|
||||
opts?: UpdatePageEventOpts,
|
||||
) {
|
||||
const result = await dbOrTx(this.db, trx)
|
||||
.updateTable('pages')
|
||||
@@ -160,6 +172,11 @@ export class PageRepo {
|
||||
this.eventEmitter.emit(EventName.PAGE_UPDATED, {
|
||||
pageIds: pageIds,
|
||||
workspaceId: updatePageData.workspaceId,
|
||||
// Optional tree snapshot for the WS listener (variant A). The caller sets
|
||||
// it ONLY for a title/icon change so the listener can broadcast an
|
||||
// `updateOne` without a DB read; content-only saves omit it and the
|
||||
// listener skips them. Built from server-side data, never client-relayed.
|
||||
...(opts?.treeUpdate ? { treeUpdate: opts.treeUpdate } : {}),
|
||||
});
|
||||
|
||||
return result;
|
||||
|
||||
Reference in New Issue
Block a user