Skip to content
/ app-polo Public

Add deep-linking for SOTAcat integration#198

Open
jeffkowalski wants to merge 5 commits intoham2k:mainfrom
jeffkowalski:feature/deep-linking-sotacat
Open

Add deep-linking for SOTAcat integration#198
jeffkowalski wants to merge 5 commits intoham2k:mainfrom
jeffkowalski:feature/deep-linking-sotacat

Conversation

@jeffkowalski
Copy link

Summary

Adds deep-link handling so companion apps like SOTAcat can send spotted stations directly into Polo for logging. A deep link pre-fills a suggested QSO with frequency, mode, and reference info, navigating the user straight to the logging screen.

URL format

com.ham2k.polo://qso?<parameters>

Example:
com.ham2k.polo://qso?myRef=W6/NC-371&mySig=sota&theirRef=W6/CT-006&theirSig=sota&freq =7032000&mode=CW&theirCall=N0CALL

Parameters (at least one ref pair required):

  • myRef / mySig — our activation reference and type (finds or creates a matching operation)
  • theirRef / theirSig — their reference and type (added as a hunt ref on the QSO)
  • freq — frequency in Hz (converted to kHz internally)
  • mode — operating mode (CW, SSB, etc.)
  • theirCall / myCall — callsigns
  • time — timestamp in ms

Supported types: sota, pota, wwff, gma, wca, zlota

Changes

New files:

  • src/deep-linking/DeepLinkUtils.js — pure functions: parseDeepLinkURL(), buildSuggestedQSO(), frequency Hz-to-kHz conversion
  • src/deep-linking/DeepLinkHandler.jsuseDeepLinkHandler() hook with cold/warm start handling, findOrCreateOperation(), DeepLinkListener component
  • src/deep-linking/DeepLinkHandler.spec.js — 51 unit tests covering URL parsing, type mapping, frequency conversion, and QSO building
  • src/deep-linking/index.js — barrel exports

Modified files:

  • AndroidManifest.xml — intent-filter for com.ham2k.polo scheme with android:host="qso" (scoped to avoid catching OAuth redirects)
  • App.jsx — mount <DeepLinkListener /> inside NavigationContainer
  • OperationScreen.jsx — inject suggested QSO into logging state via Redux with _replace semantics; update VFO with deep-link freq/mode/band
  • OpLoggingTab.jsx — split-view suggested QSO handling deferred to OperationScreen
  • LoggingPanel.jsx — prevent queuing blank QSOs when a suggested QSO is arriving; suggestedQSO excluded from closure spread in setQSO to prevent stale
    reintroduction; queue copy-before-pop to avoid Redux mutation
  • uiSlice.js_replace marker support in deepMergeState for atomic state replacement

Tests performed

  • Deep link with myRef — verify matching operation is found or created
  • Deep link with theirRef only (chase mode) — verify generic operation created
  • Verify freq shows correctly (Hz input → kHz internally → MHz display)
  • Verify mode derived from frequency when not provided, and explicit mode overrides
  • Enter callsign on suggested QSO, submit — verify next blank QSO keeps deep-link freq
  • Wipe suggested QSO (backspace without submitting) — verify new blank QSO keeps deep-link freq
  • Non-deep-link flow: manually change freq, edit old QSO, come back — verify queued QSO preserved
  • OAuth login still works (intent-filter doesn't intercept OAuth redirects)
  • Second deep link while first is still on screen — verify it replaces cleanly

Add deep-linking URL scheme to enable companion app integration
(e.g., SOTAcat) for chase, spot, and S2S scenarios.

URL scheme: com.ham2k.polo://qso?params...

Parameters (all camelCase, my/their prefix for clarity):
- myRef: Our activation reference (e.g., K-1234)
- mySig: Our activation type (sota, pota, wwff, gma, wca, zlota)
- myCall: Our callsign
- theirRef: Their activation reference (e.g., W6/CT-006)
- theirSig: Their activation type
- theirCall: Their callsign
- freq: Frequency in Hz
- mode: Operating mode (CW, SSB, etc.)
- time: Timestamp in milliseconds

At least one ref pair (myRef+mySig or theirRef+theirSig) required.

Use cases:
1. Chase: theirRef+theirSig only - creates generic operation
2. Spot: myRef+mySig only - opens/creates matching activation
3. S2S/P2P: both ref sets - opens our activation, pre-fills their ref

Files:
- DeepLinkUtils.js: Pure functions (parseDeepLinkURL, buildSuggestedQSO)
- DeepLinkHandler.js: React hooks (useDeepLinkHandler, DeepLinkListener)
- DeepLinkHandler.spec.js: 44 unit tests
- App.jsx: Mounts DeepLinkListener in NavigationContainer
- AndroidManifest.xml: Intent-filter for com.ham2k.polo scheme

Includes commented placeholder for IOTA (not yet supported in Polo).

Tested with: npm test -- --testPathPattern=DeepLinkHandler
- Prevent re-processing of initial cold-start URL when
  handleDeepLink dependencies change
- Add _replace marker support in deepMergeState for clean
  object replacement instead of recursive merging
- Fix queue mutation bug (was mutating Redux state directly)
- Exclude suggestedQSO from closure spread to prevent stale data
- Use hunting types (pota, sota) instead of activation types
  for chase QSO refs
SOTAcat sends frequency in Hz, but app stores internally in kHz.
parseFrequency() now divides by 1000 to convert properly.

Fixes incorrect ADIF exports (7245.000000 → 7.245000 MHz)
and wrong band derivation (submm → 40m).
Add host="qso" to deep-link intent-filter so
MainActivity only handles qso:// URLs, leaving
sota:// for OAuth RedirectUriReceiverActivity.
When a deep link set freq/mode, the first QSO used it correctly but
submitting reverted to the previous freq. This happened because the
blank QSO (with stale freq) was queued when switching to the deep-linked
QSO, then popped back after submit, overriding the new VFO.

Two fixes:
1. Don't queue blank QSOs when a deep-linked suggested QSO is
arriving (the blank has no user data worth preserving)
2. Update VFO Redux state when a deep link arrives so the freq persists
even if the user wipes the suggested QSO
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