Skip to content

feat(logo): add bitcoin icon to Easter egg, make morph system reusable - #327

Merged
tiero merged 3 commits into
masterfrom
feat/logo-easter-egg-icons
Feb 24, 2026
Merged

tiero merged 3 commits into
masterfrom
feat/logo-easter-egg-icons

Conversation

@sahilc0

@sahilc0 sahilc0 commented Feb 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Adds bitcoin (₿) pixel art to the logo tap Easter egg
  • Extracts shape definitions and morph logic into reusable modules (pixel-shapes.ts, usePixelMorph.ts) so the animation can be used elsewhere (loading screens, etc.)
  • Supports variable pixel counts across shapes — no more fixed 34-pixel restriction
  • Cycle order: logo → space invader → bitcoin → heart → logo

Changes

  • src/icons/pixel-shapes.ts — shared shape definitions, parseShape(), grid constants, auto-padding for variable pixel counts
  • src/icons/usePixelMorph.ts — usePixelMorph() hook encapsulating the morph state machine
  • src/icons/Logo.tsx — simplified to consume the hook and shared constants

Test plan

  • Tap logo on wallet screen, verify cycle: logo → space invader → bitcoin → heart → logo
  • Verify 3-second auto-revert timer works
  • Verify morph animations are smooth between all transitions
  • Verify LogoIconAnimated in AlertBox still pulses correctly
  • Build passes with no type errors

Summary by CodeRabbit

  • Refactor

    • Reworked the logo pixel-morph implementation for smoother, more consistent animations and fewer glitches during rapid interactions; visual behavior and accessibility (click, Space/Enter) remain unchanged.
    • Expanded and normalized pixel shapes to enable fluid transitions between icon states.
  • Style

    • Minor formatting tweak in Settings display with no functional impact.

…sable

Extract pixel shape definitions and morph state machine into reusable
modules so the animation system can be used elsewhere (e.g. loading screens).

- Add pixel-shapes.ts: shared shape definitions, parseShape(), grid constants
- Add usePixelMorph.ts: custom hook for morph state machine (advance, revert, settle)
- Refactor Logo.tsx to consume the hook and shared constants
- Add bitcoin (₿) pixel art to the shape cycle
- Support variable pixel counts across shapes via automatic padding
- Cycle order: logo → space invader → bitcoin → heart → logo
@coderabbitai

coderabbitai Bot commented Feb 23, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 725597e and df7fa24.

📒 Files selected for processing (1)
  • src/screens/Settings/General.tsx
✅ Files skipped from review due to trivial changes (1)
  • src/screens/Settings/General.tsx

Walkthrough

Extracts pixel-morph logic and shape data from Logo.tsx into a new usePixelMorph hook and pixel-shapes module, moving timers, state, and shape definitions out of the component while preserving SVG rendering and interaction surface.

Changes

Cohort / File(s) Summary
Logo Component
src/icons/Logo.tsx
Removed local morph state, timers, and shape definitions; now consumes usePixelMorph (shapeIdx, settled, reverting, slots, advance) and routes click/keyboard interactions to advance; rendering driven by hook-provided values.
Pixel Shapes Module
src/icons/pixel-shapes.ts
Added 8×8 pixel grid constants, timing constants, Pixel/Slot types, parseShape utility, raw ASCII shapes, and exported padded SHAPES and SLOT_SHAPES (per-cell ids) for consistent morph animations.
Pixel Morph Hook
src/icons/usePixelMorph.ts
Added usePixelMorph hook and UsePixelMorphResult interface: manages shape index, settled/reverting state, advance/startRevert logic, RAF/timeouts, slot selection from SLOT_SHAPES, and cleanup on unmount.
Minor formatting
src/screens/Settings/General.tsx
Non-functional formatting change (Theme Row wrapped across lines).

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant Logo as Logo Component
  participant Hook as usePixelMorph
  participant Shapes as pixel-shapes
  participant Timer as RAF/Timeouts

  User->>Logo: click / Space / Enter
  Logo->>Hook: advance()
  Hook->>Hook: update shapeIdx, settled, reverting
  Hook->>Shapes: read SLOT_SHAPES[shapeIdx]
  Hook-->>Logo: slots, settled, reverting (return state)
  Hook->>Timer: schedule revert / settle via RAF & timeouts
  Timer-->>Hook: timers fire (revert/settle)
  Hook-->>Logo: update state -> re-render SVG pixels
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • louisinger
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: adding a bitcoin icon to the logo Easter egg and refactoring the morph system into reusable modules (pixel-shapes.ts and usePixelMorph hook).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/logo-easter-egg-icons

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Feb 23, 2026 •

Copy link
Copy Markdown

Deploying wallet-bitcoin with  Cloudflare Pages  Cloudflare Pages

Latest commit: df7fa24
Status: ✅  Deploy successful!
Preview URL: https://d8b069ba.wallet-bitcoin.pages.dev
Branch Preview URL: https://feat-logo-easter-egg-icons.wallet-bitcoin.pages.dev

View logs

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Feb 23, 2026 •

Copy link
Copy Markdown

Deploying wallet-mutinynet with  Cloudflare Pages  Cloudflare Pages

Latest commit: df7fa24
Status: ✅  Deploy successful!
Preview URL: https://df674120.arkade-wallet.pages.dev
Branch Preview URL: https://feat-logo-easter-egg-icons.arkade-wallet.pages.dev

View logs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/icons/usePixelMorph.ts (2)

59-65: shapeIdx from the closure could be stale under rapid taps within the same render frame.

If advance is called twice synchronously before React re-renders (unlikely but possible), shapeIdx in the closure is still the value from the previous render. The second call won't see the first call's setShapeIdx. This can also cause two rAF/timeout schedules where the first timeout handle is leaked when the second overwrites revertRef.current.

Using a functional updater for setShapeIdx wouldn't fully solve it (the next === 0 branch decision also needs the current index). If you want to be fully safe, a useRef mirror of shapeIdx would work. Given the tap-rate constraint this is low risk, but noting for completeness.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/icons/usePixelMorph.ts` around lines 59 - 65, The closure over shapeIdx
in advance can be stale during rapid synchronous calls and can leak timers via
revertRef; fix by adding a useRef mirror (e.g., shapeIdxRef) that you update
whenever you call setShapeIdx (use functional updater to compute next index and
then set shapeIdxRef.current = next), and in advance always read from
shapeIdxRef.current to compute next and decide the next === 0 branch; also clear
any existing timeout in revertRef before setting a new one (and update
revertRef.current when scheduling) so no handles are overwritten/leaked;
reference symbols: shapeIdx, setShapeIdx, shapeIdxRef (new), revertRef,
startRevert, REVERT_DELAY, SHAPES, advance.

40-57: The reverting and settled branches are identical — consider deduplication.

Both branches perform the same operations: clear the current state, set settled(false), then via rAF advance to shape 1 and schedule revert. Collapsing them would reduce surface area for divergent bugs.

Proposed refactor
   const advance = useCallback(() => {
     if (revertRef.current) clearTimeout(revertRef.current)
     if (settleRef.current) clearTimeout(settleRef.current)

-    if (reverting) {
-      setReverting(false)
-      setSettled(false)
-      requestAnimationFrame(() => {
-        setShapeIdx(1)
-        revertRef.current = setTimeout(startRevert, REVERT_DELAY)
-      })
-      return
-    }
-
-    if (settled) {
+    if (reverting || settled) {
+      setReverting(false)
       setSettled(false)
       requestAnimationFrame(() => {
         setShapeIdx(1)
         revertRef.current = setTimeout(startRevert, REVERT_DELAY)
       })
       return
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/icons/usePixelMorph.ts` around lines 40 - 57, The two branches can be
collapsed to avoid duplication: replace the separate if (reverting) { ... } and
if (settled) { ... } blocks with a single if (reverting || settled) { if
(reverting) setReverting(false); setSettled(false); requestAnimationFrame(() =>
{ setShapeIdx(1); revertRef.current = setTimeout(startRevert, REVERT_DELAY); });
return; } so you preserve the setReverting(false) call only when reverting is
true while reusing the shared logic (setSettled(false), requestAnimationFrame,
setShapeIdx, revertRef.current, startRevert, REVERT_DELAY).
src/icons/Logo.tsx (1)

22-27: Pre-existing: onKeyDown is a no-op — keyboard users can't trigger the Easter egg with Space.

Not introduced in this PR, but worth noting: the empty onKeyDown={() => {}} means the role="button" div doesn't respond to Space key presses (Enter may work via onClick in some browsers). Since you're touching this component, consider wiring it up:

Proposed fix
- onKeyDown={() => {}}
+ onKeyDown={(e) => { if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); advance(); } }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/icons/Logo.tsx` around lines 22 - 27, The div with role='button'
currently has a no-op onKeyDown; update the onKeyDown handler in the Logo
component so keyboard users can trigger the Easter egg: implement an onKeyDown
that listens for 'Enter' and Space (handle both ' ' and legacy 'Spacebar'), call
the existing advance() function when those keys are pressed, and preventDefault
for Space to avoid page scroll; replace the empty onKeyDown={() => {}} with this
accessible handler.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/icons/usePixelMorph.ts`:
- Around line 19-24: The cleanup in the useEffect only clears existing timeouts
(revertRef/settleRef) but doesn’t prevent requestAnimationFrame callbacks from
later creating new timeouts in advance, which can orphan timers after unmount;
add a mounted flag (e.g., isMountedRef) set true on mount and false in the
cleanup, and then guard any rAF callback inside advance (and any other callbacks
that create timeouts) with if (!isMountedRef.current) return so they never set
revertRef.current or settleRef.current after unmount.

---

Nitpick comments:
In `@src/icons/Logo.tsx`:
- Around line 22-27: The div with role='button' currently has a no-op onKeyDown;
update the onKeyDown handler in the Logo component so keyboard users can trigger
the Easter egg: implement an onKeyDown that listens for 'Enter' and Space
(handle both ' ' and legacy 'Spacebar'), call the existing advance() function
when those keys are pressed, and preventDefault for Space to avoid page scroll;
replace the empty onKeyDown={() => {}} with this accessible handler.

In `@src/icons/usePixelMorph.ts`:
- Around line 59-65: The closure over shapeIdx in advance can be stale during
rapid synchronous calls and can leak timers via revertRef; fix by adding a
useRef mirror (e.g., shapeIdxRef) that you update whenever you call setShapeIdx
(use functional updater to compute next index and then set shapeIdxRef.current =
next), and in advance always read from shapeIdxRef.current to compute next and
decide the next === 0 branch; also clear any existing timeout in revertRef
before setting a new one (and update revertRef.current when scheduling) so no
handles are overwritten/leaked; reference symbols: shapeIdx, setShapeIdx,
shapeIdxRef (new), revertRef, startRevert, REVERT_DELAY, SHAPES, advance.
- Around line 40-57: The two branches can be collapsed to avoid duplication:
replace the separate if (reverting) { ... } and if (settled) { ... } blocks with
a single if (reverting || settled) { if (reverting) setReverting(false);
setSettled(false); requestAnimationFrame(() => { setShapeIdx(1);
revertRef.current = setTimeout(startRevert, REVERT_DELAY); }); return; } so you
preserve the setReverting(false) call only when reverting is true while reusing
the shared logic (setSettled(false), requestAnimationFrame, setShapeIdx,
revertRef.current, startRevert, REVERT_DELAY).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 76980ec and 18c948e.

📒 Files selected for processing (3)
  • src/icons/Logo.tsx
  • src/icons/pixel-shapes.ts
  • src/icons/usePixelMorph.ts

Comment thread src/icons/usePixelMorph.ts Resolved
- Add mountedRef to prevent orphaned timers after unmount
- Deduplicate reverting/settled branches in advance()
- Wire up keyboard handler for Space/Enter on logo button

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/icons/usePixelMorph.ts (1)

28-36: Consider guarding settleRef's callback with mountedRef for consistency.

The settleRef timeout callback (lines 32–35) calls setSettled/setReverting without a mountedRef.current check, unlike the rAF callback in advance. In practice this is safe — the cleanup effect cancels settleRef via clearTimeout before the component fully unmounts, and React 18 treats post-unmount setState as a no-op — but applying the same guard here makes the defensive pattern uniform across all async callbacks.

♻️ Proposed consistency fix
  settleRef.current = setTimeout(() => {
+   if (!mountedRef.current) return
    setSettled(true)
    setReverting(false)
  }, MORPH_MS + 50)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/icons/usePixelMorph.ts` around lines 28 - 36, The timeout callback in
startRevert uses settleRef to call setSettled and setReverting without checking
mountedRef like the rAF path; update startRevert so the setTimeout handler first
checks mountedRef.current (or bail if false) before calling
setSettled/setReverting, while keeping the existing
clearTimeout(settleRef.current) and setTimeout behavior intact to ensure the
defensive pattern matches advance's rAF callback.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/icons/usePixelMorph.ts`:
- Around line 28-36: The timeout callback in startRevert uses settleRef to call
setSettled and setReverting without checking mountedRef like the rAF path;
update startRevert so the setTimeout handler first checks mountedRef.current (or
bail if false) before calling setSettled/setReverting, while keeping the
existing clearTimeout(settleRef.current) and setTimeout behavior intact to
ensure the defensive pattern matches advance's rAF callback.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18c948e and 725597e.

📒 Files selected for processing (2)
  • src/icons/Logo.tsx
  • src/icons/usePixelMorph.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/icons/Logo.tsx

@tiero

tiero commented Feb 23, 2026

Copy link
Copy Markdown
Member

plese pnpm format and push again

@bordalix bordalix left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

NIT: change flow from:
space monster -> bitcoin -> love to
space monster -> loves -> bitcoin 😄

@tiero
tiero merged commit 13a7d16 into master Feb 24, 2026
5 checks passed
@sahilc0

sahilc0 commented Feb 26, 2026

Copy link
Copy Markdown
Contributor Author

NIT: change flow from: space monster -> bitcoin -> love to space monster -> loves -> bitcoin 😄

new pr for this submitting now lol

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.

3 participants