Skip to content

feat(settings): let everyone pick today's penguin icon - #284

Open
iebb wants to merge 3 commits into
masterfrom
feat/daily-free-penguin-icon
Open

iebb wants to merge 3 commits into
masterfrom
feat/daily-free-penguin-icon

Conversation

@iebb

@iebb iebb commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

Change

Without Supporter, today's penguin can now be chosen as the app icon. The nine standard icons in the picker show their real Home Screen backgrounds.

  • Today's penguin is floor(unixSeconds / 86400) mod 48 over the fixed catalogue order (01-pilot ... 48-stargazing), by UTC day. AppIconVariant.dailyPenguinOrder (Dart), dailyPenguinOrder (AppDelegate.swift) and paidIcons (MainActivity.kt) share that order. Native code computes the day itself when it applies the icon and never trusts Dart for it.
  • The icon never changes automatically: a chosen icon stays until the user changes it, and each new day only makes that day's penguin selectable. Supporters can still choose any of the 48.
  • Picker: a "Free today" card at the top of Penguin icons shows when the next penguin arrives, today's tile has no lock and carries a "Free today" tag, and a note at the end explains daily access.
  • The standard icons draw from 14 small WebP previews (light and dark variants, about 44 KB in total) rather than the full-size icons. tool/optimize_app_artwork.py --check passes.
  • Copy: the Support us card, benefit and access lines now describe the free daily penguin and appearance-only Supporter extras. The icon names that were still English in de, es, fr, ja and ko are translated; existing approved translations are unchanged. test/supporter_icons_test.dart is updated for the renamed keys.

Affected platforms: iOS and Android (alternate app icons); picker UI on mobile.

Related PRs and canonical PR (or None; existing PRs checked): this is the canonical PR for this change. Open and recently closed PRs were checked; none covers it. It ships alongside #281, #282 and #283, which are independent fixes from the same request. Each touches different code and is useful alone.

Telegram API Terms classification

Follow the contribution and agentic review rules. Select exactly one classification and explain it below.

This is an agentic source-code review classification. Every store version must remain compliant with applicable Telegram API Terms; the guarded-source exception does not apply to store distribution or review.

  • Compliant/unchanged: this change complies with applicable Telegram API Terms, preserves Telegram's basic functionality, and does not introduce behavior covered by section 1.4.
  • Section 1.4 exception: the entire feature is guarded by allowTelegramTosViolations (I_DONT_FUCKING_CARE_ABOUT_TOS), disabled in official builds, and available only in an explicitly enabled self-build.
  • Disallowed or unresolved: this PR must not be approved or merged until its classification and implementation are resolved.

Reason and affected Telegram behavior:
App icon selection and local cosmetic entitlements only; no Telegram API usage or behavior changes.

Basic checks (every PR)

Check each item after verification. If a check is inapplicable, explain why in the evidence below before checking it.

  • I reviewed the diff for correctness, unintended behavior, secrets, and unrelated changes.
  • Formatting passed, or I recorded why it is inapplicable: dart format --output=none --set-exit-if-changed lib test integration_test test_driver.
  • Analysis passed, or I recorded why it is inapplicable: flutter analyze.
  • Relevant tests passed, or I recorded why they are inapplicable; name the commands and test cases below. Quality CI runs flutter test --concurrency=4.
  • I checked affected build/platform paths and applicable localization/generated files; list the evidence below.
  • I completed the Terms classification above and verified that official builds cannot enable section 1.4 exceptions.

Commands, results, platform/build/UI evidence, and reasons for inapplicable checks:

  • dart format --output=none --set-exit-if-changed lib test integration_test test_driver with the Dart 3.12.2 SDK from Flutter 3.44.2: 0 changed.
  • dart analyze --fatal-infos (the same analysis server and rules flutter analyze uses, Flutter 3.44.2 SDK): No issues found on this branch alone and on all four PRs combined.
  • flutter test could not run in the authoring sandbox (Alpine/musl host), so the tests box stays unchecked until Quality CI reports on this PR head. No new tests were added, at the maintainer's request.
  • python3 translations/tools/check.py (8 locales x 4059 keys OK), python3 translations/tools/gen_assets.py --check (up to date), python3 tool/check_l10n_strings.py, python3 tool/gen_registration_anchors.py --check, python3 tool/optimize_app_artwork.py --check: all pass.
  • iPhone Duo simulator, light and dark, in English, German, French, Spanish, Japanese and Korean, including a larger interface size: standard backgrounds, the Free today card, the next-penguin time, the tag, the end note and the Support us copy all show correctly, and long strings wrap without truncation. The designer and copywriter approved them.
  • The Android MainActivity.kt change was not compiled locally; Android build paths rely on CI.
  • Built together with fix(ios): start opened iPhone Duo panes beside the status cluster #281, fix(chat): make the formatting panel readable with a bold A button #282 and fix(chat): fill the whole tab with the sticker panel switcher highlight #283 in one unsigned iOS Simulator archive (Xcode 27, iOS 27 SDK, I_DONT_FUCKING_CARE_ABOUT_TOS=false). Together the four branches match the tested source, apart from two lint-only edits in fix(ios): start opened iPhone Duo panes beside the status cluster #281.

Section 1.4 exception checks

Not applicable: compliant change, no guarded behavior.

  • All UI, routes, commands, callbacks, background work, TDLib/native effects, and registrations use the compile-time guard; a runtime preference alone cannot enable the feature.
  • With the flag omitted, the feature is absent and ordinary Telegram behavior remains intact, including with old saved settings.
  • With --dart-define=I_DONT_FUCKING_CARE_ABOUT_TOS=false, the feature is absent and ordinary Telegram behavior remains intact.
  • With --dart-define=I_DONT_FUCKING_CARE_ABOUT_TOS=true, the feature works through the guarded entry points in a self-build; I included relevant test and build/UI evidence.
  • Official release, nightly, TestFlight, and store builds keep the flag false and have no option that enables it. All store versions remain compliant with applicable Telegram API Terms.

Guard locations and evidence for each flag variant: Not applicable.

Agentic reviewer confirmation

Agentic code reviewer only: record this confirmation in the PR review or check these boxes before approval.

  • I independently confirmed the Terms classification and inspected all required guards and evidence.
  • Applicable checks and Quality CI pass; no unresolved classification or unguarded section 1.4 behavior remains.

Without Supporter, today's penguin can now be chosen as the app icon:
floor(unix seconds / 86400) mod 48 over the fixed catalogue order, by UTC
day. iOS and Android compute the day themselves when applying the icon and
never trust Dart for it. The icon never changes automatically: a chosen
icon stays until the user changes it, and only the new day's penguin
becomes selectable. Supporters can still choose any of the 48.

The picker shows today's penguin in its own card with when the next one
arrives, tags its grid tile "Free today", and explains daily access at the
end of the list. The nine standard icons now show their real Home Screen
backgrounds, from small WebP previews (about 44 KB in total) rather than
the full-size icons.

Copy: the Support us card, benefit and access lines now describe the free
daily penguin and appearance-only Supporter extras, and the icon names that
were still English in de, es, fr, ja and ko are translated. Existing
approved translations are kept.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T20:16:12.380815Z 7af6994 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Automated PR review

Head: 6b162bcf009981ed2991bec3532e3aa373d5a3eb · master: 73c1d46bb5a726e68c9bef160ccaf72a27725ef1
LLM recommendation: needs_human. This is not a GitHub approval.
Auto-merge: blocked. Only iebb can authorize the current head.

Review unavailable: Incomplete model answer (length); manual review required.

Remaining gates

  • Waiting for iebb to approve this exact current head.
  • Complete the basic check: Relevant tests passed.
  • Complete the basic check: I checked affected build/platform paths.
  • Resolve all review threads before auto-merge.
  • All current head checks must complete successfully (no skipped/neutral substitutes).
  • master source incomplete: android/app/src/main/kotlin/ad/neko/mithka/MainActivity.kt
  • head source incomplete: android/app/src/main/kotlin/ad/neko/mithka/MainActivity.kt
  • Missing/oversized patch: assets/app_icons/picker/standard-aurora.webp
  • head source incomplete: assets/app_icons/picker/standard-aurora.webp
  • Missing/oversized patch: assets/app_icons/picker/standard-blue.webp
  • head source incomplete: assets/app_icons/picker/standard-blue.webp
  • Missing/oversized patch: assets/app_icons/picker/standard-default.webp
  • head source incomplete: assets/app_icons/picker/standard-default.webp
  • Missing/oversized patch: assets/app_icons/picker/standard-letter_m_gradient.webp
  • head source incomplete: assets/app_icons/picker/standard-letter_m_gradient.webp
  • Missing/oversized patch: assets/app_icons/picker/standard-letter_m_gradient_dark.webp
  • head source incomplete: assets/app_icons/picker/standard-letter_m_gradient_dark.webp
  • Missing/oversized patch: assets/app_icons/picker/standard-letter_m_neutral.webp
  • head source incomplete: assets/app_icons/picker/standard-letter_m_neutral.webp
  • Missing/oversized patch: assets/app_icons/picker/standard-letter_m_neutral_dark.webp
  • head source incomplete: assets/app_icons/picker/standard-letter_m_neutral_dark.webp
  • Missing/oversized patch: assets/app_icons/picker/standard-pixel.webp
  • head source incomplete: assets/app_icons/picker/standard-pixel.webp
  • Missing/oversized patch: assets/app_icons/picker/standard-pixel_gradient.webp
  • head source incomplete: assets/app_icons/picker/standard-pixel_gradient.webp
  • Missing/oversized patch: assets/app_icons/picker/standard-pixel_gradient_dark.webp
  • head source incomplete: assets/app_icons/picker/standard-pixel_gradient_dark.webp
  • Missing/oversized patch: assets/app_icons/picker/standard-pixel_neutral.webp
  • head source incomplete: assets/app_icons/picker/standard-pixel_neutral.webp
  • Missing/oversized patch: assets/app_icons/picker/standard-pixel_neutral_dark.webp
  • head source incomplete: assets/app_icons/picker/standard-pixel_neutral_dark.webp
  • Missing/oversized patch: assets/app_icons/picker/standard-white.webp
  • head source incomplete: assets/app_icons/picker/standard-white.webp
  • Missing/oversized patch: assets/app_icons/picker/standard-white_dark.webp
  • head source incomplete: assets/app_icons/picker/standard-white_dark.webp
  • master source incomplete: ios/Runner/AppDelegate.swift
  • head source incomplete: ios/Runner/AppDelegate.swift
  • master source incomplete: lib/l10n/app_localizations.dart
  • head source incomplete: lib/l10n/app_localizations.dart
  • master source incomplete: lib/settings/app_icon_controller.dart
  • head source incomplete: lib/settings/app_icon_controller.dart
  • master source incomplete: lib/settings/appearance_view.dart
  • head source incomplete: lib/settings/appearance_view.dart
  • master source incomplete: test/supporter_icons_test.dart
  • head source incomplete: test/supporter_icons_test.dart
  • master source incomplete: translations/strings/de-DE/strings.xml
  • head source incomplete: translations/strings/de-DE/strings.xml
  • master source incomplete: translations/strings/en-US/strings.xml
  • head source incomplete: translations/strings/en-US/strings.xml
  • master source incomplete: translations/strings/es-ES/strings.xml
  • head source incomplete: translations/strings/es-ES/strings.xml
  • master source incomplete: translations/strings/fr-FR/strings.xml
  • head source incomplete: translations/strings/fr-FR/strings.xml
  • Context budget reached; remaining source omitted.

No PR code runs with the model key or write token. Free-only routing; provider/rate-limit failures never authorize a merge. Existing review findings must be resolved, not hidden by a new recommendation.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7af69948aa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/settings/app_icon_controller.dart Outdated
Comment on lines +647 to +649
final source = brightness == Brightness.dark ? darkAsset ?? asset : asset;
final name = source.split('/').last.replaceAll('.png', '');
return 'assets/app_icons/picker/standard-$name.webp';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Update the existing preview-asset assertions for the new paths

The existing test the nine visible free choices are available on every icon platform in test/supporter_icons_test.dart:123–134 still expects previewAsset to return launcher PNG paths for pilot and blue. This implementation instead returns standard-*.webp paths, so those assertions will fail. Update the standard-icon expectations and either retain nonstandard-icon behavior or revise those assertions to match the intended API. The Quality workflow (.github/workflows/quality.yml:145) runs the full test suite, making this a required-check failure rather than just stale test documentation. Flutter is unavailable locally, so this mismatch was verified by inspecting the implementation and assertions.

AGENTS.md reference: AGENTS.md:L19-L20

Useful? React with 👍 / 👎.

headerChromePaddingOf comes from the Duo top padding change (#281); this PR
keeps the existing header inset so it builds on its own.
iebb added a commit that referenced this pull request Oct 29, 2026
The interface size screen's header uses the same corner-cluster padding as
the other headers. It was missing from this PR and landed in #284 instead.
…s in tests

previewAsset returned a standard-picker path for every icon, including the
48 penguins that have none. It again returns the launcher artwork, and the
standard tiles use the new standardPickerAsset.

The supporter icon tests tapped penguins that now sit below the standard
icons and today's free penguin, outside what the 800x600 test list builds.
They scroll a tile into view before tapping or inspecting it.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant