Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor message docs for deletions. #402

Merged
merged 1 commit into from
Dec 9, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 50 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,22 +346,67 @@ These additional parameters are:
* `metadata`: a map of extra information that can be attached to the deletion message.

The return of this call will be the deleted message, as it would appear to other subscribers of the room.
This is a _soft delete_ and the message will still be available in the history, but with the `deletedAt` property set.
This is a _soft delete_ and the message will still be available in the history.

Example
```ts
const deletedMessage = await room.messages.delete(message, { description: 'This message was deleted for ...' });
const deletedMessage = await room.messages.delete(message,
{
description: 'This message was deleted for ...'
});
```

Note that you can update deleted messages, which will effectively undo the delete. Only the last operation on a message takes effect.
`deletedMessage` is a Message object with the deletion applied. As with sending, the promise may resolve after the deletion message is received via the messages subscription.

### Subscribing to incoming messages
A `Message` that was deleted will have `deletedAt` and `deletedBy` fields set, and `isDeleted()` will return `true`.

To subscribe to incoming messages, call `subscribe` with your listener.
Note that you can update deleted messages, which will effectively undo the deletion. Only the last operation on a message takes effect.

```ts
const { unsubscribe } = room.messages.subscribe((msg) => console.log(msg));
```

#### Handling deletes in realtime

Deletion messages received from realtime have the `action` parameter set to `ChatMessageActions.MessageDelete`, and the event received has the `type` set to `MessageEvents.Deleted`.
Just like `updates`, `deletion` messages are also full copies of the message, meaning that all that is needed to keep a state or UI up to date is to replace the old message with the received one.

On rare occasions, deletes and updates might arrive over realtime out of order.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we provide order guarantees, I think its important to clarify here what "out of order" means. What you mean to say, I think, is that the order messages are received in realtime does not necessarily reflect the global ordering of events (e.g. due to concurrent operations in multiple regions)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the comment here to detail this and also recommend that users should always use the actionBefore etc.. for ordering actions.

That is to say, should two concurrent actions happen in disparate regions, you will likely receive the action processed in the region closest to you first.
When the second action arrives, you will need to determine the order of these actions;
this is done by comparing their respective global orders, determined by the `version` field of the message.

To keep a correct state, the `Message` interface provides methods to compare two instances of the same base message to determine which action is newer:`actionBefore()`, `actionAfter()`, and `actionEqual()`.

The same out-of-order situation can happen between deletions received over realtime and HTTP responses.

In the situation where two concurrent deletes happen, both might be received via realtime before the HTTP response of the first one arrives.

In short, always use `actionAfter()`,
`actionBefore()`, or `actionEqual()` to determine the global ordering of two `Message` actions.

Example for handling deletes:
```typescript
const messages : Message[] = []; // assuming this is where state is kept

room.messages.subscribe(event => {
switch (event.type) {
case MessageEvents.Deleted: {
const serial = event.message.serial;
const index = messages.findIndex((m) => m.serial === serial);
if (index !== -1 && messages[index].actionBefore(event.message)) {
messages[index] = event.message;
}
break;
}
// other event types (ie. created and updated) omitted
}
})
```
### Subscribing to incoming messages

To subscribe to incoming messages, call `subscribe` with your listener.

### Unsubscribing from incoming messages

When you're done with the listener, call `unsubscribe` to remove that listeners subscription and prevent it from receiving
Expand Down
Loading