fix: share-residence import preview polish (closes #7) #9

Merged
admin merged 5 commits from fix/7-share-residence-import-polish into master 2026-05-11 16:17:16 -05:00
Owner

Closes #7.

Four polish fixes to the iOS QuickLook preview recipients see when they open a .honeydue residence invite (AirDrop / Save to Files / Share Extension).

1. Filename: keep spaces and apostrophes

HoneyDueShareCodec.safeShareFileName was replacing every space with an underscore, so the system title bar rendered The_Tartt's instead of The Tartt's (gitea#7 screenshot). Now we strip only the characters that are actually unsafe on iOS / Android filesystems (/, \, :, *, ?, ", <, >, |, non-whitespace control codepoints) and collapse internal whitespace to single spaces.

Locked in by HoneyDueShareCodecTest (commonTest, 6 cases): keep spaces / strip unsafe chars / collapse whitespace / 50-char cap / blank-input fallback / drop control chars.

2. Icon: brand logo instead of house.fill

PreviewViewController.updateUIForResidence previously used UIImage(systemName: "house.fill") — visually generic, didn't say "HoneyDue" at a glance. New HoneyDueQLPreview/Assets.xcassets/AppLogo.imageset/ holds the app logo @2x; the controller renders it with withRenderingMode(.alwaysOriginal) so its real colors come through. SF Symbol fallback is retained for any asset-load failure.

3. Expires-at: human-readable phrase

Was: Expires: 2026-05-12T17:11:02.067272789Z. Now: Expires in 23 hours / Expires on May 12, 2026 at 5:11 PM (depending on how close the lapse is), and Expires expired 2 hours ago for already-stale links. Implementation uses ISO8601DateFormatter (with + without fractional-seconds) for parsing and RelativeDateTimeFormatter / DateFormatter for output. Falls back to the raw string on parse failure so nothing ever goes blank.

4. Instructions: numbered, explicit, action-clear

Was:

Tap the share button below, then select "honeyDue" to join this residence.

(Pointed at the wrong location — the share button is at the top of the QuickLook chrome, not below — and assumed the recipient already recognises the share affordance.)

Now:

How to join:

  1. Tap the Share button (top right of this preview)
  2. Choose "honeyDue" from the share sheet
  3. Sign in if prompted — the app finishes the rest

Verification

  • xcodebuild build -scheme HoneyDueQLPreview → BUILD SUCCEEDED
  • xcodebuild build -scheme HoneyDue (includes Kotlin framework) → BUILD SUCCEEDED
  • ./gradlew :composeApp:testDebugUnitTest --tests HoneyDueShareCodecTest → all 6 cases green
Closes #7. Four polish fixes to the iOS QuickLook preview recipients see when they open a `.honeydue` residence invite (AirDrop / Save to Files / Share Extension). ## 1. Filename: keep spaces and apostrophes `HoneyDueShareCodec.safeShareFileName` was replacing every space with an underscore, so the system title bar rendered `The_Tartt's` instead of `The Tartt's` (gitea#7 screenshot). Now we strip only the characters that are actually unsafe on iOS / Android filesystems (`/`, `\`, `:`, `*`, `?`, `"`, `<`, `>`, `|`, non-whitespace control codepoints) and collapse internal whitespace to single spaces. Locked in by `HoneyDueShareCodecTest` (commonTest, 6 cases): keep spaces / strip unsafe chars / collapse whitespace / 50-char cap / blank-input fallback / drop control chars. ## 2. Icon: brand logo instead of `house.fill` `PreviewViewController.updateUIForResidence` previously used `UIImage(systemName: "house.fill")` — visually generic, didn't say "HoneyDue" at a glance. New `HoneyDueQLPreview/Assets.xcassets/AppLogo.imageset/` holds the app logo @2x; the controller renders it with `withRenderingMode(.alwaysOriginal)` so its real colors come through. SF Symbol fallback is retained for any asset-load failure. ## 3. Expires-at: human-readable phrase Was: `Expires: 2026-05-12T17:11:02.067272789Z`. Now: `Expires in 23 hours` / `Expires on May 12, 2026 at 5:11 PM` (depending on how close the lapse is), and `Expires expired 2 hours ago` for already-stale links. Implementation uses `ISO8601DateFormatter` (with + without fractional-seconds) for parsing and `RelativeDateTimeFormatter` / `DateFormatter` for output. Falls back to the raw string on parse failure so nothing ever goes blank. ## 4. Instructions: numbered, explicit, action-clear Was: > Tap the share button below, then select "honeyDue" to join this residence. (Pointed at the wrong location — the share button is at the *top* of the QuickLook chrome, not below — and assumed the recipient already recognises the share affordance.) Now: > **How to join:** > 1. Tap the Share button (top right of this preview) > 2. Choose "honeyDue" from the share sheet > 3. Sign in if prompted — the app finishes the rest ## Verification - `xcodebuild build -scheme HoneyDueQLPreview` → BUILD SUCCEEDED - `xcodebuild build -scheme HoneyDue` (includes Kotlin framework) → BUILD SUCCEEDED - `./gradlew :composeApp:testDebugUnitTest --tests HoneyDueShareCodecTest` → all 6 cases green
admin added 1 commit 2026-05-11 13:07:28 -05:00
fix: share-residence import preview polish (closes gitea#7)
Android UI Tests / ui-tests (pull_request) Has been cancelled
5aa31153e3
Issue #7 called out four problems with the QuickLook preview iOS
recipients see when they open a `.honeydue` invite (e.g. via AirDrop or
Save to Files). All four fixed here.

1. Filename: keep spaces and apostrophes
   `HoneyDueShareCodec.safeShareFileName` previously replaced every space
   with an underscore, so the system title bar rendered "The_Tartt's"
   instead of "The Tartt's". Now we strip only the characters that are
   actually unsafe on iOS / Android filesystems (`/`, `\`, `:`, `*`,
   `?`, `"`, `<`, `>`, `|`, non-whitespace control codepoints) and
   collapse internal whitespace to single spaces. Locked in with six
   new commonTest cases.

2. Icon: brand logo instead of generic house glyph
   `PreviewViewController.updateUIForResidence` was using
   `UIImage(systemName: "house.fill")` — recipients couldn't tell at a
   glance that this was a HoneyDue invite. The honeyDue app logo
   (Assets.xcassets/AppLogo) is now loaded from a new asset catalog in
   the QL preview bundle and rendered in original colors. SF Symbol
   fallback retained for any asset-load failure.

3. Expires-at: human-readable phrase, not a raw ISO timestamp
   The previous "Expires: 2026-05-12T17:11:02.067272789Z" line is now
   formatted via `RelativeDateTimeFormatter` for invites that lapse
   within a day ("in 5 hours") and a localized medium-date + short-time
   string ("on May 12, 2026 at 5:11 PM") otherwise. Already-expired
   links render "expired 2 hours ago". Falls back to the raw string if
   ISO parsing fails so nothing ever goes blank.

4. Instructions: numbered, explicit, action-clear
   The single-line "Tap the share button below, then select..." copy
   pointed at the wrong location (the share button is at the top of
   the QuickLook chrome, not "below") and assumed the recipient
   recognised the share affordance. Replaced with a three-step list.

Tests: new `HoneyDueShareCodecTest` (commonTest, 6 cases) covers the
filename contract end-to-end — passes on the JVM unit-test target.
No iOS unit test for the date formatter because the SDK helpers it
uses (`RelativeDateTimeFormatter`, `ISO8601DateFormatter`) are
deterministic enough to spot-check by hand.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
admin added 1 commit 2026-05-11 13:44:31 -05:00
test(qlpreview): screenshot of the post-fix residence-invite preview (gitea#7)
Android UI Tests / ui-tests (pull_request) Has been cancelled
d26714f043
Adds a one-shot SnapshotTesting case that renders the new
`PreviewViewController.updateUIForResidence` layout on the iPhone-13
simulator with deterministic data ("The Tartt's", expiry exactly 23h
in the future). The PNG it writes is what gets attached to issue #7
so reviewers can see the post-fix look without AirDropping a
`.honeydue` file to a device.

`MockPreviewViewController` mirrors the production UIKit layout
1:1 — same colors, fonts, constraints, image asset. (The QL extension
target itself can't be `@testable import`ed from HoneyDueTests
without project-file surgery; the mirror is a pragmatic faithful copy
so we get a real on-simulator render via SnapshotTesting.)

The included PNG is the recorded golden.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
admin added 1 commit 2026-05-11 13:47:01 -05:00
fix(qlpreview): inline share icon instead of fixed position (gitea#7 review)
Android UI Tests / ui-tests (pull_request) Has been cancelled
f4c2780e34
The previous copy "1. Tap the Share button (top right of this preview)"
named a position that's wrong on iOS file-preview chrome (the share
button is at the BOTTOM, not the top), and may move across iOS
versions / contexts (mail attachment vs Files vs AirDrop).

Switch the instruction to an attributed string that inlines the
universal iOS share glyph (SF Symbol `square.and.arrow.up`) next to
"Tap" — the recipient finds the right control by sight regardless of
where the chrome puts it. New `PreviewViewController.makeResidenceInstructions()`
builds the attributed string with the glyph attachment vertically
aligned to the body-text baseline.

`Issue7PreviewScreenshotTest` mirrors the new builder so the recorded
PNG attached to the gitea issue stays in sync with production.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
admin added 1 commit 2026-05-11 13:58:00 -05:00
fix(qlpreview): expired-state copy + dedicated row text (gitea#7 review)
Android UI Tests / ui-tests (pull_request) Has been cancelled
83c3428b05
When the share link's expiry is in the past, the preview now
swaps the "How to join" steps for a dead-end message ("This
invite has expired. Ask <sender> to send a new link.") and
re-words the clock row to "Expired 1 hour ago" so users don't
see share-sheet directions for a link the server will reject.

Also adds an expired-state snapshot test alongside the existing
active-state one.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
admin added 1 commit 2026-05-11 15:21:59 -05:00
fix(qlpreview): hide share-arrow in expired state (gitea#7 review)
Android UI Tests / ui-tests (pull_request) Has been cancelled
0b6f26da99
The down-chevron above the system Share button is a "tap here"
cue for the active flow. In the expired state there's nothing
worth sharing (the bundled code will be rejected on import) so
the arrow is misleading; hide it whenever we render the
"This invite has expired" message.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
admin merged commit f364ab05dc into master 2026-05-11 16:17:16 -05:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/honeyDueKMP#9