UnigramDev/Unigram/search-tabs-negative-index • a2af3cb • 1 files, +3/-1
Fix crash when searching while no tab is selected
Query guarded the upper bound of SelectedTab but not the lower. SelectedTab is
bound two-way to the tab selector's SelectedIndex, which is -1 whenever there is
no selection, so Tabs[SelectedTab] threw ArgumentOutOfRangeException if the tabs
were repopulated while the user was still typing.
Reported by crash telemetry on 12.9.0.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Fix crash when searching while no tab is selected
Query guarded the upper bound of SelectedTab but not the lower. SelectedTab is
bound two-way to the tab selector's SelectedIndex, which is -1 whenever there is
no selection, so Tabs[SelectedTab] threw ArgumentOutOfRangeException if the tabs
were repopulated while the user was still typing.
Reported by crash telemetry on 12.9.0.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/fasttext-fontsize-zero • f2a73c6 • 1 files, +9/-1
Fix crash when rendering the wallpaper preview bubbles
The plain-run fast path wrote whatever font size had changed, including 0, and
XAML rejects a FontSize of 0 with E_INVALIDARG. GetOrCreateRun already treats
non-positive as "inherit" and clears the property instead; the fast path now
does the same.
MessageBubble.Mockup reaches this: it calls SetText without a font size, so the
default of 0 replaces the size the bubble was previously rendered with, and the
transition is exactly what triggers the write.
Reported by crash telemetry on 12.9.0.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Fix crash when rendering the wallpaper preview bubbles
The plain-run fast path wrote whatever font size had changed, including 0, and
XAML rejects a FontSize of 0 with E_INVALIDARG. GetOrCreateRun already treats
non-positive as "inherit" and clears the property instead; the fast path now
does the same.
MessageBubble.Mockup reaches this: it calls SetText without a font size, so the
default of 0 replaces the size the bubble was previously rendered with, and the
transition is exactly what triggers the write.
Reported by crash telemetry on 12.9.0.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/fasttext-fontsize-zero • 8dfbb0a • 1 files, +3/-9
Use the computed font size in the fast path
Per review: _fontSize is the raw value, fontSize is what AutoFontSize resolved
it to, and the slow path already passes the resolved one to GetOrCreateRun. The
fast path writing the raw value is the actual defect, so write the resolved one
instead of clearing the property — clearing would inherit the parent's size
rather than applying the theme size.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Use the computed font size in the fast path
Per review: _fontSize is the raw value, fontSize is what AutoFontSize resolved
it to, and the slow path already passes the resolved one to GetOrCreateRun. The
fast path writing the raw value is the actual defect, so write the resolved one
instead of clearing the property — clearing would inherit the parent's size
rather than applying the theme size.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/copytext-null-formattedtext • e0fae49 • 1 files, +8/-0
Fix crash when copying an empty selection or an absent note
CopyText(XamlRoot, FormattedText) dereferenced its argument immediately, but its
callers pass values that are legitimately null: TextSelectionManager's
GetSelectedText returns null when nothing is selected, BlockQuote passes the
result of FormattedTextBlock.GetSelectedText, and ProfileHeader passes
UserFullInfo.Note, which is null when the contact has no note.
The method is async void, so the NullReferenceException was posted to the
dispatcher and terminated the app instead of being catchable.
Reported by crash telemetry on 12.9.0.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Fix crash when copying an empty selection or an absent note
CopyText(XamlRoot, FormattedText) dereferenced its argument immediately, but its
callers pass values that are legitimately null: TextSelectionManager's
GetSelectedText returns null when nothing is selected, BlockQuote passes the
result of FormattedTextBlock.GetSelectedText, and ProfileHeader passes
UserFullInfo.Note, which is null when the contact has no note.
The method is async void, so the NullReferenceException was posted to the
dispatcher and terminated the app instead of being catchable.
Reported by crash telemetry on 12.9.0.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/settingsexpander-collapse-guard • ecf9d06 • 1 files, +3/-1
Fix the expander never collapsing its content
The scoped batch's Completed handler compares _tracker against a value captured
with a post-increment, so the comparison was (n + 1) == n and never held. The
handler is what applies Visibility.Collapsed after the collapse animation, and
PopupRoot.Visibility is set to Visible unconditionally just above it, so
collapsed content stayed visible and kept participating in layout — hidden only
by PopupHost.Height = 0 and the negative margin.
Pre-increment gives the guard its intended meaning: apply the final visibility
unless a newer toggle has started since this batch began.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Fix the expander never collapsing its content
The scoped batch's Completed handler compares _tracker against a value captured
with a post-increment, so the comparison was (n + 1) == n and never held. The
handler is what applies Visibility.Collapsed after the collapse animation, and
PopupRoot.Visibility is set to Visible unconditionally just above it, so
collapsed content stayed visible and kept participating in layout — hidden only
by PopupHost.Height = 0 and the negative margin.
Pre-increment gives the guard its intended meaning: apply the final visibility
unless a newer toggle has started since this batch began.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/settingspanel-negative-height • 52187f9 • 1 files, +5/-1
Never report a negative desired height from SettingsPanel
The IsHeader branch subtracts the trailing gap that follows the last child, but
the accumulator starts at 0 when IsFooter is also set, so a panel with nothing
visible reported -16. XAML rejects a negative desired size.
Both flags are set together on ShareGroupCallPopup, ChatInviteLinkInfoPopup and
FolderPage, so the combination is reachable; no crash report is currently tied
to it, this is a latent defect found while reading the layout code.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Never report a negative desired height from SettingsPanel
The IsHeader branch subtracts the trailing gap that follows the last child, but
the accumulator starts at 0 when IsFooter is also set, so a panel with nothing
visible reported -16. XAML rejects a negative desired size.
Both flags are set together on ShareGroupCallPopup, ChatInviteLinkInfoPopup and
FolderPage, so the combination is reachable; no crash report is currently tied
to it, this is a latent defect found while reading the layout code.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/voipcall-log-on-change • 7547111 • 1 files, +2/-2
Only log the voip ready state when it changes
OnStateUpdated logged before the guard that ignores an unchanged state, so a
source repeating the same state filled the whole log window with identical
lines — one crash report held nothing but Failed, every entry stamped in the
same millisecond, which cost us everything that preceded it.
OnSignalBarsUpdated just below already logs from inside its change guard.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Only log the voip ready state when it changes
OnStateUpdated logged before the guard that ignores an unchanged state, so a
source repeating the same state filled the whole log window with identical
lines — one crash report held nothing but Failed, every entry stamped in the
same millisecond, which cost us everything that preceded it.
OnSignalBarsUpdated just below already logs from inside its change guard.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/develop • 5b03668 • 3 files, +3/-17
Enable rich text editor for all users
UnigramDev/Unigram/develop • e23f722 • 1 files, +4/-0
Accessibility names for AI and rich text editor
UnigramDev/Unigram/develop • daf6021 • 1 files, +25/-13
Fix time entity paste
UnigramDev/Unigram/develop • 4743674 • 2 files, +146/-71
Align date entity parsing to TDLib
UnigramDev/Unigram/develop • 276ef92 • 2 files, +49/-36
Optimize reply markup content
UnigramDev/Unigram/develop • fd2b0c5 • 6 files, +18/-8
Fix reply markup buttons accessibility
UnigramDev/Unigram/develop • 15d6521 • 1 files, +18/-11
Fix double click to select
UnigramDev/Unigram/develop • f108380 • 1 files, +17/-2
Announce call duration on message focus
The automation name for a call message was only the outcome, so a screen reader
read "Incoming call" and nothing more. The duration was rendered by CallContent
into a TextBlock of its own, reachable only by navigating into the message, so
it was never part of what is read when the message takes focus.
Both GetSummary overloads share a helper rather than repeating the missed check
that ToOutcomeText and CallContent already make separately.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 73568f3 • 1 files, +1/-1
Fix reply in closed topics
UnigramDev/Unigram/develop • 1622cf7 • 6 files, +2612/-2585
Move service message text rendering to MessageServiceText
MessageService was 3252 lines: a 600 line control, and 2600 lines of static
text builders that the chat list, the reply preview and the automation peers
call without ever touching the control.
Pure move, no behaviour change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • f9bda35 • 1 files, +1/-1
Fix ContentOpacity default value
The property is a double but was registered with a boxed int, so the CLR
getter's cast throws for anything that reads it before it's set.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Enable rich text editor for all users
UnigramDev/Unigram/develop • e23f722 • 1 files, +4/-0
Accessibility names for AI and rich text editor
UnigramDev/Unigram/develop • daf6021 • 1 files, +25/-13
Fix time entity paste
UnigramDev/Unigram/develop • 4743674 • 2 files, +146/-71
Align date entity parsing to TDLib
UnigramDev/Unigram/develop • 276ef92 • 2 files, +49/-36
Optimize reply markup content
UnigramDev/Unigram/develop • fd2b0c5 • 6 files, +18/-8
Fix reply markup buttons accessibility
UnigramDev/Unigram/develop • 15d6521 • 1 files, +18/-11
Fix double click to select
UnigramDev/Unigram/develop • f108380 • 1 files, +17/-2
Announce call duration on message focus
The automation name for a call message was only the outcome, so a screen reader
read "Incoming call" and nothing more. The duration was rendered by CallContent
into a TextBlock of its own, reachable only by navigating into the message, so
it was never part of what is read when the message takes focus.
Both GetSummary overloads share a helper rather than repeating the missed check
that ToOutcomeText and CallContent already make separately.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 73568f3 • 1 files, +1/-1
Fix reply in closed topics
UnigramDev/Unigram/develop • 1622cf7 • 6 files, +2612/-2585
Move service message text rendering to MessageServiceText
MessageService was 3252 lines: a 600 line control, and 2600 lines of static
text builders that the chat list, the reply preview and the automation peers
call without ever touching the control.
Pure move, no behaviour change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • f9bda35 • 1 files, +1/-1
Fix ContentOpacity default value
The property is a double but was registered with a boxed int, so the CLR
getter's cast throws for anything that reads it before it's set.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/develop • cc87fde • 1 files, +3/-39
Reuse UpdateMessageTopic from UpdateContent
The MessageHeaderMessageTopic branch was a verbatim copy of it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • d5ca61b • 2 files, +23/-0
Recycle service message containers
Only MessageSelector was recycled, so a service message leaving the viewport
went back on the queue still holding its view model and its text inlines.
Also gives the per-content subclasses a place to reset the state they set,
which the shared templates need.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • be84f01 • 1 files, +16/-0
Fix stale publisher chip on recycled gift messages
MessageGift, MessageGiftedPremium, MessageGiftedStars and MessagePremiumGiftCode
share one template, but only the first one touches the publisher border, so a
recycled container kept showing the previous message's publisher.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • e2d8ffb • 1 files, +7/-9
Start a selection only on a direct hit
ResolvePosition resolves a point to the nearest selectable, clamping when the
pointer is past the end of a line, below the last block or beside a cell. A drag
needs that clamp, but a press was taking it too: pressing on media resolved to a
neighbouring caption, and the capture that follows moved the whole pointer
sequence to the selection root. Nothing else saw it, so a drag out of
PhotoContent never began.
The press now requires ResolvePosition to report a direct hit. Drags still clamp,
so extending a selection past the end of a line is unchanged.
The multi-tap branch tested the same flag to keep gap presses out of the tap
sequence, which the anchor now guarantees.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 6df1637 • 1 files, +24/-6
Fix null dereferences in service message text
- a gift purchase offer in a chat that isn't a user
- a stopped poll event whose message isn't a poll
- a refunded suggested post whose replied-to message has no post info
- an empty task list on a checklist update
All of them dereferenced without checking; they now fall back to no text.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • d715db8 • 1 files, +18/-13
Guard the entity offset in the suggested photo action
The bold entity was placed at the index of {0} in the format string, with no
check: a translation without the placeholder gave a negative offset.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Reuse UpdateMessageTopic from UpdateContent
The MessageHeaderMessageTopic branch was a verbatim copy of it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • d5ca61b • 2 files, +23/-0
Recycle service message containers
Only MessageSelector was recycled, so a service message leaving the viewport
went back on the queue still holding its view model and its text inlines.
Also gives the per-content subclasses a place to reset the state they set,
which the shared templates need.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • be84f01 • 1 files, +16/-0
Fix stale publisher chip on recycled gift messages
MessageGift, MessageGiftedPremium, MessageGiftedStars and MessagePremiumGiftCode
share one template, but only the first one touches the publisher border, so a
recycled container kept showing the previous message's publisher.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • e2d8ffb • 1 files, +7/-9
Start a selection only on a direct hit
ResolvePosition resolves a point to the nearest selectable, clamping when the
pointer is past the end of a line, below the last block or beside a cell. A drag
needs that clamp, but a press was taking it too: pressing on media resolved to a
neighbouring caption, and the capture that follows moved the whole pointer
sequence to the selection root. Nothing else saw it, so a drag out of
PhotoContent never began.
The press now requires ResolvePosition to report a direct hit. Drags still clamp,
so extending a selection past the end of a line is unchanged.
The multi-tap branch tested the same flag to keep gap presses out of the tap
sequence, which the anchor now guarantees.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 6df1637 • 1 files, +24/-6
Fix null dereferences in service message text
- a gift purchase offer in a chat that isn't a user
- a stopped poll event whose message isn't a poll
- a refunded suggested post whose replied-to message has no post info
- an empty task list on a checklist update
All of them dereferenced without checking; they now fall back to no text.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • d715db8 • 1 files, +18/-13
Guard the entity offset in the suggested photo action
The bold entity was placed at the index of {0} in the format string, with no
check: a translation without the placeholder gave a negative offset.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/develop • a1bed19 • 1 files, +7/-5
Use ActionRequestedPeerUser when no user was shared
The branch that shows it sat behind a condition identical to the one above it,
so it was unreachable and an unresolved user list rendered as a gap.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 84f8968 • 1 files, +20/-3
Keep entities aligned across a placeholder substitution
Only entities starting after the placeholder were shifted, so an entity
spanning it (markdown parsed before the substitution) kept its old length and
ended short. ReplaceWithLinks also measured against the last inserted name
rather than the placeholder, leaving anything in between unshifted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 0220eb9 • 1 files, +5/-21
Remove dead code in service message text
An if (true) with an unreachable else, a condition on a local just set to 0,
and a local assigned but never read.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 87b706c • 2 files, +9/-0
Expose ExecuteServiceMessage on the message delegate
Service message controls moving out of ChatView.xaml can no longer reach the
view's click handler, so they need the action through their own delegate.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 8b6dc02 • 5 files, +369/-331
Move the gift service message into its own control
126 lines of ChatView.xaml and four branches of UpdateContent, addressed by
string through FindName, become one control with typed fields.
The publisher chip and the ribbon are now reset in one place shared by the
three contents that don't own them, and Recycle releases the sticker.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 3bae444 • 5 files, +251/-231
Move the upgraded gift service message into its own control
Same shape as the gift one; the dead commented-out layout it carried is gone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • e9b50de • 5 files, +116/-57
Move the giveaway prize service message into its own control
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 06c45d7 • 5 files, +186/-139
Move the suggested birthdate service message into its own control
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Use ActionRequestedPeerUser when no user was shared
The branch that shows it sat behind a condition identical to the one above it,
so it was unreachable and an unresolved user list rendered as a gap.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 84f8968 • 1 files, +20/-3
Keep entities aligned across a placeholder substitution
Only entities starting after the placeholder were shifted, so an entity
spanning it (markdown parsed before the substitution) kept its old length and
ended short. ReplaceWithLinks also measured against the last inserted name
rather than the placeholder, leaving anything in between unshifted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 0220eb9 • 1 files, +5/-21
Remove dead code in service message text
An if (true) with an unreachable else, a condition on a local just set to 0,
and a local assigned but never read.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 87b706c • 2 files, +9/-0
Expose ExecuteServiceMessage on the message delegate
Service message controls moving out of ChatView.xaml can no longer reach the
view's click handler, so they need the action through their own delegate.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 8b6dc02 • 5 files, +369/-331
Move the gift service message into its own control
126 lines of ChatView.xaml and four branches of UpdateContent, addressed by
string through FindName, become one control with typed fields.
The publisher chip and the ribbon are now reset in one place shared by the
three contents that don't own them, and Recycle releases the sticker.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 3bae444 • 5 files, +251/-231
Move the upgraded gift service message into its own control
Same shape as the gift one; the dead commented-out layout it carried is gone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • e9b50de • 5 files, +116/-57
Move the giveaway prize service message into its own control
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 06c45d7 • 5 files, +186/-139
Move the suggested birthdate service message into its own control
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/develop • 1d8c026 • 5 files, +123/-84
Move the chat background service message into its own control
Covers the event log counterpart too, which shares the template.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 606cb04 • 6 files, +205/-165
Move the photo service message into its own control
Chat photo, suggested profile photo and story mention share one template; the
story-opening click moves with them, so ChatView no longer reaches into the
template by name to find the segments to animate from.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 3d4814f • 8 files, +80/-145
Move the unsupported message into its own control
Unsupported messages have been rendered by the service template for a while, so
the old Content\UnsupportedContent control was only still reachable through the
bubble content factory. Its update check, the one part still in use, moves with
the template.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 8b4907d • 10 files, +342/-243
Move the header service messages into their own controls
The account info and message topic headers were the last two contents
UpdateContent knew about, so the base implementation is now empty and
MessageService no longer reaches into a template it doesn't own.
UpdateServiceWithForumTopic dropped its action parameter with them: it existed
to call UpdateMessageTopic, which is now the header control's own method.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • e634d66 • 1 files, +38/-7
Allow a selection to start from the bubble surface
Requiring a direct hit kept media drags working but took the gap with it: a press
in the padding beside the text, which ResolvePosition clamps to the nearest
block, no longer began a selection.
The press is now filtered by what it landed on rather than by whether the
position was clamped. Walking up from the pressed element to the root, a
selectable starts a gesture, any other IContent refuses it, and reaching the root
without crossing either means the press was on the bubble itself. Testing the
root by identity would not do: a press on the bubble background reports whichever
template element carries the brush, never the root the manager was attached to.
The multi-tap branch tests the clamp again, since clamped presses can begin a
gesture once more and a click beside the text should not turn the next one into a
word selection.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • fb2e4b3 • 1 files, +1/-1
Update TDLib
#unigram
Move the chat background service message into its own control
Covers the event log counterpart too, which shares the template.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 606cb04 • 6 files, +205/-165
Move the photo service message into its own control
Chat photo, suggested profile photo and story mention share one template; the
story-opening click moves with them, so ChatView no longer reaches into the
template by name to find the segments to animate from.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 3d4814f • 8 files, +80/-145
Move the unsupported message into its own control
Unsupported messages have been rendered by the service template for a while, so
the old Content\UnsupportedContent control was only still reachable through the
bubble content factory. Its update check, the one part still in use, moves with
the template.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 8b4907d • 10 files, +342/-243
Move the header service messages into their own controls
The account info and message topic headers were the last two contents
UpdateContent knew about, so the base implementation is now empty and
MessageService no longer reaches into a template it doesn't own.
UpdateServiceWithForumTopic dropped its action parameter with them: it existed
to call UpdateMessageTopic, which is now the header control's own method.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • e634d66 • 1 files, +38/-7
Allow a selection to start from the bubble surface
Requiring a direct hit kept media drags working but took the gap with it: a press
in the padding beside the text, which ResolvePosition clamps to the nearest
block, no longer began a selection.
The press is now filtered by what it landed on rather than by whether the
position was clamped. Walking up from the pressed element to the root, a
selectable starts a gesture, any other IContent refuses it, and reaching the root
without crossing either means the press was on the bubble itself. Testing the
root by identity would not do: a press on the bubble background reports whichever
template element carries the brush, never the root the manager was attached to.
The multi-tap branch tests the clamp again, since clamped presses can begin a
gesture once more and a click beside the text should not turn the next one into a
word selection.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • fb2e4b3 • 1 files, +1/-1
Update TDLib
#unigram
UnigramDev/Unigram/develop • 953756e • 1 files, +1/-1
Fix crash when sending non-text content with ephemeral bot commands (#3323)
CreateSendMessage maps anything that is not an InputMessageText to a null
caption, then dereferenced it unconditionally. Sending a sticker (or any
other media) in a chat where HasEphemeralBotCommands is set crashed with a
NullReferenceException.
Reported by crash telemetry on 12.9.0.0.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • f254228 • 1 files, +17/-0
Translate font and render target error messages (#3324)
DirectWrite's two font-file errors were missing from TranslateText entirely, so
every locale reported them as its own crash group. The render target message was
already translated, but only for a few locales.
Adds the localized variants observed in crash reports:
- font file not found: de, es, fr, it, pt, ru, tr, zh-Hans
- font file access denied: fr, ru
- wrong render target: es, fr, ru
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 0d1ab88 • 1 files, +6/-0
Fix crash when opening the gallery (#3326)
ConvertOf is an x:Bind function binding over ViewModel.SelectedItem, and
Bindings.Update runs from Load before an item has been selected, so the very
first evaluation dereferenced a null GalleryMedia.
ConvertCaption in the same file already guards its input the same way.
Reported by crash telemetry on 12.9.0.0.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 1088776 • 1 files, +3/-1
Fix crash when rendering the wallpaper preview bubbles (#3327)
* Fix crash when rendering the wallpaper preview bubbles
The plain-run fast path wrote whatever font size had changed, including 0, and
XAML rejects a FontSize of 0 with E_INVALIDARG. GetOrCreateRun already treats
non-positive as "inherit" and clears the property instead; the fast path now
does the same.
MessageBubble.Mockup reaches this: it calls SetText without a font size, so the
default of 0 replaces the size the bubble was previously rendered with, and the
transition is exactly what triggers the write.
Reported by crash telemetry on 12.9.0.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Use the computed font size in the fast path
Per review: _fontSize is the raw value, fontSize is what AutoFontSize resolved
it to, and the slow path already passes the resolved one to GetOrCreateRun. The
fast path writing the raw value is the actual defect, so write the resolved one
instead of clearing the property — clearing would inherit the parent's size
rather than applying the theme size.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Fix crash when sending non-text content with ephemeral bot commands (#3323)
CreateSendMessage maps anything that is not an InputMessageText to a null
caption, then dereferenced it unconditionally. Sending a sticker (or any
other media) in a chat where HasEphemeralBotCommands is set crashed with a
NullReferenceException.
Reported by crash telemetry on 12.9.0.0.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • f254228 • 1 files, +17/-0
Translate font and render target error messages (#3324)
DirectWrite's two font-file errors were missing from TranslateText entirely, so
every locale reported them as its own crash group. The render target message was
already translated, but only for a few locales.
Adds the localized variants observed in crash reports:
- font file not found: de, es, fr, it, pt, ru, tr, zh-Hans
- font file access denied: fr, ru
- wrong render target: es, fr, ru
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 0d1ab88 • 1 files, +6/-0
Fix crash when opening the gallery (#3326)
ConvertOf is an x:Bind function binding over ViewModel.SelectedItem, and
Bindings.Update runs from Load before an item has been selected, so the very
first evaluation dereferenced a null GalleryMedia.
ConvertCaption in the same file already guards its input the same way.
Reported by crash telemetry on 12.9.0.0.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 1088776 • 1 files, +3/-1
Fix crash when rendering the wallpaper preview bubbles (#3327)
* Fix crash when rendering the wallpaper preview bubbles
The plain-run fast path wrote whatever font size had changed, including 0, and
XAML rejects a FontSize of 0 with E_INVALIDARG. GetOrCreateRun already treats
non-positive as "inherit" and clears the property instead; the fast path now
does the same.
MessageBubble.Mockup reaches this: it calls SetText without a font size, so the
default of 0 replaces the size the bubble was previously rendered with, and the
transition is exactly what triggers the write.
Reported by crash telemetry on 12.9.0.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Use the computed font size in the fast path
Per review: _fontSize is the raw value, fontSize is what AutoFontSize resolved
it to, and the slow path already passes the resolved one to GetOrCreateRun. The
fast path writing the raw value is the actual defect, so write the resolved one
instead of clearing the property — clearing would inherit the parent's size
rather than applying the theme size.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/develop • 50f25b1 • 1 files, +5/-1
Never report a negative desired height from SettingsPanel (#3328)
The IsHeader branch subtracts the trailing gap that follows the last child, but
the accumulator starts at 0 when IsFooter is also set, so a panel with nothing
visible reported -16. XAML rejects a negative desired size.
Both flags are set together on ShareGroupCallPopup, ChatInviteLinkInfoPopup and
FolderPage, so the combination is reachable; no crash report is currently tied
to it, this is a latent defect found while reading the layout code.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • afb36b1 • 1 files, +3/-1
Fix the expander never collapsing its content (#3329)
The scoped batch's Completed handler compares _tracker against a value captured
with a post-increment, so the comparison was (n + 1) == n and never held. The
handler is what applies Visibility.Collapsed after the collapse animation, and
PopupRoot.Visibility is set to Visible unconditionally just above it, so
collapsed content stayed visible and kept participating in layout — hidden only
by PopupHost.Height = 0 and the negative margin.
Pre-increment gives the guard its intended meaning: apply the final visibility
unless a newer toggle has started since this batch began.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • e032aae • 1 files, +8/-0
Fix crash when copying an empty selection or an absent note (#3330)
CopyText(XamlRoot, FormattedText) dereferenced its argument immediately, but its
callers pass values that are legitimately null: TextSelectionManager's
GetSelectedText returns null when nothing is selected, BlockQuote passes the
result of FormattedTextBlock.GetSelectedText, and ProfileHeader passes
UserFullInfo.Note, which is null when the contact has no note.
The method is async void, so the NullReferenceException was posted to the
dispatcher and terminated the app instead of being catchable.
Reported by crash telemetry on 12.9.0.0.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • e5fe62d • 1 files, +2/-2
Only log the voip ready state when it changes (#3331)
OnStateUpdated logged before the guard that ignores an unchanged state, so a
source repeating the same state filled the whole log window with identical
lines — one crash report held nothing but Failed, every entry stamped in the
same millisecond, which cost us everything that preceded it.
OnSignalBarsUpdated just below already logs from inside its change guard.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Never report a negative desired height from SettingsPanel (#3328)
The IsHeader branch subtracts the trailing gap that follows the last child, but
the accumulator starts at 0 when IsFooter is also set, so a panel with nothing
visible reported -16. XAML rejects a negative desired size.
Both flags are set together on ShareGroupCallPopup, ChatInviteLinkInfoPopup and
FolderPage, so the combination is reachable; no crash report is currently tied
to it, this is a latent defect found while reading the layout code.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • afb36b1 • 1 files, +3/-1
Fix the expander never collapsing its content (#3329)
The scoped batch's Completed handler compares _tracker against a value captured
with a post-increment, so the comparison was (n + 1) == n and never held. The
handler is what applies Visibility.Collapsed after the collapse animation, and
PopupRoot.Visibility is set to Visible unconditionally just above it, so
collapsed content stayed visible and kept participating in layout — hidden only
by PopupHost.Height = 0 and the negative margin.
Pre-increment gives the guard its intended meaning: apply the final visibility
unless a newer toggle has started since this batch began.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • e032aae • 1 files, +8/-0
Fix crash when copying an empty selection or an absent note (#3330)
CopyText(XamlRoot, FormattedText) dereferenced its argument immediately, but its
callers pass values that are legitimately null: TextSelectionManager's
GetSelectedText returns null when nothing is selected, BlockQuote passes the
result of FormattedTextBlock.GetSelectedText, and ProfileHeader passes
UserFullInfo.Note, which is null when the contact has no note.
The method is async void, so the NullReferenceException was posted to the
dispatcher and terminated the app instead of being catchable.
Reported by crash telemetry on 12.9.0.0.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • e5fe62d • 1 files, +2/-2
Only log the voip ready state when it changes (#3331)
OnStateUpdated logged before the guard that ignores an unchanged state, so a
source repeating the same state filled the whole log window with identical
lines — one crash report held nothing but Failed, every entry stamped in the
same millisecond, which cost us everything that preceded it.
OnSignalBarsUpdated just below already logs from inside its change guard.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/logger-ring-buffer • 271c644 • 1 files, +36/-13
Keep the log tail in a ring buffer
Three things were wrong with the window that ships in every crash report.
It was a List trimmed with RemoveAt(0), so once full every log call copied all
retained entries down one slot. Logging happens on hot paths; this is now an
overwrite of one slot.
Dump stored its "Bump" marker as an entry, so taking a dump evicted a real line
and a second dump left the first marker behind. It is appended to the output
instead, and Dump no longer mutates anything.
The size was an inline 50, which is under a second of ordinary logging. It is
now a named constant at 200.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Keep the log tail in a ring buffer
Three things were wrong with the window that ships in every crash report.
It was a List trimmed with RemoveAt(0), so once full every log call copied all
retained entries down one slot. Logging happens on hot paths; this is now an
overwrite of one slot.
Dump stored its "Bump" marker as an entry, so taking a dump evicted a real line
and a second dump left the first marker behind. It is appended to the output
instead, and Dump no longer mutates anything.
The size was an inline 50, which is under a second of ordinary logging. It is
now a named constant at 200.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/develop • 80ea15f • 1 files, +36/-13
Keep the log tail in a ring buffer (#3332)
Three things were wrong with the window that ships in every crash report.
It was a List trimmed with RemoveAt(0), so once full every log call copied all
retained entries down one slot. Logging happens on hot paths; this is now an
overwrite of one slot.
Dump stored its "Bump" marker as an entry, so taking a dump evicted a real line
and a second dump left the first marker behind. It is appended to the output
instead, and Dump no longer mutates anything.
The size was an inline 50, which is under a second of ordinary logging. It is
now a named constant at 200.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Keep the log tail in a ring buffer (#3332)
Three things were wrong with the window that ships in every crash report.
It was a List trimmed with RemoveAt(0), so once full every log call copied all
retained entries down one slot. Logging happens on hot paths; this is now an
overwrite of one slot.
Dump stored its "Bump" marker as an entry, so taking a dump evicted a real line
and a second dump left the first marker behind. It is appended to the output
instead, and Dump no longer mutates anything.
The size was an inline 50, which is under a second of ordinary logging. It is
now a named constant at 200.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/develop • c095b72 • 1 files, +1/-0
Add todo note
UnigramDev/Unigram/develop • e10cce4 • 1 files, +10/-4
Optimize hashset pop
UnigramDev/Unigram/develop • 0392794 • 7 files, +127/-8
Experimental bubble content recycling
UnigramDev/Unigram/develop • 3063bc6 • 2 files, +10/-0
Clear text block pool on navigate
UnigramDev/Unigram/develop • 1766419 • 8 files, +113/-57
Improve recycling for some contents
UnigramDev/Unigram/develop • 50eeabf • 1 files, +255/-0
Started to work on WebRTC issues
UnigramDev/Unigram/develop • 7d73e2b • 2 files, +0/-3
Code cleanup
UnigramDev/Unigram/develop • 63857cc • 2 files, +0/-0
Update tlottie to latest dev
UnigramDev/Unigram/develop • 54759b3 • 1 files, +4/-4
Update webrtc todo
#unigram
Add todo note
UnigramDev/Unigram/develop • e10cce4 • 1 files, +10/-4
Optimize hashset pop
UnigramDev/Unigram/develop • 0392794 • 7 files, +127/-8
Experimental bubble content recycling
UnigramDev/Unigram/develop • 3063bc6 • 2 files, +10/-0
Clear text block pool on navigate
UnigramDev/Unigram/develop • 1766419 • 8 files, +113/-57
Improve recycling for some contents
UnigramDev/Unigram/develop • 50eeabf • 1 files, +255/-0
Started to work on WebRTC issues
UnigramDev/Unigram/develop • 7d73e2b • 2 files, +0/-3
Code cleanup
UnigramDev/Unigram/develop • 63857cc • 2 files, +0/-0
Update tlottie to latest dev
UnigramDev/Unigram/develop • 54759b3 • 1 files, +4/-4
Update webrtc todo
#unigram
UnigramDev/Unigram/search-tabs-negative-index • 7b9967e • 1 files, +13/-8
Keep SelectedTab in range instead of guarding its readers
Per review: nothing deselects a tab, so the question was where -1 comes from.
TopNavView.SelectedIndex is bound TwoWay, and a selector reports -1 whenever it
holds no selection — which it does while its ItemsSource is being attached.
BindableBase.Set stores the value and raises the notification before returning,
so the existing guard, which ran after Set and only checked the upper bound,
had already let -1 into the field. Tabs[value] on the next line was reading at
-1 too; the reported crash simply landed later, in Query, on the next
keystroke.
Validating before the value is committed keeps the field in range for every
reader — Query, ItemsView, IsTopChatsVisible and the setter itself — so the
guard added to Query in the first commit is no longer needed and is reverted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Keep SelectedTab in range instead of guarding its readers
Per review: nothing deselects a tab, so the question was where -1 comes from.
TopNavView.SelectedIndex is bound TwoWay, and a selector reports -1 whenever it
holds no selection — which it does while its ItemsSource is being attached.
BindableBase.Set stores the value and raises the notification before returning,
so the existing guard, which ran after Set and only checked the upper bound,
had already let -1 into the field. Tabs[value] on the next line was reading at
-1 too; the reported crash simply landed later, in Query, on the next
keystroke.
Validating before the value is committed keeps the field in range for every
reader — Query, ItemsView, IsTopChatsVisible and the setter itself — so the
guard added to Query in the first commit is no longer needed and is reverted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/temp • 5054be6 • 1 files, +51/-30
Fix reply markup button templates
UnigramDev/Unigram/temp • 46b282e • 3 files, +3/-17
Enable rich text editor for all users
UnigramDev/Unigram/temp • 4be40ee • 1 files, +4/-0
Accessibility names for AI and rich text editor
UnigramDev/Unigram/temp • 540bf51 • 1 files, +25/-13
Fix time entity paste
UnigramDev/Unigram/temp • c3378ab • 2 files, +146/-71
Align date entity parsing to TDLib
UnigramDev/Unigram/temp • 6f4ffcd • 6 files, +18/-8
Fix reply markup buttons accessibility
UnigramDev/Unigram/temp • 663f2fc • 1 files, +18/-11
Fix double click to select
UnigramDev/Unigram/temp • b9fda8f • 1 files, +17/-2
Announce call duration on message focus
The automation name for a call message was only the outcome, so a screen reader
read "Incoming call" and nothing more. The duration was rendered by CallContent
into a TextBlock of its own, reachable only by navigating into the message, so
it was never part of what is read when the message takes focus.
Both GetSummary overloads share a helper rather than repeating the missed check
that ToOutcomeText and CallContent already make separately.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • b1b4ee0 • 1 files, +1/-1
Fix reply in closed topics
UnigramDev/Unigram/temp • 6b744ad • 6 files, +2612/-2585
Move service message text rendering to MessageServiceText
MessageService was 3252 lines: a 600 line control, and 2600 lines of static
text builders that the chat list, the reply preview and the automation peers
call without ever touching the control.
Pure move, no behaviour change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 7324f09 • 1 files, +1/-1
Fix ContentOpacity default value
The property is a double but was registered with a boxed int, so the CLR
getter's cast throws for anything that reads it before it's set.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Fix reply markup button templates
UnigramDev/Unigram/temp • 46b282e • 3 files, +3/-17
Enable rich text editor for all users
UnigramDev/Unigram/temp • 4be40ee • 1 files, +4/-0
Accessibility names for AI and rich text editor
UnigramDev/Unigram/temp • 540bf51 • 1 files, +25/-13
Fix time entity paste
UnigramDev/Unigram/temp • c3378ab • 2 files, +146/-71
Align date entity parsing to TDLib
UnigramDev/Unigram/temp • 6f4ffcd • 6 files, +18/-8
Fix reply markup buttons accessibility
UnigramDev/Unigram/temp • 663f2fc • 1 files, +18/-11
Fix double click to select
UnigramDev/Unigram/temp • b9fda8f • 1 files, +17/-2
Announce call duration on message focus
The automation name for a call message was only the outcome, so a screen reader
read "Incoming call" and nothing more. The duration was rendered by CallContent
into a TextBlock of its own, reachable only by navigating into the message, so
it was never part of what is read when the message takes focus.
Both GetSummary overloads share a helper rather than repeating the missed check
that ToOutcomeText and CallContent already make separately.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • b1b4ee0 • 1 files, +1/-1
Fix reply in closed topics
UnigramDev/Unigram/temp • 6b744ad • 6 files, +2612/-2585
Move service message text rendering to MessageServiceText
MessageService was 3252 lines: a 600 line control, and 2600 lines of static
text builders that the chat list, the reply preview and the automation peers
call without ever touching the control.
Pure move, no behaviour change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 7324f09 • 1 files, +1/-1
Fix ContentOpacity default value
The property is a double but was registered with a boxed int, so the CLR
getter's cast throws for anything that reads it before it's set.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/temp • 9b6feb4 • 1 files, +3/-39
Reuse UpdateMessageTopic from UpdateContent
The MessageHeaderMessageTopic branch was a verbatim copy of it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 8282849 • 2 files, +23/-0
Recycle service message containers
Only MessageSelector was recycled, so a service message leaving the viewport
went back on the queue still holding its view model and its text inlines.
Also gives the per-content subclasses a place to reset the state they set,
which the shared templates need.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 3bd2fe4 • 1 files, +16/-0
Fix stale publisher chip on recycled gift messages
MessageGift, MessageGiftedPremium, MessageGiftedStars and MessagePremiumGiftCode
share one template, but only the first one touches the publisher border, so a
recycled container kept showing the previous message's publisher.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 551580a • 1 files, +7/-9
Start a selection only on a direct hit
ResolvePosition resolves a point to the nearest selectable, clamping when the
pointer is past the end of a line, below the last block or beside a cell. A drag
needs that clamp, but a press was taking it too: pressing on media resolved to a
neighbouring caption, and the capture that follows moved the whole pointer
sequence to the selection root. Nothing else saw it, so a drag out of
PhotoContent never began.
The press now requires ResolvePosition to report a direct hit. Drags still clamp,
so extending a selection past the end of a line is unchanged.
The multi-tap branch tested the same flag to keep gap presses out of the tap
sequence, which the anchor now guarantees.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 34ab631 • 1 files, +24/-6
Fix null dereferences in service message text
- a gift purchase offer in a chat that isn't a user
- a stopped poll event whose message isn't a poll
- a refunded suggested post whose replied-to message has no post info
- an empty task list on a checklist update
All of them dereferenced without checking; they now fall back to no text.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • ab2e714 • 1 files, +18/-13
Guard the entity offset in the suggested photo action
The bold entity was placed at the index of {0} in the format string, with no
check: a translation without the placeholder gave a negative offset.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Reuse UpdateMessageTopic from UpdateContent
The MessageHeaderMessageTopic branch was a verbatim copy of it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 8282849 • 2 files, +23/-0
Recycle service message containers
Only MessageSelector was recycled, so a service message leaving the viewport
went back on the queue still holding its view model and its text inlines.
Also gives the per-content subclasses a place to reset the state they set,
which the shared templates need.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 3bd2fe4 • 1 files, +16/-0
Fix stale publisher chip on recycled gift messages
MessageGift, MessageGiftedPremium, MessageGiftedStars and MessagePremiumGiftCode
share one template, but only the first one touches the publisher border, so a
recycled container kept showing the previous message's publisher.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 551580a • 1 files, +7/-9
Start a selection only on a direct hit
ResolvePosition resolves a point to the nearest selectable, clamping when the
pointer is past the end of a line, below the last block or beside a cell. A drag
needs that clamp, but a press was taking it too: pressing on media resolved to a
neighbouring caption, and the capture that follows moved the whole pointer
sequence to the selection root. Nothing else saw it, so a drag out of
PhotoContent never began.
The press now requires ResolvePosition to report a direct hit. Drags still clamp,
so extending a selection past the end of a line is unchanged.
The multi-tap branch tested the same flag to keep gap presses out of the tap
sequence, which the anchor now guarantees.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 34ab631 • 1 files, +24/-6
Fix null dereferences in service message text
- a gift purchase offer in a chat that isn't a user
- a stopped poll event whose message isn't a poll
- a refunded suggested post whose replied-to message has no post info
- an empty task list on a checklist update
All of them dereferenced without checking; they now fall back to no text.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • ab2e714 • 1 files, +18/-13
Guard the entity offset in the suggested photo action
The bold entity was placed at the index of {0} in the format string, with no
check: a translation without the placeholder gave a negative offset.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/temp • fbc19b7 • 1 files, +7/-5
Use ActionRequestedPeerUser when no user was shared
The branch that shows it sat behind a condition identical to the one above it,
so it was unreachable and an unresolved user list rendered as a gap.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • f15da6e • 1 files, +20/-3
Keep entities aligned across a placeholder substitution
Only entities starting after the placeholder were shifted, so an entity
spanning it (markdown parsed before the substitution) kept its old length and
ended short. ReplaceWithLinks also measured against the last inserted name
rather than the placeholder, leaving anything in between unshifted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 3dfb83d • 1 files, +5/-21
Remove dead code in service message text
An if (true) with an unreachable else, a condition on a local just set to 0,
and a local assigned but never read.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 6fa750b • 2 files, +9/-0
Expose ExecuteServiceMessage on the message delegate
Service message controls moving out of ChatView.xaml can no longer reach the
view's click handler, so they need the action through their own delegate.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • d09f682 • 5 files, +369/-331
Move the gift service message into its own control
126 lines of ChatView.xaml and four branches of UpdateContent, addressed by
string through FindName, become one control with typed fields.
The publisher chip and the ribbon are now reset in one place shared by the
three contents that don't own them, and Recycle releases the sticker.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 4035780 • 5 files, +251/-231
Move the upgraded gift service message into its own control
Same shape as the gift one; the dead commented-out layout it carried is gone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • f88e127 • 5 files, +116/-57
Move the giveaway prize service message into its own control
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 6708089 • 5 files, +186/-139
Move the suggested birthdate service message into its own control
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Use ActionRequestedPeerUser when no user was shared
The branch that shows it sat behind a condition identical to the one above it,
so it was unreachable and an unresolved user list rendered as a gap.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • f15da6e • 1 files, +20/-3
Keep entities aligned across a placeholder substitution
Only entities starting after the placeholder were shifted, so an entity
spanning it (markdown parsed before the substitution) kept its old length and
ended short. ReplaceWithLinks also measured against the last inserted name
rather than the placeholder, leaving anything in between unshifted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 3dfb83d • 1 files, +5/-21
Remove dead code in service message text
An if (true) with an unreachable else, a condition on a local just set to 0,
and a local assigned but never read.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 6fa750b • 2 files, +9/-0
Expose ExecuteServiceMessage on the message delegate
Service message controls moving out of ChatView.xaml can no longer reach the
view's click handler, so they need the action through their own delegate.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • d09f682 • 5 files, +369/-331
Move the gift service message into its own control
126 lines of ChatView.xaml and four branches of UpdateContent, addressed by
string through FindName, become one control with typed fields.
The publisher chip and the ribbon are now reset in one place shared by the
three contents that don't own them, and Recycle releases the sticker.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 4035780 • 5 files, +251/-231
Move the upgraded gift service message into its own control
Same shape as the gift one; the dead commented-out layout it carried is gone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • f88e127 • 5 files, +116/-57
Move the giveaway prize service message into its own control
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 6708089 • 5 files, +186/-139
Move the suggested birthdate service message into its own control
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/temp • 1f8ad8a • 5 files, +123/-84
Move the chat background service message into its own control
Covers the event log counterpart too, which shares the template.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • bbce8fa • 6 files, +205/-165
Move the photo service message into its own control
Chat photo, suggested profile photo and story mention share one template; the
story-opening click moves with them, so ChatView no longer reaches into the
template by name to find the segments to animate from.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • a8e0f54 • 8 files, +80/-145
Move the unsupported message into its own control
Unsupported messages have been rendered by the service template for a while, so
the old Content\UnsupportedContent control was only still reachable through the
bubble content factory. Its update check, the one part still in use, moves with
the template.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 9c0ff6d • 10 files, +342/-243
Move the header service messages into their own controls
The account info and message topic headers were the last two contents
UpdateContent knew about, so the base implementation is now empty and
MessageService no longer reaches into a template it doesn't own.
UpdateServiceWithForumTopic dropped its action parameter with them: it existed
to call UpdateMessageTopic, which is now the header control's own method.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 03f7b19 • 1 files, +38/-7
Allow a selection to start from the bubble surface
Requiring a direct hit kept media drags working but took the gap with it: a press
in the padding beside the text, which ResolvePosition clamps to the nearest
block, no longer began a selection.
The press is now filtered by what it landed on rather than by whether the
position was clamped. Walking up from the pressed element to the root, a
selectable starts a gesture, any other IContent refuses it, and reaching the root
without crossing either means the press was on the bubble itself. Testing the
root by identity would not do: a press on the bubble background reports whichever
template element carries the brush, never the root the manager was attached to.
The multi-tap branch tests the clamp again, since clamped presses can begin a
gesture once more and a click beside the text should not turn the next one into a
word selection.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 13b0aa2 • 1 files, +1/-1
Update TDLib
#unigram
Move the chat background service message into its own control
Covers the event log counterpart too, which shares the template.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • bbce8fa • 6 files, +205/-165
Move the photo service message into its own control
Chat photo, suggested profile photo and story mention share one template; the
story-opening click moves with them, so ChatView no longer reaches into the
template by name to find the segments to animate from.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • a8e0f54 • 8 files, +80/-145
Move the unsupported message into its own control
Unsupported messages have been rendered by the service template for a while, so
the old Content\UnsupportedContent control was only still reachable through the
bubble content factory. Its update check, the one part still in use, moves with
the template.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 9c0ff6d • 10 files, +342/-243
Move the header service messages into their own controls
The account info and message topic headers were the last two contents
UpdateContent knew about, so the base implementation is now empty and
MessageService no longer reaches into a template it doesn't own.
UpdateServiceWithForumTopic dropped its action parameter with them: it existed
to call UpdateMessageTopic, which is now the header control's own method.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 03f7b19 • 1 files, +38/-7
Allow a selection to start from the bubble surface
Requiring a direct hit kept media drags working but took the gap with it: a press
in the padding beside the text, which ResolvePosition clamps to the nearest
block, no longer began a selection.
The press is now filtered by what it landed on rather than by whether the
position was clamped. Walking up from the pressed element to the root, a
selectable starts a gesture, any other IContent refuses it, and reaching the root
without crossing either means the press was on the bubble itself. Testing the
root by identity would not do: a press on the bubble background reports whichever
template element carries the brush, never the root the manager was attached to.
The multi-tap branch tests the clamp again, since clamped presses can begin a
gesture once more and a click beside the text should not turn the next one into a
word selection.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/temp • 13b0aa2 • 1 files, +1/-1
Update TDLib
#unigram