Skip to content

Improve PWA#752

Merged
feruzm merged 3 commits intodevelopfrom
paw
Apr 11, 2026
Merged

Improve PWA#752
feruzm merged 3 commits intodevelopfrom
paw

Conversation

@feruzm
Copy link
Copy Markdown
Member

@feruzm feruzm commented Apr 11, 2026

Summary by CodeRabbit

  • New Features

    • Install the app as a PWA from the landing page; new install link, footer entry, and client-wide install handling
    • Smart install banner with 14-day dismissal cooldown and iOS-specific hint
  • Configuration

    • Service worker activates immediately and claims clients
    • Updated PWA metadata, manifest, icons, and theme/viewport settings
    • Added localization strings for install messaging
  • Bug Fixes

    • Improved reply cleaning to strip auto-footer patterns
  • Tests

    • Added/updated tests for PWA install flow, iOS detection, and reply cleaning

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 11, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cfc71df0-796c-42fe-b350-9bc6427a70fb

📥 Commits

Reviewing files that changed from the base of the PR and between 3b59bd7 and 6ca5ef8.

⛔ Files ignored due to path filters (6)
  • packages/render-helper/dist/browser/index.js is excluded by !**/dist/**
  • packages/render-helper/dist/browser/index.js.map is excluded by !**/dist/**, !**/*.map
  • packages/render-helper/dist/node/index.cjs is excluded by !**/dist/**
  • packages/render-helper/dist/node/index.cjs.map is excluded by !**/dist/**, !**/*.map
  • packages/render-helper/dist/node/index.mjs is excluded by !**/dist/**
  • packages/render-helper/dist/node/index.mjs.map is excluded by !**/dist/**, !**/*.map
📒 Files selected for processing (2)
  • packages/render-helper/CHANGELOG.md
  • packages/render-helper/package.json

📝 Walkthrough

Walkthrough

Adds PWA install support: a new usePwaInstall hook and helpers capture beforeinstallprompt and appinstalled, expose install controls, integrate an install CTA in landing page and a route-scoped banner, update manifest/Next PWA config and metadata, and add tests and i18n strings.

Changes

Cohort / File(s) Summary
PWA Install Core
apps/web/src/features/pwa-install/index.ts, apps/web/src/features/pwa-install/use-pwa-install.ts, apps/web/src/features/pwa-install/is-ios-safari.ts
New feature: usePwaInstall() hook (deferred prompt snapshot, canInstall/installed/install()), isIosSafari() utility, and re-export entrypoint.
App Bootstrap & Banner Integration
apps/web/src/app/client-init.tsx, apps/web/src/features/banners/index.tsx, apps/web/src/features/banners/pwa-install-banner.tsx
Side-effect import of pwa-install at client init; added PwaInstallBanner rendered by BannerManager, shown on /mobile with 14-day localStorage cooldown, install/dismiss handling and iOS hint logic.
Landing Page Integration
apps/web/src/app/_components/landing-page/index.tsx, apps/web/src/app/_components/landing-page/landing-download-links.tsx
Swapped Android icon to PNG, added /mobile footer nav item, and added conditional internal “install web app” link that intercepts navigation to trigger install when canInstall.
PWA Config & Metadata
apps/web/next.config.js, apps/web/src/app/layout.tsx, apps/web/src/app/manifest.json
Next PWA: skipWaiting: true, clientsClaim: true; layout metadata extended (applicationName, appleWebApp, icons) and exported viewport; manifest start_url/ and added scope: "/".
i18n
apps/web/src/features/i18n/locales/en-US.json
Added landing and banner strings: landing-page.install-web-app, landing-page.get-mobile-app, and banners.install-* plus banners.install-ios-hint.
Tests
apps/web/src/specs/features/landing-page.spec.tsx, apps/web/src/specs/features/pwa-install/is-ios-safari.spec.ts, apps/web/src/specs/features/pwa-install/use-pwa-install.spec.ts
Updated landing tests to distinguish external links; added test suites for isIosSafari() (UA permutations) and usePwaInstall() (event handling, install outcomes, standalone detection, state reset).
Render Helper: Reply Cleaning
packages/render-helper/src/methods/clean-reply.method.ts, packages/render-helper/src/methods/clean-reply.method.spec.ts
cleanReply now filters out auto-footer pattern containing '<sub>[via apps from](' (case-insensitive); added tests covering footer removal and preservation of non-footer mentions.
Package Metadata
packages/render-helper/CHANGELOG.md, packages/render-helper/package.json
Bumped render-helper version to 2.4.29 and added changelog entry mentioning PWA improvements.

Sequence Diagram

sequenceDiagram
    participant User
    participant Browser
    participant App as App (client-init)
    participant Hook as usePwaInstall
    participant UI as UI Components
    participant Storage as localStorage

    Note over Browser,App: initial page load
    Browser->>App: beforeinstallprompt (browser event)
    App->>Hook: global listener captures event
    Hook->>Hook: store deferredPrompt, set canInstall=true
    Hook->>UI: expose canInstall/installed
    User->>UI: clicks install CTA
    UI->>Hook: call install()
    Hook->>Browser: deferredPrompt.prompt()
    Browser->>User: show install dialog
    User-->>Browser: accept/dismiss
    Browser->>Hook: userChoice result
    Hook->>Hook: clear deferredPrompt, update installed/canInstall
    alt dismissed
        Hook->>Storage: write dismissal timestamp
    end
    Hook->>UI: update rendering (hide CTA / show installed state)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I caught a prompt that blinked so bright,

tucked it safe before the night.
Banners hop and icons beam,
click to plant the install dream.
Hop in — the web app’s ready to light!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Improve PWA' is vague and generic, providing minimal information about the specific changes made beyond broad PWA enhancements. Consider a more specific title that captures the primary changes, such as 'Add PWA install prompt and banner with mobile landing page integration' or 'Implement PWA install UI and manifest configuration updates'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch paw

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
apps/web/src/app/_components/landing-page/index.tsx (1)

361-363: Use route constants instead of a hardcoded /mobile path.

Please route this through the shared src/routes.ts constant to keep navigation paths centralized and safer for future route refactors.

As per coding guidelines, "Use Next.js App Router with the routing architecture defined in src/routes.ts for route constants."

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

In `@apps/web/src/app/_components/landing-page/index.tsx` around lines 361 - 363,
Replace the hardcoded href="/mobile" on the Link in the landing-page component
with the centralized route constant from src/routes.ts: import the exported
routes object (or the named constant) and use routes.mobile (or the appropriate
exported name) instead of the string; update the Link invocation (Link
href={routes.mobile}) and add the import for the routes constant at the top of
the module so navigation uses the shared route constant rather than a literal
path.
apps/web/src/specs/features/landing-page.spec.tsx (1)

181-189: Add one interaction test for the install-click path.

Consider adding a case where usePwaInstall is mocked with canInstall: true and asserting click behavior triggers install flow (instead of plain navigation fallback). This would cover the new user-visible branch more completely.

As per coding guidelines, "Use React Testing Library with Vitest for component tests, testing user-visible behavior rather than implementation details."

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

In `@apps/web/src/specs/features/landing-page.spec.tsx` around lines 181 - 189,
Add an interaction test for LandingDownloadLinks that mocks usePwaInstall to
return canInstall: true and a jest/vitest spy for onInstall, then render the
component, simulate a user click on the PWA link (use userEvent) and assert the
onInstall spy was called (and that no navigation fallback occurred).
Specifically, mock the module that exports usePwaInstall to return { canInstall:
true, install: mockInstall } (or named fields used in the component), import
userEvent from `@testing-library/user-event`, render LandingDownloadLinks, find
the link via screen.getByRole(..., { name: /landing-page\.install-web-app/ }),
userEvent.click it, and expect the mockInstall toHaveBeenCalled (and optionally
assert href still equals "/mobile" but click triggers install). Use Vitest mocks
(vi.fn/vi.mock) and async assertions (await/await waitFor) as appropriate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/features/banners/pwa-install-banner.tsx`:
- Line 11: Replace the unprefixed localStorage key by updating the DISMISSED_KEY
constant to use the app storage prefix (e.g. change "pwa-install-dismissed-at"
to "ecency_pwa-install-dismissed-at") so it matches the ls helper's
auto-prefixed convention; ensure all reads/writes in pwa-install-banner.tsx that
reference DISMISSED_KEY continue to use that constant so existing storage access
remains consistent.
- Around line 93-97: The icon-only dismiss Button in PwaInstallBanner lacks an
accessible name; update the Button (the one using onClick={handleDismiss} and
icon={<UilMultiply .../>}) to include an explicit accessible label (e.g.,
aria-label="Dismiss" or aria-label="Close install banner") or provide an
associated visually-hidden text element so screen readers can announce the
control; ensure the label text is clear and localized if applicable.

In `@apps/web/src/features/i18n/locales/en-US.json`:
- Line 2827: The string value for the "no-ls-description" locale key has awkward
grammar; update the value for "no-ls-description" to clearer, natural English
(e.g., "It seems you or your browser has disabled local data for our website.
Unfortunately, you won't be able to use many Ecency features such as drafts,
local decks, the advanced trading dashboard, and more. Please use the latest
version of a modern browser or enable local data in your browser settings.") so
punctuation/capitalization and phrasing are corrected and consistent with UI
tone.

---

Nitpick comments:
In `@apps/web/src/app/_components/landing-page/index.tsx`:
- Around line 361-363: Replace the hardcoded href="/mobile" on the Link in the
landing-page component with the centralized route constant from src/routes.ts:
import the exported routes object (or the named constant) and use routes.mobile
(or the appropriate exported name) instead of the string; update the Link
invocation (Link href={routes.mobile}) and add the import for the routes
constant at the top of the module so navigation uses the shared route constant
rather than a literal path.

In `@apps/web/src/specs/features/landing-page.spec.tsx`:
- Around line 181-189: Add an interaction test for LandingDownloadLinks that
mocks usePwaInstall to return canInstall: true and a jest/vitest spy for
onInstall, then render the component, simulate a user click on the PWA link (use
userEvent) and assert the onInstall spy was called (and that no navigation
fallback occurred). Specifically, mock the module that exports usePwaInstall to
return { canInstall: true, install: mockInstall } (or named fields used in the
component), import userEvent from `@testing-library/user-event`, render
LandingDownloadLinks, find the link via screen.getByRole(..., { name:
/landing-page\.install-web-app/ }), userEvent.click it, and expect the
mockInstall toHaveBeenCalled (and optionally assert href still equals "/mobile"
but click triggers install). Use Vitest mocks (vi.fn/vi.mock) and async
assertions (await/await waitFor) as appropriate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5839e1bb-71ce-4170-be53-ac184d063817

📥 Commits

Reviewing files that changed from the base of the PR and between 64d5335 and f043f00.

📒 Files selected for processing (15)
  • apps/web/next.config.js
  • apps/web/src/app/_components/landing-page/index.tsx
  • apps/web/src/app/_components/landing-page/landing-download-links.tsx
  • apps/web/src/app/client-init.tsx
  • apps/web/src/app/layout.tsx
  • apps/web/src/app/manifest.json
  • apps/web/src/features/banners/index.tsx
  • apps/web/src/features/banners/pwa-install-banner.tsx
  • apps/web/src/features/i18n/locales/en-US.json
  • apps/web/src/features/pwa-install/index.ts
  • apps/web/src/features/pwa-install/is-ios-safari.ts
  • apps/web/src/features/pwa-install/use-pwa-install.ts
  • apps/web/src/specs/features/landing-page.spec.tsx
  • apps/web/src/specs/features/pwa-install/is-ios-safari.spec.ts
  • apps/web/src/specs/features/pwa-install/use-pwa-install.spec.ts

import { useCallback, useEffect, useState } from "react";
import { isIosSafari, usePwaInstall } from "@/features/pwa-install";

const DISMISSED_KEY = "pwa-install-dismissed-at";
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.

⚠️ Potential issue | 🟡 Minor

Use prefixed localStorage key for consistency with app storage conventions.

Line 11 uses an unprefixed key, which can drift from existing storage naming conventions.

💡 Suggested fix
-const DISMISSED_KEY = "pwa-install-dismissed-at";
+const DISMISSED_KEY = "ecency_pwa-install-dismissed-at";
Based on learnings: LocalStorage keys are auto-prefixed by the ls helper (`ecency_`), and direct localStorage usage in the web app should use full prefixed keys for consistency.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const DISMISSED_KEY = "pwa-install-dismissed-at";
const DISMISSED_KEY = "ecency_pwa-install-dismissed-at";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/features/banners/pwa-install-banner.tsx` at line 11, Replace the
unprefixed localStorage key by updating the DISMISSED_KEY constant to use the
app storage prefix (e.g. change "pwa-install-dismissed-at" to
"ecency_pwa-install-dismissed-at") so it matches the ls helper's auto-prefixed
convention; ensure all reads/writes in pwa-install-banner.tsx that reference
DISMISSED_KEY continue to use that constant so existing storage access remains
consistent.

Comment on lines +93 to +97
<Button
onClick={handleDismiss}
appearance="white-link"
icon={<UilMultiply className="w-4 h-4" />}
/>
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.

⚠️ Potential issue | 🟠 Major

Add an accessible name to the dismiss icon button.

The dismiss button is icon-only and has no explicit text alternative, so screen readers won’t get a reliable control label.

💡 Suggested fix
           <Button
             onClick={handleDismiss}
             appearance="white-link"
+            aria-label={i18next.t("banners.install-dismiss")}
+            title={i18next.t("banners.install-dismiss")}
             icon={<UilMultiply className="w-4 h-4" />}
           />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Button
onClick={handleDismiss}
appearance="white-link"
icon={<UilMultiply className="w-4 h-4" />}
/>
<Button
onClick={handleDismiss}
appearance="white-link"
aria-label={i18next.t("banners.install-dismiss")}
title={i18next.t("banners.install-dismiss")}
icon={<UilMultiply className="w-4 h-4" />}
/>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/features/banners/pwa-install-banner.tsx` around lines 93 - 97,
The icon-only dismiss Button in PwaInstallBanner lacks an accessible name;
update the Button (the one using onClick={handleDismiss} and icon={<UilMultiply
.../>}) to include an explicit accessible label (e.g., aria-label="Dismiss" or
aria-label="Close install banner") or provide an associated visually-hidden text
element so screen readers can announce the control; ensure the label text is
clear and localized if applicable.

"banners": {
"no-ls-title": "Unable to use local data",
"no-ls-description": "It seems You or your browser has disabled local data for our website. Unfortunately, You can't use the most of Ecency features such as drafts, local decks, advanced trading dashboard and more. Please, use latest versions of any modern browser or turn on local data manually."
"no-ls-description": "It seems You or your browser has disabled local data for our website. Unfortunately, You can't use the most of Ecency features such as drafts, local decks, advanced trading dashboard and more. Please, use latest versions of any modern browser or turn on local data manually.",
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.

⚠️ Potential issue | 🟡 Minor

Fix awkward grammar in local-data warning copy.

Line 2827 reads unnaturally (“can't use the most of Ecency features”), which reduces UI text quality.

💡 Suggested copy edit
-    "no-ls-description": "It seems You or your browser has disabled local data for our website. Unfortunately, You can't use the most of Ecency features such as drafts, local decks, advanced trading dashboard and more. Please, use latest versions of any modern browser or turn on local data manually.",
+    "no-ls-description": "It seems you or your browser has disabled local data for our website. Unfortunately, you can't use most Ecency features such as drafts, local decks, advanced trading dashboard, and more. Please use the latest version of a modern browser or turn on local data manually.",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"no-ls-description": "It seems You or your browser has disabled local data for our website. Unfortunately, You can't use the most of Ecency features such as drafts, local decks, advanced trading dashboard and more. Please, use latest versions of any modern browser or turn on local data manually.",
"no-ls-description": "It seems you or your browser has disabled local data for our website. Unfortunately, you can't use most Ecency features such as drafts, local decks, advanced trading dashboard, and more. Please use the latest version of a modern browser or turn on local data manually.",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/features/i18n/locales/en-US.json` at line 2827, The string value
for the "no-ls-description" locale key has awkward grammar; update the value for
"no-ls-description" to clearer, natural English (e.g., "It seems you or your
browser has disabled local data for our website. Unfortunately, you won't be
able to use many Ecency features such as drafts, local decks, the advanced
trading dashboard, and more. Please use the latest version of a modern browser
or enable local data in your browser settings.") so punctuation/capitalization
and phrasing are corrected and consistent with UI tone.

@feruzm feruzm added the patch Bug fixes and patches (1.0.0 → 1.0.1), add this only if any packages/ have patch changes in PR label Apr 11, 2026
@feruzm feruzm merged commit 4a0af10 into develop Apr 11, 2026
1 check was pending
@feruzm feruzm deleted the paw branch April 11, 2026 08:50
@coderabbitai coderabbitai bot mentioned this pull request Apr 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch Bug fixes and patches (1.0.0 → 1.0.1), add this only if any packages/ have patch changes in PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant