Skip to content

Allow setting of favourite serp easter egg logo#7467

Merged
mikescamell merged 26 commits intodevelopfrom
feature/mike/allow-setting-of-favourite-serp-easter-egg-logo
Feb 6, 2026
Merged

Allow setting of favourite serp easter egg logo#7467
mikescamell merged 26 commits intodevelopfrom
feature/mike/allow-setting-of-favourite-serp-easter-egg-logo

Conversation

@mikescamell
Copy link
Contributor

@mikescamell mikescamell commented Jan 12, 2026

Task/Issue URL: https://app.asana.com/1/137249556945/project/1207908166761516/task/1212553599261552?focus=true

Description

This PR adds the ability for users to set a SERP Easter Egg logo as their favourite, which will then display on all DuckDuckGo search result pages.

Features implemented:

  1. Feature toggle - Added remote feature toggle (serpEasterEggLogos.setFavourite) to control the favourite logo functionality
  2. Favourite logo data store - Added FavouriteSerpLogoDataStore to persist the user's favourite logo URL preference
  3. Discovery screen enhancements - Updated the Easter Egg logo screen to:
    - Display a title telling the user they've discovered an Easter Egg logo
    - Added a button to set/unset the logo as favourite
  4. Wiggle animation - Easter Egg logos now play a subtle wiggle animation when displayed in the omnibar:
    - Only plays once per unique logo URL
    - Skips animation for favourite logos
    - Waits for image crossfade to complete before animating
  5. State management fixes - Fixed race conditions that could cause Easter Egg logos to be lost:
    - Preserve existing Easter Egg when onViewModeChanged is called
    - Distinguish between SerpLogo.Normal (explicitly no Easter Egg) and null (pending state)
    - Reset to Dax logo when favourite preference changes

Steps to test this PR

Easter Egg Discovery

  • Search for a term that triggers an Easter Egg logo (e.g., "Southampton FC", "f1", "predator")
  • Verify the Easter Egg logo appears in the omnibar with a wiggle animation
  • Tap on the logo to open the enlarged view
  • Verify the discovery text "You found a hidden logo!" is displayed at the top
  • Verify the "Switch to This Logo" button is visible
  • Tap anywhere outside to dismiss the view
  • Search for another easter egg e.g. "hydra" and anticipate the wiggle animation
  • As the wiggle animation plays click the logo
  • The easter egg logo enlarged view should open and the logo should transition with no glitches
  • Tap anywhere outside to dismiss the view
  • The easter egg logo enlarged view should should transition back to the address with no glitches

Setting a Favourite Logo

  • Tap on the logo to open the enlarged view
  • Open the Easter Egg logo screen
  • Tap "Switch to This Logo"
  • Tap anywhere outside to dismiss the view
  • Search for something that does not have an easter egg logo e.g. "fpl"
  • Verify the favourite logo persists in the omnibar
  • Search for a term that triggers an Easter Egg logo (e.g., "Southampton FC", "f1", "predator") but is not your current favourite logo
  • Verify the favourite logo persists in the omnibar
  • Search for the same term for your favourite logo
  • Verify the logo no longer plays the wiggle animation (since it's now a favourite)

Resetting to Default

  • With a favourite logo set, tap on the logo
  • Verify the button now shows "Reset to Default"
  • Tap "Reset Search Logo"
  • Dismiss the easter egg logo screen
  • Verify the DDG logo is visible
  • Search for something that does not have an easter egg logo e.g. "fpl"
  • Verify the DDG logo is still visible
  • Search for a term that triggers an Easter Egg logo (e.g., "Southampton FC", "f1", "predator")
  • Verify the Easter Egg logo appears in the omnibar with a wiggle animation

Flag off

Index: serp-logos/serp-logos-api/src/main/kotlin/com/duckduckgo/serp/logos/api/SerpEasterEggLogosToggles.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/serp-logos/serp-logos-api/src/main/kotlin/com/duckduckgo/serp/logos/api/SerpEasterEggLogosToggles.kt b/serp-logos/serp-logos-api/src/main/kotlin/com/duckduckgo/serp/logos/api/SerpEasterEggLogosToggles.kt
--- a/serp-logos/serp-logos-api/src/main/kotlin/com/duckduckgo/serp/logos/api/SerpEasterEggLogosToggles.kt	(revision 2a9b098666ba9cd103cbe20e24fa7ff018cf7168)
+++ b/serp-logos/serp-logos-api/src/main/kotlin/com/duckduckgo/serp/logos/api/SerpEasterEggLogosToggles.kt	(date 1769694204288)
@@ -24,6 +24,6 @@
   @Toggle.DefaultValue(DefaultFeatureValue.FALSE)
   fun self(): Toggle

-    @Toggle.DefaultValue(DefaultFeatureValue.TRUE)
+    @Toggle.DefaultValue(DefaultFeatureValue.FALSE)
   fun setFavourite(): Toggle
}

  • Set the logo you just searched for as a favourite
  • Apply the patch above
  • Install the app
  • You should see the logo you set as favourite if you did not navigate away before re-installation
  • Search for a term that triggers an Easter Egg logo (e.g., "Southampton FC", "f1", "predator")
  • The new easter egg logo should show and should not be your favourite (flag off means no favourite so user's don't get stuck with it if we need to turn off the flag)
  • Tap the EasterEgg logo
  • The Easter Egg Logo screen should open
  • Verify the discovery text "You found a hidden logo!" is displayed
  • Verify the "Switch to This Logo" button is not visible
  • Dismiss the easter egg logo screen
  • Search for something that does not have an easter egg logo e.g. "fpl"
  • The DDG logo should show
  • Navigate to a website e.g. ign.com
  • The green shield should be visible
  • General smoke test, do searches in SERP with easter eggs, do non easter egg searches, navigate away to websites.
  • Favourite logo set before turning the flag off does not show

UI changes

Before After
before-light-mode after-light-mode
before-dark-mode after-dark-mode

Note

Medium Risk
Touches omnibar state/animation handling and adds new persistence + feature-flagged behaviour, so regressions could affect address bar logo rendering across navigation and page loads. Scope is contained to SERP-logo UI/state and is well covered by new/updated tests.

Overview
Adds a feature-flagged “favourite” SERP Easter Egg logo that, when set, overrides normal SERP logo extraction and is shown on all DuckDuckGo search result pages.

Introduces persistence via a new FavouriteSerpLogoDataStore (DataStore-backed) and wires it through SerpLogos/BrowserTabViewModel so favourite changes immediately update the omnibar and avoid issuing ExtractSerpLogo when a favourite is active.

Updates the discovery UI to let users set/unset a favourite (new SerpEasterEggLogoViewModel, layout + strings) and refines omnibar rendering/animations: adds a one-time wiggle animation for non-favourite Easter Egg logos, plus explicit cancellation of that animation during logo transitions to prevent glitches. Tests are expanded across app + serp-logos modules, and SerpLogo.EasterEgg now carries an isFavourite flag to distinguish behaviour.

Written by Cursor Bugbot for commit e3e334d. This will update automatically on new commits. Configure here.

@mikescamell mikescamell changed the title Feature/mike/allow setting of favourite serp easter egg logo Allow setting of favourite serp easter egg logo Jan 12, 2026
@mikescamell mikescamell force-pushed the feature/mike/allow-setting-of-favourite-serp-easter-egg-logo branch 2 times, most recently from 19e36d2 to 3b8dd59 Compare January 13, 2026 17:48
@mikescamell mikescamell force-pushed the feature/mike/allow-setting-of-favourite-serp-easter-egg-logo branch 6 times, most recently from 7362335 to b763bc4 Compare February 2, 2026 09:02
@mikescamell mikescamell marked this pull request as ready for review February 2, 2026 09:46
@anikiki anikiki self-requested a review February 2, 2026 10:19
@mikescamell mikescamell force-pushed the feature/mike/allow-setting-of-favourite-serp-easter-egg-logo branch from b763bc4 to bff3474 Compare February 2, 2026 15:32
@mikescamell mikescamell force-pushed the feature/mike/allow-setting-of-favourite-serp-easter-egg-logo branch from bff3474 to cf15f2f Compare February 2, 2026 16:04
@mikescamell mikescamell force-pushed the feature/mike/allow-setting-of-favourite-serp-easter-egg-logo branch from cae2868 to 20a6686 Compare February 2, 2026 16:31
@mikescamell mikescamell force-pushed the feature/mike/allow-setting-of-favourite-serp-easter-egg-logo branch from 500da4f to b0e3498 Compare February 4, 2026 12:10
Copy link
Contributor

@anikiki anikiki left a comment

Choose a reason for hiding this comment

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

Looks good and works as expected! 🎉

There's a failing test (missing param).

@mikescamell mikescamell force-pushed the feature/mike/allow-setting-of-favourite-serp-easter-egg-logo branch 3 times, most recently from 5b93a05 to 64f8fcd Compare February 4, 2026 20:29
We now include a title to tell the user they have discovered an easter egg logo and allow them to set the logo as their favourite via the newly added button on screen.
When an Easter Egg logo is displayed in the omnibar (from a special
search like "f1"), it now plays a subtle wiggle animation once
the image loads. The animation:

- Only plays once per unique logo URL (prevents re-triggering on
  ViewState updates)
- Skips animation for favourite logos (set by user preference)

We also need to make sure we cancel the wobble animation before the shared element transition otherwise it becomes glitchy.
When a SERP page loads with an Easter Egg logo, two events occur
in non-deterministic order: onViewModeChanged and
onExternalOmnibarStateChanged. If onViewModeChanged was called
after the Easter Egg was already set, it would overwrite the
leadingIconState with Dax because it always passed logoUrl = null.

This fix preserves the existing Easter Egg URL in onViewModeChanged
by extracting it from the current state before updating, following
the same pattern already used in onExternalLoadingStateChanged.
When navigating between searches, onExternalOmnibarStateChanged may be
called with serpLogo = null before the new logo extraction completes.
Previously this would clear the existing Easter Egg logo, causing Dax
to briefly flash or persist if timing was unfavorable.

Now we distinguish between:
- SerpLogo.Normal: Explicitly no Easter Egg, clear and show Dax
- null: Unknown/pending state, preserve existing Easter Egg if present

This prevents the logo from being lost during navigation between
Easter Egg searches.
No need to extract an existing logo if we have a favourite set. Also prevents getting into any weird state.
…te button clicked

Matches iOS and request from Ship Review
… on SERP page in BrowserTabViewModel

Moves the logic to ensure we set a Favourite EasterEgg logo to the BrowserTabViewModel. This makes the OmnibarViewModel dumber like before adding the favourite feature, leaving responsibility to the caller (BrowserTab) to decide what should be shown.

Add flow observer that combines favourite toggle state and favourite URL
to reactively update the serpLogo when:
- Favourite logo is set: Show the favourite Easter egg logo
- Favourite logo is cleared: Show Dax (Normal logo)
- Feature is disabled: Let normal extraction handle it

Only reacts when on a SERP page to avoid unnecessary updates on other pages.
When a favourite SERP logo is cleared while on a SERP page, the
omnibarViewState.serpLogo should update from EasterEgg to Normal,
but currently it stays stale.

This test verifies the expected behavior and will pass once a flow
observer is added to BrowserTabViewModel to react to favourite logo
changes
The icon will now shrink depending on how much room we have between the text and the button. On all but the tiniest of phones this will be fine.

I reduced margins to give the logo the best chance of being as big as possible without overlapping.
No longer needed as we moved the logic to the BrowserViewModel
@mikescamell mikescamell force-pushed the feature/mike/allow-setting-of-favourite-serp-easter-egg-logo branch from 64f8fcd to 918b1f5 Compare February 4, 2026 20:50
Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

mikescamell and others added 2 commits February 5, 2026 09:22
Task/Issue URL:
https://app.asana.com/1/137249556945/project/1207908166761516/task/1212768545044373?focus=true

### Description

Translations for setting a favourite easter egg logo

### Steps to test this PR

Enable setFavourite toggle

_Translations_
- [x] Switch languages
- [x] Search for an easter egg logo e.g. "predator"
- [x] Check strings on the SERP easter egg logo screen have changed

### UI changes
N/A

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Resource-only localization changes with no functional logic
modifications; risk is limited to missing/incorrect string resources
causing UI text regressions.
> 
> **Overview**
> Adds new localized strings used by the SERP easter egg logo screen for
**setting a favourite logo**, **resetting to default**, and the
discovery message (`serpLogoSetAsFavourite`, `serpLogoResetToDefault`,
`serpLogoDiscoveryText`) across multiple
`values-xx/strings-serp-logos.xml` files, plus the base
`values/strings-serp-logos.xml`.
> 
> Removes the `values/donottranslate.xml` resource that previously held
the English versions of these strings, and updates several locale
resource headers (copyright year).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
d6264dd. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@mikescamell mikescamell merged commit ddd02a3 into develop Feb 6, 2026
12 of 13 checks passed
@mikescamell mikescamell deleted the feature/mike/allow-setting-of-favourite-serp-easter-egg-logo branch February 6, 2026 16:57
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.

2 participants