Thread PresentationStrings through RichText/InstantPageBlock/InstantPage
previewText(), replacing the hardcoded //TODO:localize placeholders
("Photo", "Fx", "Table", "Map", ...) with localized keys and reusing the
existing Message.Photo/Video/Location strings. Add RichTextPreview.Formula
("[formula]"), RichTextPreview.Table ("[table]"), and RichTextPreview.Music
("Music").
The .audio block previously rendered the wrong label (Message.Audio is
"Voice Message"). Thread InstantPage.media down so the block can resolve
media[id] as TelegramMediaFile and split: isVoice -> "Voice Message",
otherwise -> "Music", mirroring MessageContentKind's voice/music handling.
Update both previewText() call sites (MessageContentKind, ChatListItemStrings)
to pass strings, and complete the InstantPageListItem migration that was
left on the old signature.
Also remove the dead streaming-status ("Thinking...") rendering block from
ChatMessageTextBubbleContentNode (guarded by an always-false `!"".isEmpty`)
along with the now-orphaned streamingTextFrame layout machinery it fed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Server-sent rich messages can arrive partial (RichMessage isPartial ->
instantPage.isComplete == false). The bubble renders the partial page with
an inline "Show more" link; tapping it fetches the full page once and
expands in place.
- RichTextMessageAttribute keeps the partial instantPage and gains an
optional fullInstantPage, filled by engine.messages.requestFullRichText
via transaction.updateMessage. The seed-config merge preserves a fetched
fullInstantPage across later server updates.
- ChatMessageRichDataBubbleContentNode: node-local, per-message expand
state (collapsed on every fresh display, even when fullInstantPage is
already cached); renders (expanded ? fullInstantPage : nil) ?? instantPage;
gates the link on !expanded && !isComplete (+ not streaming, Cloud-only,
not preview/messageOptions); expand state threaded through both layout
caches; shimmer while fetching (instant when cached); bubble grows
downward on expand via setInvertOffsetDirection.
- New localized string Chat.RichText.ShowMore; docs in
docs/instantpage-richtext.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Re-applies commit 1120b9084e (reverted in ef2c1d47cc during install
debugging). The install failure was proven to be the oversized watch
binary (fixed by DEAD_CODE_STRIPPING), not the bundle-id mechanism — which
was verified to produce a byte-identical artifact for ph.telegra.Telegraph.
xcodebuild bakes PRODUCT_BUNDLE_IDENTIFIER=<host>.watchkitapp and the watch
Info.plist derives WKCompanionAppBundleIdentifier via
$(PRODUCT_BUNDLE_IDENTIFIER:base), so the embedded watch app is correct for
any host (ph.telegra.Telegraph and org.telegram.TelegramInternal/enterprise)
with no post-build plist patching.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Revert the universal/dynamic watch bundle id change (commit 1120b9084e)
back to the static 0682 shape, for install testing.
This reverts the PRODUCT_BUNDLE_IDENTIFIER override + $(...:base) companion-id
derivation; the watch app again bakes the hardcoded ph.telegra.Telegraph.watchkitapp
and literal WKCompanionAppBundleIdentifier=ph.telegra.Telegraph.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Vendor the standalone tgwatch watch-app sources into Telegram/WatchApp/ as a
tracked snapshot and build it from there by default (tracked Bazel inputs).
Make.py drives embedding via --embedWatchApp (--watchAppSourcePath removed);
the filegroup excludes .swiftpm/.build. Documented in CLAUDE.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add the apple_prebuilt_watchos_application rule (prebuilt_watchos.bzl + its
worker) that runs xcodebuild on the watch app, codesigns the .app and nested
framework, and feeds it to the Telegram ios_application's watch_application
slot, gated on //Telegram:embedWatchApp. Make.py gains device-gated watch
embed flags (--watchApiId/--watchApiHash/--watchSigningIdentity/
--watchProvisioningProfile), and the embedded app's version is matched to the
host (versions.json + buildNumber).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>