Files
biørn bd846c6e43 broadcast channel reactions (#8450)
this PR adds support for reactions in broadcast channels.

> the idea of broadcast reactions is that they are sent as usual from
subscribers to owner. after some time, the owner broadcasts them to all
subscribers, who only get to see reaction+count, not who-reacted-what

the PR is quite large, but a good share are tests and otherwise many
things are straight forward.

review should be done by-file, not by-commit. to make review easier,
here is a high-level overview:

first a change in the existing internal `Reactions` object was required.
before this PR, `Reactions` had a "contact to reaction map" only, and
the "frequencies map", that are actually mainly needed for UI, were
calculated as needed. with this PR, the "frequencies map" is the field
that always exist, the "contact map" is only available on top of that.
moreover, this PR shifts that part to the core, it was unfortunately in
the bindings before.

with that preparation things done as follows:

1. reactions from broadcast channel subscriber (`Chattype::InBroadcast`)
to broadcast channel owner (`Chattype::OutBroadcast`) are sent as usual,
only change for that step was to allow sending them at all

2. the owner receives reactions and saves them to the existing
`reactions` table as usual. additionally, the changed message is
remembered in `reactions_need_broadcast` table

3. in the IMAP loop, when ~10 minutes have passed, and
`reactions_need_broadcast` contains entries, a single, hidden message
with accumulated reactions is sent. this message may contain reactions
to different messages. for each message, all known reactions are sent as
reaction+count.

4. subscriber receive that message and save the accumulated reactions in
`reactions_broadcasted`

6. `get_message_reactions` is adapted so that `frequencies` are set
independently of who-reacted-what (the old and only field).
who-reacted-what is called `by_contact` now. it is always set for
compatibility reasons, however, it is not exhaustive for subscribers.
in general, UI should work with frequencies, the API itself, however,
has not changed.

other tweaks:

- outgoing channels are muted on creation, and UI shall allow to
unmute/mute them as all other chats. reason is that reactions are
notified, but in many cases not of large interest. this is also what
telegram is doing

- to have an intermediate feedback when reacting, the local state should
include ones own reaction, even if it is not yet broadcasted. for that,
we modify `reactions_broadcasted` using `modify_frequencies()` as needed
when sending an reaction. there are still some situations where the
update may not include ones own reaction, in this case it is added
lately by `refine_frequencies()`, so that `get_message_reactions()`
always contain SELF.
(in a first implementation, we always increased SELF reaction in
refine_frequencies(), however, that was worse and led to SELF counted
twice once the owner sent broadcast)

<details>
<summary>wire format</summary>

wire format is a JSON in the `Broadcast-Reactions:` header.
additionally, `Content-Disposition: reaction` is set to not show the
hidden message on existing devices.

using a header also allows us to broadcast reactions with resent channel
messages (on joining) later.

```
{
  "messages": [
    {
      "id": "12345678",
      "reactions": [
        { "emoji": "👍", "count": 4 },
        { "emoji": "🎉", "count": 2 }
      ]
    },
    {
      "id": "23456789",
      "reactions": []
    }
  ]
}
```

for `id`, the wire format needs to use `rfc724_mid` as `msg_id` are
local only.

</details>

### known issues

- if the channel owner uses multiple devices, broadcasted reaction
updates are sent from each device. the updates are not that big, so that
is probably not a big deal. if it turns out that this is an issue, we
can think about fixes in another PR. might be done by restarting our
10-minute-wait once we see an update from another device

- we cannot set contact_id for DC_EVENT_REACTIONS_CHANGED - but i doubt
it was ever used

### for another pr

- ~~add `Broadcast-Reactions:` header also for resent channel messages,
so that new subscriber do not only get the latest messages, but also
their reactions. for that, the `Broadcast-Reactions:` header can go to
the corresponding message, no need to send extra messages. we would need
to change the sending part to send all reactions for a given message. on
receiving part, we need to make sure, `receive_broadcast_reactions()` is
called when the message actually exist.~~
EDIT: subsequent PR for resending broadcast reactions at
https://github.com/chatmail/core/pull/8496

- add api to allow only a subset of reactions, fiter incoming reactions
before broadcasting

### misc.

ui pr: https://github.com/deltachat/deltachat-ios/pull/3225 and
https://github.com/deltachat/deltachat-android/pull/4560 , which both
were tested successfully with this core PR already. desktop is meant to
be done once this is merged

---------

Co-authored-by: l <link2xt@testrun.org>
2026-08-04 18:54:17 +02:00
..
2025-06-26 14:07:39 +00:00
2025-06-26 14:07:39 +00:00
2025-06-26 14:07:39 +00:00