Skip to content

Variant-sibling messages: switch between regenerated bot replies in place - #1997

Open
AlexGogin wants to merge 3 commits into
DrKLO:masterfrom
AlexGogin:variant-sibling-messages
Open

Variant-sibling messages: switch between regenerated bot replies in place#1997
AlexGogin wants to merge 3 commits into
DrKLO:masterfrom
AlexGogin:variant-sibling-messages

Conversation

@AlexGogin

Copy link
Copy Markdown

Conversational bots have changed what people expect from a chat. A bot that generates its
answers is not a menu tree — the same question can be answered several ways, and users now
routinely ask for another take. Every major assistant interface treats that as a basic
primitive: ChatGPT, DeepSeek and Qwen all let you regenerate an answer and page between the
variants in place, without the conversation turning into a wall of near-duplicates.

Telegram has no way to express this. A bot that regenerates has to send a new message, so the
chat fills with slight variations of the same reply and the thread of the conversation is lost.
The gap is not cosmetic: it is the difference between a bot that feels like a conversation and
one that feels like a log. This PR closes that gap natively, for every bot on the platform.

The mechanism is generic. Messages known to be variants of one answer are correlated in a
per-account store; one of them is visible, the siblings are kept out of the render list. The
bubble shows a small i/N counter in its footer, arrow tap-zones flank the counter, a swipe
pages forward, and a reload side-button asks the bot for another variant. Nothing changes for a
message that was never regenerated — the counter and the outline are gated on group size
greater than one, so there is no extra draw cost.

Everything reuses what is already in the codebase rather than introducing parallel machinery:
the side-button geometry is the existing Forward/Share slot, the arrow hit-test mirrors
checkDateMotionEvent, the drawable registers through the same path as every other side-button
icon, and eviction of superseded siblings goes through the existing processDeletedMessages
primitive, touching only the render list and never MessagesStorage.

grouped_id/GroupedMessages were deliberately not reused: those positional flags drive the
album/collage layout, which shows all members at once — the opposite of a sequential pager.
That was verified before writing the code, not assumed.

The feature has been tested on developer devices and found working: regeneration, paging in
both directions, the i/N counter, and the eviction of superseded siblings all behave as
described above on real hardware, not only in review.

What this PR deliberately leaves to you. A variant group needs a signal for "this bot
produces alternatives". Any client-side choice of a TL flag bit is wrong by construction — the
schema is yours, and the bit I originally used locally (flags2 bit 21) is the one 12.9.0 gave
to linked_community_id. So the mechanism here is driven through a small seam instead, and
wiring it to a real server-side signal is a decision only you can make. The client side is
complete and self-contained either way.

On the interaction design. The counter, the arrows and the reload button are placed to fit
the existing bubble geometry, but they are one reading of how this should look and feel. If
variant paging belongs somewhere else in Telegram's interaction language — a different affordance,
a different gesture, a different place in the bubble — I would rather it be shaped your way than
mine. The underlying mechanism does not depend on the presentation: the store, the sibling
filtering and the eviction path stay the same whatever the surface ends up being.

Correlation store for groups of real, independently-persisted messages that
represent alternative answers to the same prompt, of which only one is shown
at a time. Tracks the ordered member list and the active member per group,
plus a registry of the peers whose messages may be grouped this way.

Deliberately independent of grouped_id/GroupedMessages, which drive native
album rendering and are not safe to repurpose for this.
For a message that has variant siblings the cell now draws an "i/N" counter
centred under the bubble, two tap zones flanking it to page between siblings,
and a stack of cards peeking out behind the bubble. The reload side button
reuses the existing Forward/Share side-button geometry and slot.

The counter is also mirrored onto the accessibility node, since the footer
counter itself is drawn on the canvas and is invisible to TalkBack. A message
without siblings takes no extra measure or draw work.
Non-active members of a variant group are dropped from the render list on both
the cache-reload and the live path, and a message that supersedes its siblings
evicts the resident one through the existing deletion primitive, so only the
active variant is ever visible. Paging swaps a cached sibling into the list in
place, without the unread-count and scroll-to-bottom side effects a genuinely
new message would carry.

Three triggers reach the same dispatch point: the counter arrows, a swipe-left
on the message, and a long-press menu item. All of them are gated on the sender
being a registered variant-authoring peer, so every other chat behaves exactly
as before.
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