[morethanwords/tweb/master](https://github.com/morethanwords/tweb/tree/master) • [a381f83](https://github.com/morethanwords/tweb/commit/a381f83133e2f0549b941253b6aecb49d1389239) • _3 files, +45/-5_
Make the dialog list's delete cover both collections it reads from

The list keeps pinned entries in their own collection, and every read API merges
the two - has(), getDialogElement() and getAllDialogElementsMap() all see pinned
entries - while delete() only ever removed from the unpinned one. On top of that
removeItem answered from the merged map, so it reported success for a row it had
left in place, which would take adjustTotalCount down for a dialog that never
went away and skip onItemDiscard, leaving the DialogElement holding its
middlewareHelper.

No caller can reach that today. Real DialogElements are pinned only by addPinned,
used by the monoforum and bot-forum tabs to pin the parent peer above the topic
list, and those tabs build on AutonomousDialogListBase - not AutonomousDialogList,
where the deletes that walk the merged view (validateListForFilter,
updateDialog) live. The keys their own deletes carry - monoforum thread peers,
topic ids - never equal the pinned parent. So this closes an asymmetry between
what the list reports and what it does rather than fixing an observed bug.

Make each remover report what it actually removed, bail before writing when the
id is not theirs (the setter hands back a fresh array either way, invalidating
the memos that rebuild the rendered list), and have delete() cover both sides.

Should a pinned key ever reach delete(), its row now goes away with it instead of
staying behind as a header for a dialog that no longer exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

[morethanwords/tweb/master](https://github.com/morethanwords/tweb/tree/master) • [8314c57](https://github.com/morethanwords/tweb/commit/8314c57ac064bc344927fee04279bdf16b04fdf9) • _8 files, +114/-16_
Hold the panel's reordering until it hides, and reorder a gif that was used

Sending a sticker from the panel reshuffled the recent row on screen. Only one
event was ever postponed - stickers_top - and even that one slipped through:
postponing checked isActive(), the class that sendDocId drops the moment the
send resolves, while the panel keeps fading out for another 200ms. Anything
arriving in that window was applied on a panel still in plain view.

Postponing now waits for the panel to be off screen (isDisplayed) and covers
every event that changes order: the recent and faved stickers, the set order,
the recent emoji and the saved gifs. What the user does in the open panel -
faving, deleting from the recent ones, clearing them, un-saving a gif - still
lands at once; a list that got shorter is the tell.

The emoji tab subscribed to emoji_recent twice, postponed and immediate, so the
recent row always reshuffled live and the delete branch had to cancel its own
queued copy. One subscription now, and that guard turned into the one that
matters: deleting an emoji drops a queued use of it, so closing the panel
cannot bring it back.

Gifs were not reordered at all - only the sticker half of it was ever wired up.
Every official client moves a gif that was just sent to the front of the saved
ones (tdesktop Stickers::addSavedGif, Android MediaDataController.addRecentGif,
iOS ApplyUpdateMessage), locally and without a request of its own: the server
reorders the list on send and updateSavedGifs brings the result over.
appGifsManager.addRecentGif does the same, both for a gif sent from the panel
and for one picked out of an inline bot's results, and the gifs tab got the
super.init() it never had - without it nothing flushed its postponed events.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

[#webk](?q=%23webk)