fix(calendar): make URLs in the event description tappable - #53
Open
lucletoffe wants to merge 1 commit into
Open
fix(calendar): make URLs in the event description tappable#53lucletoffe wants to merge 1 commit into
lucletoffe wants to merge 1 commit into
Conversation
A meeting invitation puts the join URL in the description - Teams, Meet, Zoom and the plain iTIP fallbacks all do - and the event detail sheet rendered it as bare <Text>. The one actionable thing in the event could not be tapped: you had to select the URL by hand and paste it into a browser. The mail body already linkifies (plainTextToSafeHtml, rendered in the WebView); only the calendar was left out. `splitTextLinks` (lib/linkify-text) splits the text into runs and marks the http(s) URLs, using the same rule as plainTextToSafeHtml so a message linkifies identically in the body and in the calendar. DetailRow gains an opt-in `linkify` prop - set on the description row only, not on rows the app composed itself - and hands each URL to `openExternalUrl` with `confirm: true`, the same guard the "Open link" button on the video row already uses: scheme allow-list plus the host confirmation, since this text arrives in someone else's data. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018QPPAw4SK4M9Z7euVPM2cL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
A meeting invitation carries the join URL in the event description - Teams, Meet, Zoom and the plain iTIP fallbacks all do - and the event detail sheet renders it as bare
<Text>. The one actionable thing in the event cannot be tapped: you have to select the URL by hand and paste it into a browser.The mail body already linkifies (
plainTextToSafeHtml, rendered in the WebView); only the calendar was left out. Same gap as bulwarkmail/webmail#968, which fixes the web client's three description renders.The change
src/lib/linkify-text.ts-splitTextLinks(text)splits plain text into runs and marks the http(s) URLs, deliberately using the same rule asplainTextToSafeHtmlinlib/email-html(/(https?:\/\/[^\s<>"']+)/g), so a message linkifies identically in the body and in the calendar. The URL terminating at<>also means the<https://...>form mail clients emit yields a bare URL, not one with a trailing bracket.DetailRowgains an opt-inlinkifyprop, set on the description row only - never on rows the app composed itself.openExternalUrl(url, { confirm: true }), the same guard the "Open link" button on the video row already uses: scheme allow-list plus the host confirmation, since this text arrives in someone else's data. The regex is only a hint about where a link starts and ends;open-urlremains the thing that decides whether it may be opened.Checks
tsc --noEmitclean.vitest run: 972 passed, 14 skipped, 0 failures.src/lib/__tests__/linkify-text.test.ts(6 tests): URL marked with surrounding text preserved,<https://...>termination, several URLs across lines, non-http schemes left alone (javascript:,file:,tel:), no characters lost when reassembling, and nolastIndexleak between calls on the module-level regex.<Text>inside the existingDetailRow.