Skip to content

fix(dom-element-manipulation): evict chat message elements the client drops - #303

Open
Arthurk12 wants to merge 3 commits into
bigbluebutton:v1.xfrom
Arthurk12:issue/293
Open

Arthurk12 wants to merge 3 commits into
bigbluebutton:v1.xfrom
Arthurk12:issue/293

Conversation

@Arthurk12

Copy link
Copy Markdown
Member

What does this PR do?

  • useChatMessageDomElements now applies removals. The hook only accepted a payload carrying an element it did not store yet; a removal adds nothing, so it was always discarded. The client payload is now treated as authoritative: it is filtered by the ids this plugin requested and replaces the page whenever the result differs, in either direction (compared by element identity, so a replaced node is picked up too). This also:
    • drops the empty-payload special case, which read messages: [] as "page unmounted" although the client also sends it for a mounted page rendering none of the requested ids — an empty payload now simply empties the page;
    • keeps elements requested only by other plugins out of this hook's store, and avoids updating it when only those change;
    • registers the event listener once and removes it on unmount, reading the requested ids from a ref, instead of re-adding it on every state change to refresh a stale closure.
  • LoadedChatMessage type: message is now string | null (the soft delete nulls it), and senderRole, deletedAt and deletedBy are declared, matching what the client delivers.
  • Samples: sample-server-commands-plugin and sample-dom-element-manipulation skip deleted messages. The latter threw on deleted messages (message.search on null).

Closes Issue(s)

Closes #293

Related: bigbluebutton/bigbluebutton#25812 (client side, exposes deletedAt / deletedBy) — bigbluebutton/bigbluebutton#

Motivation

After a message was deleted, useChatMessageDomElements kept a strong reference to its detached element until the chat page unmounted, and kept returning it to a plugin that still requested the id — sample-server-commands-plugin then threw a TypeError on parentElement. A plugin also had no typed way to know a message was deleted, so it could not stop requesting it. With this PR the hook only answers "which requested elements are on screen now", and deletedAt from useLoadedChatMessages answers whether a message was deleted:

useChatMessageDomElements useLoadedChatMessages
Message deleted element absent message present, deletedAt set
Page unmounted element absent message absent

More

  • Tests: new unit tests in tests/unit/dom-element-manipulation/chat/message/hooks.test.ts. "drops an element the client stops sending while the id is still requested" and "stores only the elements this plugin requested" fail on the previous implementation and pass now. Both samples pass tsc and lint against the local build (scripts/publish-to-project-folder.sh).
  • Compatibility: the hook ↔ client protocol (events and payload shape) is unchanged, so this SDK works with clients without Plugins cannot tell that a chat message was deleted bigbluebutton#25812 and older SDKs keep working with clients that have it. On a client without that fix, deletedAt / deletedBy arrive as undefined; the samples test deletedAt for truthiness for that reason.
  • Type change: message: string | null may break the compilation of plugins with strict null checks that use it as a plain string. At runtime nothing changes — those plugins already receive null for deleted messages.

… drops

useChatMessageDomElements only applies a payload when it carries an
element the hook does not store yet. A removal adds nothing, so its
payload is always discarded: the hook keeps a strong reference to the
detached element until the page unmounts and keeps returning it while
the plugin still requests its id, which then throws on parentElement.
The hook also reads an empty payload as the page unmounting, although
the client sends one whenever none of the requested ids is rendered on
a mounted page.

Treat the client payload as authoritative: filter it by the ids this
plugin requested and replace the page whenever the result differs, in
either direction. This drops the empty-payload inference, and filtering
keeps elements requested only by other plugins out of the store. The
handler now reads the requested ids from a ref, so the listener is
registered once and removed on unmount instead of being re-added on
every state change.
The client soft-deletes chat messages by nulling message and recording
deletedAt and deletedBy, but LoadedChatMessage declares message as
string and has no deletion fields. A plugin cannot read deletedAt
without a cast, and one trusting the message declaration crashes on
the first deleted message. senderRole is also sent by the client but
missing from the type.

Declare message as string | null and add senderRole, deletedAt and
deletedBy, matching what the client delivers, so plugins can tell a
deleted message from a live one instead of inferring it from a null
message.
The server commands sample selects its custom messages by metadata,
which a soft delete leaves intact, so it keeps requesting the DOM
element of a deleted message. The DOM element manipulation sample
calls search on message, which is null for a deleted message, and
throws.

Skip messages with deletedAt set in both filters. Test it for
truthiness rather than against null, so the samples keep working with
clients that do not send the field yet.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant