feat: revamp Ko-fi support prompts in header and export flow#2042
Conversation
Move the Ko-fi ask to the surfaces that actually convert and retire the one that didn't. - Header: accent-colored "Support me on Ko-fi" pill (native reproduction of the official widget — animated cup, white label) using the user's accent color; trimmed the verbose "Press ? for help" hint to reclaim room. - Export dialog: inline post-export success view with a low-pressure support ask + a free "star on GitHub" fallback, in both the bin designer and baseplate generator. Gated to returning makers (2nd+ export) and capped at once per 30 days, so first-timers and power users aren't nagged. - Removed the timer-based Ko-fi nudge toast (it converted at ~0.7%); tightened the remaining feedback copy across all 10 locales. - Tagged the export CTA with kofi_clicked source=bin_designer_export / baseplate_export to compare against the header pill.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This PR repositions Ko-fi support prompts to higher-intent UI moments (header + post-export success view), removes the timer-based Ko-fi nudge, and adds a shared gating mechanism so the export support ask is shown only to returning users on a cooldown.
Changes:
- Revamps the header Ko-fi affordance into an accent-colored “Support me on Ko-fi” pill (with native cup wiggle animation) and simplifies the help affordance.
- Adds a post-export inline success view (
ExportSupportPrompt) gated byexportSupportGate(2nd+ export; max once per 30 days), wired into both bin designer and baseplate export flows. - Removes the timer-based Ko-fi engagement nudge and updates i18n strings across supported locales.
Reviewed changes
Copilot reviewed 25 out of 26 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/shared/components/HeaderSupportLinks/HeaderSupportLinks.tsx | Updates Help UI + replaces Ko-fi action with a prominent btn-primary pill using the animated cup asset. |
| src/shared/components/HeaderSupportLinks/HeaderSupportLinks.test.tsx | Updates header tests to reflect the new Ko-fi label/ARIA key. |
| src/shared/components/ExportDialog/index.ts | Re-exports ExportSupportPrompt from the shared ExportDialog module. |
| src/shared/components/ExportDialog/ExportSupportPrompt.tsx | Adds the post-export success/support view with Ko-fi + GitHub actions and analytics. |
| src/shared/components/ExportDialog/ExportSupportPrompt.test.tsx | Adds unit tests for the support prompt (copy, click tracking, window.open, done callback). |
| src/shared/components/ExportDialog/exportSupportGate.ts | Introduces the localStorage-backed gating logic (returning user + cooldown). |
| src/shared/components/ExportDialog/exportSupportGate.test.ts | Adds unit tests for the gate behavior (1st vs 2nd export; cooldown). |
| src/shared/components/ExportDialog/ExportDialog.tsx | Extends the shared dialog to optionally render a success view instead of the export form. |
| src/index.css | Adds the Ko-fi cup “wiggle” keyframes/class and relies on reduced-motion blanket rules to disable it. |
| src/i18n/locales/en.ts | Removes old header/help/tip keys; adds header.supportOnKofi + export support keys; removes timer-nudge key. |
| src/i18n/locales/en.json | Same key updates as en.ts for JSON locale. |
| src/i18n/locales/de.json | Updates German strings: removes old keys; adds export support keys + new Ko-fi label. |
| src/i18n/locales/es.json | Updates Spanish strings: removes old keys; adds export support keys + new Ko-fi label. |
| src/i18n/locales/fr.json | Updates French strings: removes old keys; adds export support keys + new Ko-fi label. |
| src/i18n/locales/ja.json | Updates Japanese strings: removes old keys; adds export support keys + new Ko-fi label. |
| src/i18n/locales/nb.json | Updates Norwegian strings: removes old keys; adds export support keys + new Ko-fi label. |
| src/i18n/locales/nl.json | Updates Dutch strings: removes old keys; adds export support keys + new Ko-fi label. |
| src/i18n/locales/pt-BR.json | Updates Brazilian Portuguese strings: removes old keys; adds export support keys + new Ko-fi label. |
| src/i18n/locales/sv.json | Updates Swedish strings: removes old keys; adds export support keys + new Ko-fi label. |
| src/i18n/locales/uk.json | Updates Ukrainian strings: removes old keys; adds export support keys + new Ko-fi label. |
| src/features/engagement/useEngagementNudges.ts | Removes Ko-fi from the periodic engagement nudge polling; keeps feedback nudge only. |
| src/features/engagement/useEngagementNudges.test.ts | Adds tests ensuring only feedback nudge is considered/shown on the timer. |
| src/features/bin-designer/components/ExportDialog/ExportDialog.tsx | Integrates the new gated success view into the bin designer export flow. |
| src/features/baseplate/hooks/useBaseplateExport.ts | Changes baseplate export to return success boolean and removes single-file success toast (handled by dialog view). |
| src/features/baseplate/components/BaseplatePage/BaseplatePage.tsx | Integrates the new gated success view + export-complete toast/close behavior into baseplate export. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <button | ||
| onClick={handleKofiClick} | ||
| className="btn btn-ghost px-2.5 py-1.5 text-sm leading-none text-content-secondary flex items-center gap-1.5" | ||
| title={t('header.tip')} | ||
| aria-label={t('header.tip')} | ||
| className="btn btn-primary px-3 py-1.5 text-sm leading-none flex items-center gap-1.5" | ||
| style={{ color: '#fff', textShadow: '0 1px 1px rgba(34, 34, 34, 0.15)' }} | ||
| title={t('header.supportOnKofi')} | ||
| aria-label={t('header.supportOnKofi')} |
| <button | ||
| onClick={handleKofi} | ||
| className="btn btn-primary flex w-full items-center justify-center gap-2 px-4 py-2.5 text-sm leading-none" | ||
| style={{ color: '#fff', textShadow: '0 1px 1px rgba(34, 34, 34, 0.15)' }} | ||
| > |
| function load(): ExportSupportState { | ||
| try { | ||
| const raw = localStorage.getItem(STORAGE_KEY); | ||
| if (raw) return JSON.parse(raw) as ExportSupportState; | ||
| } catch { | ||
| /* ignore malformed / unavailable storage */ | ||
| } | ||
| return { exportCount: 0 }; | ||
| } | ||
|
|
||
| function save(state: ExportSupportState): void { | ||
| try { | ||
| localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); | ||
| } catch { | ||
| /* storage may be full or unavailable */ | ||
| } | ||
| } | ||
|
|
||
| function cooledDown(lastShown: string | undefined): boolean { | ||
| if (!lastShown) return true; | ||
| const elapsed = Date.now() - new Date(lastShown).getTime(); | ||
| return elapsed > COOLDOWN_DAYS * 24 * 60 * 60 * 1000; | ||
| } |
Greptile SummaryThis PR revamps Ko-fi support prompts across three surfaces: the header pill is redesigned as an accent-colored "Support me on Ko-fi" button, a new post-export success view (
Confidence Score: 5/5Safe to merge — the changes are additive UI improvements with no mutations to core layout data, storage, or geometry logic. The export gate is well-isolated in its own localStorage key with defensive parsing and a tested cooldown; the boolean return from downloadBaseplate cleanly delegates dialog-close responsibility to the component; all 10 locale files carry the new keys; and the removed Ko-fi nudge path is fully deleted rather than left dormant. No cross-feature imports, no changes to coordinate math, no store schema changes. No files require special attention. Important Files Changed
Reviews (3): Last reviewed commit: "refactor: centralize Ko-fi/GitHub URLs i..." | Re-trigger Greptile |
Address PR review: - exportSupportGate: validate the persisted shape so a malformed payload (non-numeric count or unparseable timestamp) can't poison the counter with NaN and permanently suppress the prompt; treat an invalid lastShown as cooled down and make the cooldown boundary inclusive. - Export recordExportAndShouldPromptSupport through the ExportDialog barrel and import it there from both export wrappers (public-API contract).
|
Thanks for the review — addressed in 91d1e37: Fixed
Intentional — keeping as-is
|
The support URLs were duplicated as per-file constants in HeaderSupportLinks and ExportSupportPrompt. Add src/shared/constants/links.ts as the single source of truth and import from it (issues URL derives from the repo URL). Mobile/sidebar inline usages are left untouched (out of this PR's scope).
What & why
Moves the Ko-fi support ask to the surfaces that actually convert, and retires the one that didn't.
Header
<script>widget can't run here — CSP blocks third-party scripts — and can't use the accent color, so it's reproduced natively.)?for help" hint to a compact "Help" to make room.Export flow (bin designer + baseplate)
Nudges
Measurement
The export CTA is tagged
kofi_clickedwithsource: bin_designer_export/baseplate_export, so its conversion can be compared head-to-head against the header pill (source: header).Notes
exportSupportGate(returning-maker + cooldown logic) lives inshared/with its own storage key — no cross-feature import into the engagement slice.Testing