Skip to content

🐛 Hid "open in your email app" button on iOS, where it's unreliable#26449

Merged
EvanHahn merged 1 commit intomainfrom
hide-inbox-links-on-ios
Feb 18, 2026
Merged

🐛 Hid "open in your email app" button on iOS, where it's unreliable#26449
EvanHahn merged 1 commit intomainfrom
hide-inbox-links-on-ios

Conversation

@EvanHahn
Copy link
Contributor

closes https://linear.app/ghost/issue/NY-1050

On iOS, we have four options for Inbox Links:

  1. Don't show them at all.
  2. Open the email inbox in the browser (a regular link).
  3. Try to open the associated email app. If it's installed, great. If it's not, nothing will happen. (Kind of a bad experience.)
  4. Try to open the associated email app. If it's installed, great. If the user taps it and nothing happens, wait 1 second and then do something else. (Not a great option either for several reasons.)

Previously, we did option 2. Now we do option 1.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 17, 2026

Walkthrough

Adds an isIos utility that detects iOS via navigator.userAgent, platform, and maxTouchPoints. Updates the magic-link-page component to suppress rendering of InboxLinkButton in three places when isIos(navigator) is true, falling back to the existing ActionButton behavior. Adds unit tests for isIos and two signup-flow tests asserting that inbox links are hidden on iOS. Bumps apps/portal package version from 2.64.3 to 2.64.4.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: hiding the inbox links button on iOS due to reliability concerns, which matches the core objective of the changeset.
Description check ✅ Passed The description is directly related to the changeset, explaining the reasoning behind hiding inbox links on iOS and referencing the issue being closed.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hide-inbox-links-on-ios

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.

@EvanHahn EvanHahn force-pushed the hide-inbox-links-on-ios branch from 9406da1 to 2b5c044 Compare February 17, 2026 22:14
Copy link
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.

🧹 Nitpick comments (1)
apps/portal/test/utils/is-ios.test.js (1)

3-19: No test exercises the /iPad/ or /iPod/ regex branches directly.

The regex in isIos covers iPad|iPhone|iPod, but the test suite only exercises the iPhone path (via IPHONE_UA) and the MacIntel + maxTouchPoints path. Adding a case with a pre-iPadOS 13 iPad UA (where iPad appears in the UA string) would give complete coverage of all three regex branches.

✨ Proposed additional test cases
 const IPHONE_UA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_6 like Mac OS X) ...';
+const IPAD_UA = 'Mozilla/5.0 (iPad; CPU OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1';
+const IPOD_UA = 'Mozilla/5.0 (iPod touch; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1';

 it('returns true for iOS', () => {
+    expect(isIos({userAgent: IPAD_UA, platform: 'iPad', maxTouchPoints: 5})).toBe(true);
+    expect(isIos({userAgent: IPOD_UA, platform: 'iPod', maxTouchPoints: 0})).toBe(true);
     expect(isIos({
         userAgent: IPHONE_UA,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/portal/test/utils/is-ios.test.js` around lines 3 - 19, Add unit tests to
exercise the remaining regex branches in isIos by adding cases where the
userAgent contains "iPad" and "iPod" (pre-iPadOS 13 style UAs) so the
/iPad|iPhone|iPod/ branch is covered directly; for each case call isIos with an
appropriate userAgent string (e.g., a pre-iPadOS UA including "iPad" and a UA
including "iPod") and set platform/maxTouchPoints to values that don't trigger
the MacIntel+touch fallback, then assert true (or false as appropriate) to
ensure both iPad and iPod paths are exercised.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/portal/test/utils/is-ios.test.js`:
- Around line 3-19: Add unit tests to exercise the remaining regex branches in
isIos by adding cases where the userAgent contains "iPad" and "iPod" (pre-iPadOS
13 style UAs) so the /iPad|iPhone|iPod/ branch is covered directly; for each
case call isIos with an appropriate userAgent string (e.g., a pre-iPadOS UA
including "iPad" and a UA including "iPod") and set platform/maxTouchPoints to
values that don't trigger the MacIntel+touch fallback, then assert true (or
false as appropriate) to ensure both iPad and iPod paths are exercised.

closes https://linear.app/ghost/issue/NY-1050

On iOS, we have four options for Inbox Links:

1. Don't show them at all.
2. Open the email inbox in the browser (a regular link).
3. Try to open the associated email app. If it's installed, great. If
   it's not, nothing will happen. (Kind of a bad experience.)
4. Try to open the associated email app. If it's installed, great. If
   the user taps it and nothing happens, wait 1 second and then do
   something else. (Not a great option either for several reasons.)

Previously, we did option 2. Now we do option 1.
@EvanHahn EvanHahn force-pushed the hide-inbox-links-on-ios branch from 2b5c044 to d0c297e Compare February 17, 2026 22:21
@EvanHahn EvanHahn requested a review from 9larsons February 17, 2026 22:22
Copy link
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.

🧹 Nitpick comments (1)
apps/portal/src/utils/is-ios.js (1)

10-10: Consider forward-compatible iPad detection using optional userAgentData fallback.

navigator.platform is unreliable and deprecated per MDN, but the current MacIntel + maxTouchPoints heuristic is pragmatically correct for iPadOS 13+ detection since Safari remains the primary target and doesn't support the modern navigator.userAgentData.platform API (Chromium-only as of 2025).

To future-proof when userAgentData gains wider browser support, you can optionally add it as a fast-path:

♻️ Optional forward-compatible fallback
 export const isIos = navigator => (
     /iPad|iPhone|iPod/.test(navigator.userAgent) ||
     // Checks for modern iPads (iPadOS) which mimic macOS
-    (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
+    // navigator.userAgentData is Chromium-only; navigator.platform is the
+    // fallback for Safari (deprecated but no universal replacement yet).
+    (
+        (navigator.userAgentData?.platform === 'macOS' || navigator.platform === 'MacIntel') &&
+        navigator.maxTouchPoints > 1
+    )
 );

Note: if adopting userAgentData, the JSDoc type should also be updated to include userAgentData in the Pick.

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

In `@apps/portal/src/utils/is-ios.js` at line 10, Update the iPad detection to
prefer a modern userAgentData fast-path: when available use
navigator.userAgentData.platform (or navigator.userAgentData?.platform) to
detect "iPad", and fall back to the existing (navigator.platform === 'MacIntel'
&& navigator.maxTouchPoints > 1) heuristic; also update the JSDoc Pick type for
the exported is-ios utility to include userAgentData so the linter/types know
the property exists (adjust the Pick to include "userAgentData" alongside
"platform" and "maxTouchPoints").
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/portal/src/utils/is-ios.js`:
- Line 10: Update the iPad detection to prefer a modern userAgentData fast-path:
when available use navigator.userAgentData.platform (or
navigator.userAgentData?.platform) to detect "iPad", and fall back to the
existing (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
heuristic; also update the JSDoc Pick type for the exported is-ios utility to
include userAgentData so the linter/types know the property exists (adjust the
Pick to include "userAgentData" alongside "platform" and "maxTouchPoints").

@EvanHahn EvanHahn merged commit 209bc2b into main Feb 18, 2026
31 checks passed
@EvanHahn EvanHahn deleted the hide-inbox-links-on-ios branch February 18, 2026 16:02
9larsons added a commit that referenced this pull request Feb 18, 2026
ref #26449

This PR was intended to bump Portal, however we have an issue with the CI job/checks that didn't seem to compare the branch to main.
9larsons added a commit that referenced this pull request Feb 18, 2026
ref #26449

This PR was intended to bump Portal, however the CI job that ran to
validate the Portal bump ran before the bump happened, leading to a
false pass.
betschki pushed a commit to magicpages/Ghost that referenced this pull request Feb 19, 2026
…ryGhost#26449)

closes https://linear.app/ghost/issue/NY-1050

On iOS, we have four options for Inbox Links:

1. Don't show them at all.
2. Open the email inbox in the browser (a regular link).
3. Try to open the associated email app. If it's installed, great. If
it's not, nothing will happen. (Kind of a bad experience.)
4. Try to open the associated email app. If it's installed, great. If
the user taps it and nothing happens, wait 1 second and then do
something else. (Not a great option either for several reasons.)

Previously, we did option 2. Now we do option 1.
betschki pushed a commit to magicpages/Ghost that referenced this pull request Feb 19, 2026
ref TryGhost#26449

This PR was intended to bump Portal, however the CI job that ran to
validate the Portal bump ran before the bump happened, leading to a
false pass.
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