Skip to content

feat: open channel previews from QR deeplinks#2305

Open
riderx wants to merge 8 commits into
mainfrom
codex/preview-channel-deeplink
Open

feat: open channel previews from QR deeplinks#2305
riderx wants to merge 8 commits into
mainfrom
codex/preview-channel-deeplink

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented May 20, 2026

What

  • Updates Capgo mobile channel-preview scanning to request the target appId and channel directly instead of switching the Capgo app channel.
  • Enables updater preview mode in the Capgo app config.
  • Adds backend support for preview: true update checks gated by apps.allow_preview.
  • Adds tests covering enabled and disabled preview update checks.

Why

  • Capgo mobile must be able to fully preview any preview-enabled app/channel from any user, not only channels from the Capgo app itself.

How

  • The scan flow calls getLatest({ appId, channel, preview: true }), downloads that bundle, then starts a preview session with the target app id.
  • Preview update checks bypass normal channel assignment/platform/prod/dev/device restrictions while still requiring allow_preview.
  • Preview update checks skip analytics/device stats so testing does not count as real app usage.

Testing

  • bun run lint:fix
  • bun run typecheck
  • bun run supabase:start
  • bun run supabase:with-env -- bunx vitest run tests/updates.test.ts (40 passed)

Not Tested

  • Manual mobile QR scan against a real second-user app/channel.

Summary by CodeRabbit

  • New Features

    • Deep-link support for channel previews so shared preview links open directly in the app (including improved universal link handling on iOS and Android).
    • QR codes and the scan/preview flow now open channel-specific previews when channel info is present.
    • Added a lightweight preview page that routes preview links into the in-app preview experience.
  • Bug Fixes / Behavior

    • Preview requests are gated by an app-level "allow preview" setting; blocked previews return a clear error.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds channel-preview deep-link contracts, registers native URL schemes and associated domains, installs a native deep-link handler, adds a /preview/channel landing route, extends the scanner to process preview links and start preview sessions, builds channel-specific QR deep links, and threads preview mode through backend queries, validation, and tests.

Changes

Channel preview deep-linking and QR support

Layer / File(s) Summary
Preview deep-link contract and builders
src/services/previewLinks.ts
Defines ChannelPreviewLink and implements buildChannelPreviewDeepLink and parseChannelPreviewDeepLink for /preview/channel URLs.
Native platform URL & associated-domain registration
android/app/src/main/AndroidManifest.xml, ios/App/App/App.entitlements, ios/App/App/Info.plist
Android manifest now lists explicit HTTPS applinks hosts and a separate capgo scheme intent filter; iOS entitlements add com.apple.developer.associated-domains; Info.plist registers capgo and ee.forgr.capacitor_go URL schemes.
Deep-link routing service
src/services/deepLinks.ts
Adds handleDeepLink and installDeepLinkHandler to parse incoming URLs and route channel preview links to /scan?preview=URL or console HTTPS URLs to their original path via Vue Router.
App init, route, and landing page
src/route-map.d.ts, src/main.ts, src/pages/preview/channel.vue, capacitor.config.ts
Adds typed route for /preview/channel, marks it guest-accessible in guestPath, installs the deep-link handler during app init, and provides a landing page that redirects native users to /scan with the current URL in preview query. Includes small Capacitor plugin config refactor used at init.
Channel-specific QR generation and UI wiring
src/components/BundlePreviewFrame.vue, src/pages/app/[app].channel.[channel].preview.vue
Adds optional channelName prop, computes qrCodeUrl using channel preview builder when available, regenerates QR on qrCodeUrl changes, and passes channel.name from the preview page.
Scanner preview processing & preview session
src/pages/scan.vue
On mount reads route.query.preview, parses preview deep links, and when present runs startChannelPreview to fetch, download, and apply the channel preview bundle and start a preview session; retains existing barcode scanning and URL download fallback.
Server preview gating and tests
supabase/functions/_backend/utils/*.ts, tests/updates.test.ts, tests/test-utils.ts
Threads preview flag through Postgres channel selection queries, adds apps.allow_preview column and AppOwnerPostgresResult.allow_preview, validates preview in request bodies, suppresses stats/permission checks in preview mode, and adds tests asserting preview-enabled vs preview-disabled behavior.

Sequence Diagram(s)

sequenceDiagram
  participant OS as Operating System
  participant DeepLinkService as src/services/deepLinks.ts
  participant Router as Vue Router
  participant Scanner as src/pages/scan.vue
  OS->>DeepLinkService: appUrlOpen event or launch URL
  alt Preview deep-link
    DeepLinkService->>Router: navigate /scan?preview=URL
    Router->>Scanner: route with preview param
  else Console HTTPS
    DeepLinkService->>Router: navigate original pathname+search+hash
  end
Loading
sequenceDiagram
  participant Scanner as src/pages/scan.vue
  participant Parser as src/services/previewLinks.ts
  participant Updater as CapacitorUpdater
  participant API as Capgo API
  Scanner->>Scanner: check route.query.preview
  alt Preview param exists
    Scanner->>Parser: parseChannelPreviewDeepLink(url)
    Parser-->>Scanner: ChannelPreviewLink
    Scanner->>API: getLatest({ preview: true }) for channel
    API-->>Scanner: bundle metadata
    Scanner->>Updater: download & set(bundle)
    Updater-->>Scanner: applied
    Scanner->>Updater: startPreviewSession()
  else No preview param
    Scanner->>Scanner: start barcode scanner
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Cap-go/capgo#1993: Overlaps backend channel-selection and preview handling changes to requestInfosPostgres/requestInfosChannelPostgres and related tests.
  • Cap-go/capgo#2031: Touches shared app-shell integration points and may overlap with client deep-link/QR wiring changes.
  • Cap-go/capgo#2301: Related edits to Supabase query helpers in supabase/functions/_backend/utils/pg.ts.

Poem

🐰 I hopped a deep-link through the night,
QR beacons guiding channel light.
Android, iOS, web all sing,
Scan, fetch, apply — previews spring.
A little rabbit cheers this linking flight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main feature: enabling channel preview opening via QR code deep links.
Description check ✅ Passed The PR description provides comprehensive What/Why/How sections, testing details, and known gaps, though the template checklist is incomplete.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 codex/preview-channel-deeplink

Comment @coderabbitai help to get the list of available commands and usage tips.

@riderx riderx marked this pull request as ready for review May 20, 2026 11:03
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

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 (1)
src/services/deepLinks.ts (1)

4-4: ⚡ Quick win

Use ~/ alias instead of relative import in src/ code.

Please switch this import to the project alias to match frontend import conventions.

Suggested fix
-import { parseChannelPreviewDeepLink } from './previewLinks'
+import { parseChannelPreviewDeepLink } from '~/services/previewLinks'

As per coding guidelines: "Import using ~/ alias for src/ directory in frontend code instead of relative paths".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/deepLinks.ts` at line 4, The import in deepLinks.ts currently
uses a relative path for parseChannelPreviewDeepLink; update the import to use
the project alias for src by replacing the relative import ("./previewLinks")
with the alias path ("~/services/previewLinks" or whichever alias maps to the
previewLinks module) so parseChannelPreviewDeepLink is imported via the ~/ root
import convention consistent with frontend code.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@android/app/src/main/AndroidManifest.xml`:
- Around line 28-30: The Android app-link intent filter is missing the staging
host declared by isCapgoConsoleHost(); add a new <data android:scheme="https"
android:host="console.staging.capgo.app" /> entry alongside the existing
console.capgo.app, console.dev.capgo.app, and console.preprod.capgo.app <data>
elements inside the app's intent-filter in AndroidManifest.xml so HTTPS deep
links for console.staging.capgo.app will be handed off to the app.

In `@ios/App/App/App.entitlements`:
- Around line 9-12: Add the missing staging associated-domain entry by inserting
the applinks domain string for staging (applinks:console.staging.capgo.app)
alongside the existing applinks entries (e.g., applinks:console.capgo.app,
applinks:console.dev.capgo.app, applinks:console.preprod.capgo.app) in the
App.entitlements so iOS universal links from staging will open the app; ensure
the new string follows the same XML <string>…</string> pattern as the others.

In `@src/pages/scan.vue`:
- Around line 97-103: The code sets the preview channel via
CapacitorUpdater.next() before confirming the preview will work; defer changing
the channel until after startPreviewSession()/getLatest() and any download
succeed, or if you must change it earlier, capture the original channel (via
CapacitorUpdater.current()/current.bundle.id) and ensure you call
CapacitorUpdater.setChannel()/CapacitorUpdater.next() to restore that original
channel in every error path (catch blocks) and before exiting the fallback
branch; update the logic around startPreviewSession(), getLatest(),
CapacitorUpdater.next(), and the error handlers so the preview channel is only
persisted on successful start and is restored on failure.

---

Nitpick comments:
In `@src/services/deepLinks.ts`:
- Line 4: The import in deepLinks.ts currently uses a relative path for
parseChannelPreviewDeepLink; update the import to use the project alias for src
by replacing the relative import ("./previewLinks") with the alias path
("~/services/previewLinks" or whichever alias maps to the previewLinks module)
so parseChannelPreviewDeepLink is imported via the ~/ root import convention
consistent with frontend code.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 64b9922e-537d-44de-85e6-07f50db563b1

📥 Commits

Reviewing files that changed from the base of the PR and between 7712cd5 and 4e2111a.

📒 Files selected for processing (11)
  • android/app/src/main/AndroidManifest.xml
  • ios/App/App/App.entitlements
  • ios/App/App/Info.plist
  • src/components/BundlePreviewFrame.vue
  • src/main.ts
  • src/pages/app/[app].channel.[channel].preview.vue
  • src/pages/preview/channel.vue
  • src/pages/scan.vue
  • src/route-map.d.ts
  • src/services/deepLinks.ts
  • src/services/previewLinks.ts

Comment thread android/app/src/main/AndroidManifest.xml
Comment thread ios/App/App/App.entitlements
Comment thread src/pages/scan.vue
@riderx riderx force-pushed the codex/preview-channel-deeplink branch from 4e2111a to e436e42 Compare May 20, 2026 11:22
@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq Bot commented May 20, 2026

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing codex/preview-channel-deeplink (72870d4) with main (ebf5e04)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/updates.test.ts`:
- Line 587: Change the two new tests from synchronous `it()` to parallel
`it.concurrent()` to follow the repo's parallel-test convention: locate the test
with the description "allows preview requests to fetch a private target channel
when app preview is enabled" and replace `it(` with `it.concurrent(`, and do the
same for the other new test referenced in the comment (around line 646) so both
run concurrently within the same file.
- Around line 618-643: The test flips shared app state by setting allow_preview
to true then later resetting it; wrap the mutable section (the call that sets
allow_preview true, the setup of baseData, the call to postUpdate and assertions
using postUpdate and parseSchema/updateNewScheme) in a try/finally so the final
supabase update that sets allow_preview back to false always runs even if
assertions throw; locate the two supabase.update calls referencing
APP_NAME_UPDATE and move the second one into a finally block, keeping
throwOnError on both updates to surface errors.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 883969bd-2731-44e8-bd42-df73dc42ebbf

📥 Commits

Reviewing files that changed from the base of the PR and between e436e42 and 381f781.

📒 Files selected for processing (10)
  • capacitor.config.ts
  • src/pages/scan.vue
  • supabase/functions/_backend/utils/pg.ts
  • supabase/functions/_backend/utils/plugin_parser.ts
  • supabase/functions/_backend/utils/plugin_validation.ts
  • supabase/functions/_backend/utils/postgres_schema.ts
  • supabase/functions/_backend/utils/types.ts
  • supabase/functions/_backend/utils/update.ts
  • tests/test-utils.ts
  • tests/updates.test.ts

Comment thread tests/updates.test.ts Outdated
Comment thread tests/updates.test.ts Outdated
@riderx riderx deployed to deepsec-pr May 20, 2026 18:01 — with GitHub Actions Active
@sonarqubecloud
Copy link
Copy Markdown

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.

1 participant