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
UnigramDev/Unigram/temp • 2b6f4ac • 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/temp • e53bc6c • 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/temp • 3b186d2 • 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/temp • 63a834d • 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/temp • e53bc6c • 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/temp • 3b186d2 • 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/temp • 63a834d • 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/temp • 21628b7 • 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/temp • 26e3672 • 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/temp • d675e79 • 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/temp • 7d0d7be • 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/temp • 26e3672 • 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/temp • d675e79 • 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/temp • 7d0d7be • 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/temp • ea40fcc • 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>
UnigramDev/Unigram/temp • da46bee • 1 files, +1/-0
Add todo note
UnigramDev/Unigram/temp • 066672a • 1 files, +10/-4
Optimize hashset pop
UnigramDev/Unigram/temp • 320dce9 • 7 files, +127/-8
Experimental bubble content recycling
UnigramDev/Unigram/temp • d642d86 • 2 files, +10/-0
Clear text block pool on navigate
UnigramDev/Unigram/temp • e7ffacf • 8 files, +113/-57
Improve recycling for some contents
UnigramDev/Unigram/temp • 99d1e14 • 1 files, +255/-0
Started to work on WebRTC issues
UnigramDev/Unigram/temp • c71aa98 • 2 files, +0/-3
Code cleanup
UnigramDev/Unigram/temp • 7446972 • 2 files, +0/-0
Update tlottie to latest dev
UnigramDev/Unigram/temp • 9090b24 • 1 files, +4/-4
Update webrtc todo
UnigramDev/Unigram/temp • c533f04 • 2 files, +2/-2
Bump version to 12.9.1
#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>
UnigramDev/Unigram/temp • da46bee • 1 files, +1/-0
Add todo note
UnigramDev/Unigram/temp • 066672a • 1 files, +10/-4
Optimize hashset pop
UnigramDev/Unigram/temp • 320dce9 • 7 files, +127/-8
Experimental bubble content recycling
UnigramDev/Unigram/temp • d642d86 • 2 files, +10/-0
Clear text block pool on navigate
UnigramDev/Unigram/temp • e7ffacf • 8 files, +113/-57
Improve recycling for some contents
UnigramDev/Unigram/temp • 99d1e14 • 1 files, +255/-0
Started to work on WebRTC issues
UnigramDev/Unigram/temp • c71aa98 • 2 files, +0/-3
Code cleanup
UnigramDev/Unigram/temp • 7446972 • 2 files, +0/-0
Update tlottie to latest dev
UnigramDev/Unigram/temp • 9090b24 • 1 files, +4/-4
Update webrtc todo
UnigramDev/Unigram/temp • c533f04 • 2 files, +2/-2
Bump version to 12.9.1
#unigram
UnigramDev/Unigram/devicelost • ba693f9 • 2 files, +41/-13
Don't recreate the Direct3D device from the device-lost callback
PlaceholderImageHelper watches its D3D device with RegisterDeviceRemovedEvent
and recreated the device straight from the threadpool wait callback. That runs
while the display driver is still resetting, and D3D11CreateDevice at that
moment faults inside the vendor user-mode driver (reported by crash telemetry
on 12.9, on two different Intel UMDs).
The device is now rebuilt lazily instead: PlaceholderHelper already calls
HandleDeviceLost() on every access to the singleton, so the next use recreates
it from the UI thread. HandleDeviceLost tolerates a null device, which is the
state left behind when creation failed.
Two related lifetime fixes in the same path:
- CreateDeviceResources released nothing before calling D3D11CreateDevice.
com_ptr::put() overwrites without releasing, so recreating over the removed
device stranded it and everything the driver held behind it.
- PlaceholderImageHelper::Close() called StopWatchingCurrentDevice(), which
closes the threadpool wait but does not wait for a callback that is already
running -- the callback then reached RaiseDeviceLostEvent on an object that
was being destroyed. The drain that already existed in ~DeviceLostHelper is
now a Shutdown() method that Close() calls, and it was dead code before
because Close() had already cleared the handle it checks.
Not built: no UWP/.NET Native build environment here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Don't recreate the Direct3D device from the device-lost callback
PlaceholderImageHelper watches its D3D device with RegisterDeviceRemovedEvent
and recreated the device straight from the threadpool wait callback. That runs
while the display driver is still resetting, and D3D11CreateDevice at that
moment faults inside the vendor user-mode driver (reported by crash telemetry
on 12.9, on two different Intel UMDs).
The device is now rebuilt lazily instead: PlaceholderHelper already calls
HandleDeviceLost() on every access to the singleton, so the next use recreates
it from the UI thread. HandleDeviceLost tolerates a null device, which is the
state left behind when creation failed.
Two related lifetime fixes in the same path:
- CreateDeviceResources released nothing before calling D3D11CreateDevice.
com_ptr::put() overwrites without releasing, so recreating over the removed
device stranded it and everything the driver held behind it.
- PlaceholderImageHelper::Close() called StopWatchingCurrentDevice(), which
closes the threadpool wait but does not wait for a callback that is already
running -- the callback then reached RaiseDeviceLostEvent on an object that
was being destroyed. The drain that already existed in ~DeviceLostHelper is
now a Shutdown() method that Close() calls, and it was dead code before
because Close() had already cleared the handle it checks.
Not built: no UWP/.NET Native build environment here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/instant-empty-table • 3b63279 • 1 files, +7/-0
Handle an instant view table with no rows
ProcessTable computed its column count with table.Cells.Max(...), which throws
InvalidOperationException: Sequence contains no elements when the table has no
rows at all. It reached OnApplyTemplate through InstantContent.UpdateView, so
the whole message failed to render. Reported by crash telemetry on 12.9.1.
There is no grid to build for an empty table, so render just the caption --
which is what the method already falls back to, and callers of ProcessBlock all
handle a null return.
Not built: no UWP/.NET Native build environment here. Verified the file still
parses with Roslyn.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Handle an instant view table with no rows
ProcessTable computed its column count with table.Cells.Max(...), which throws
InvalidOperationException: Sequence contains no elements when the table has no
rows at all. It reached OnApplyTemplate through InstantContent.UpdateView, so
the whole message failed to render. Reported by crash telemetry on 12.9.1.
There is no grid to build for an empty table, so render just the caption --
which is what the method already falls back to, and callers of ProcessBlock all
handle a null return.
Not built: no UWP/.NET Native build environment here. Verified the file still
parses with Roslyn.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/swapchain-diagnostics • daaee85 • 2 files, +50/-5
Log why the video swap chain failed, and don't crash when it does
AsyncMediaPlayerSwapChain::Create() swallowed every failure and returned false,
so the only trace left was SwapChainOptions() throwing "You must wait for the
VideoView to be loaded before calling GetSwapChainOptions()" -- a message
inherited from LibVLCSharp that describes a lifecycle mistake this code cannot
make. The real cause is that one of the Direct3D calls failed, and nothing
recorded which one. That throw crossed a XAML Loaded handler (gallery video,
stories, the music player all construct AsyncMediaPlayer there) and became an
unhandled exception, reported by crash telemetry on 12.8.1 and 12.9.
Create() now names each stage as it goes and logs the stage, the HRESULT and
the message, so the next report says which driver call failed. The thrown
message says what actually happened.
The player no longer fails construction over it: the Direct3D device can
legitimately be unavailable while the display driver is resetting, and there is
nothing the caller could do about it. When the swap chain is not loaded the
player is built with --no-video instead, so audio still plays and the failure
is in the log rather than in a crash.
Also releases m_d3d11Device/m_deviceContext between adapter attempts --
com_ptr::put() overwrites without releasing, so a failed attempt could strand a
device.
Not built: no UWP/.NET Native build environment here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Log why the video swap chain failed, and don't crash when it does
AsyncMediaPlayerSwapChain::Create() swallowed every failure and returned false,
so the only trace left was SwapChainOptions() throwing "You must wait for the
VideoView to be loaded before calling GetSwapChainOptions()" -- a message
inherited from LibVLCSharp that describes a lifecycle mistake this code cannot
make. The real cause is that one of the Direct3D calls failed, and nothing
recorded which one. That throw crossed a XAML Loaded handler (gallery video,
stories, the music player all construct AsyncMediaPlayer there) and became an
unhandled exception, reported by crash telemetry on 12.8.1 and 12.9.
Create() now names each stage as it goes and logs the stage, the HRESULT and
the message, so the next report says which driver call failed. The thrown
message says what actually happened.
The player no longer fails construction over it: the Direct3D device can
legitimately be unavailable while the display driver is resetting, and there is
nothing the caller could do about it. When the swap chain is not loaded the
player is built with --no-video instead, so audio still plays and the failure
is in the log rather than in a crash.
Also releases m_d3d11Device/m_deviceContext between adapter attempts --
com_ptr::put() overwrites without releasing, so a failed attempt could strand a
device.
Not built: no UWP/.NET Native build environment here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/settingsbutton-disabled • 0b6ba98 • 1 files, +4/-5
Don't animate a deferred template part from SettingsButton's Disabled state
SettingsButton's Disabled visual state animated the Foreground of three template
parts, one of which -- DescriptionPresenter -- is x:Load="False" and is only
realized by OnApplyTemplate when Description is non-empty. A Storyboard whose
TargetName cannot be resolved fails with E_INVALIDARG, and because the initial
visual state is applied while the template is being applied, that is inside the
control's measure. The ArgumentException therefore surfaces from the measure of
whatever is above it, which is why the reported stacks anchor on
HeaderedControlPanel and SettingsPanel and name nothing closer to the cause.
It needs the button to be disabled before its template is applied, which is what
happens when the disabled state is inherited: on Settings > Power saving the
HeaderedControl carries IsEnabled, so its SettingsExpanders -- whose ActionButton
has no Description -- are born disabled. Settings > Data and storage has the same
disabled expanders but sets Description, so the part is realized and the target
resolves. Reported by crash telemetry on every version from 12.4.1 to 12.9.1.
Dropping the key frame is enough: DescriptionPresenter is already painted with
SystemControlDisabledChromeDisabledLowBrush, so the disabled appearance is
effectively unchanged.
Checked the rest of the app for the same shape: this is the only
Storyboard.TargetName in any template that points at an x:Load="False" element.
The VisualState.Setters that target deferred parts (RecentChoosers in the message
reaction templates) are left alone -- those resolve differently and do not throw,
as reactions demonstrate every day.
Not built: no UWP/.NET Native build environment here. The file was checked for XML
well-formedness.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Don't animate a deferred template part from SettingsButton's Disabled state
SettingsButton's Disabled visual state animated the Foreground of three template
parts, one of which -- DescriptionPresenter -- is x:Load="False" and is only
realized by OnApplyTemplate when Description is non-empty. A Storyboard whose
TargetName cannot be resolved fails with E_INVALIDARG, and because the initial
visual state is applied while the template is being applied, that is inside the
control's measure. The ArgumentException therefore surfaces from the measure of
whatever is above it, which is why the reported stacks anchor on
HeaderedControlPanel and SettingsPanel and name nothing closer to the cause.
It needs the button to be disabled before its template is applied, which is what
happens when the disabled state is inherited: on Settings > Power saving the
HeaderedControl carries IsEnabled, so its SettingsExpanders -- whose ActionButton
has no Description -- are born disabled. Settings > Data and storage has the same
disabled expanders but sets Description, so the part is realized and the target
resolves. Reported by crash telemetry on every version from 12.4.1 to 12.9.1.
Dropping the key frame is enough: DescriptionPresenter is already painted with
SystemControlDisabledChromeDisabledLowBrush, so the disabled appearance is
effectively unchanged.
Checked the rest of the app for the same shape: this is the only
Storyboard.TargetName in any template that points at an x:Load="False" element.
The VisualState.Setters that target deferred parts (RecentChoosers in the message
reaction templates) are left alone -- those resolve differently and do not throw,
as reactions demonstrate every day.
Not built: no UWP/.NET Native build environment here. The file was checked for XML
well-formedness.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/settingsbutton-disabled • b852e7b • 1 files, +10/-5
Keep the disabled description dimming, realize the part instead
Dropping the key frame left the description on its own brush while the title
dimmed, so on pages that do set Description the two no longer matched. Restore
the key frame and take x:Load off DescriptionPresenter instead, so the target
always resolves.
The disabled appearance is now exactly what it was before this branch; the cost
is one collapsed, unmeasured ContentPresenter per SettingsButton. The other two
optional parts keep their deferral -- nothing animates them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/settingsbutton-disabled • 4b57151 • 1 files, +6/-4
Defer DescriptionPresenter with Lazy instead of dropping the deferral
x:DeferLoadStrategy="Lazy" realizes the element when something references it,
including a visual state, so the Disabled storyboard resolves without the part
being materialized up front. x:Load does not, which is what made the target
unresolvable. The stock TextBox template deferrs HeaderContentPresenter the same
way for the same reason.
Keeps the deferral the previous commit had given up, and the disabled appearance
is still what it was before this branch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Keep the disabled description dimming, realize the part instead
Dropping the key frame left the description on its own brush while the title
dimmed, so on pages that do set Description the two no longer matched. Restore
the key frame and take x:Load off DescriptionPresenter instead, so the target
always resolves.
The disabled appearance is now exactly what it was before this branch; the cost
is one collapsed, unmeasured ContentPresenter per SettingsButton. The other two
optional parts keep their deferral -- nothing animates them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/settingsbutton-disabled • 4b57151 • 1 files, +6/-4
Defer DescriptionPresenter with Lazy instead of dropping the deferral
x:DeferLoadStrategy="Lazy" realizes the element when something references it,
including a visual state, so the Disabled storyboard resolves without the part
being materialized up front. x:Load does not, which is what made the target
unresolvable. The stock TextBox template deferrs HeaderContentPresenter the same
way for the same reason.
Keeps the deferral the previous commit had given up, and the disabled appearance
is still what it was before this branch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram