Skip to content

feat: finish v1 — Tag/Nexus, binary FFI, UX gaps, unit tests, iOS wired - #3

Merged
jvsena42 merged 8 commits into
mainfrom
feat/finish-implementation
Jun 13, 2026
Merged

feat: finish v1 — Tag/Nexus, binary FFI, UX gaps, unit tests, iOS wired#3
jvsena42 merged 8 commits into
mainfrom
feat/finish-implementation

Conversation

@jvsena42

Copy link
Copy Markdown
Owner

Summary

Finishes the v1 implementation across shared logic, Android, and iOS, adds business-logic unit tests, wires the iOS app end-to-end, and adopts the extended Pubky FFI surface.

Shared / data

  • Binary FFI surface: PubkyClient gains putBytes/getBytes/putBytesWithSession, paginated list, and createTagId. Media blobs are now written raw to the homeserver (was Base64-in-a-String); reads are Base64-decoded from the FFI transport.
  • TagRepository implemented (TagRepositoryImpl): writes pubky-app-specs tag records (/pub/pubky.app/tags/{id}, content-derived id via createTagId) so Pubky Nexus indexes Echo decks network-wide; reads trending from the Nexus REST API (data/nexus/NexusClient over a tiny per-platform HttpFetcher — no HTTP library added to shared). Deck publish/delete maintain tag records.
  • Deck delete now sweeps the whole deck directory (cards, media, SRS) via list instead of orphaning blobs.
  • Discover shows a "Trending on Pubky" chip row.

UX gaps (Android)

  • Foreign-deck loading (Discover → deck no longer dead-ends), owner-only edit/delete with confirmation.
  • 10s publish-undo window (spec §5.6), deck/card input validation.
  • Settings screen (identity, copy pubky, homeserver, version, sign out); session-expiry recovery routes to onboarding instead of dead error states.
  • Pull-to-refresh on Home/Decks/Discover; mastered-% computed from SRS intervals.

iOS — wired end-to-end

  • Swift RawPubkyClient pass-through + Kotlin IosPubkyClientAdapter bridge (Swift can't implement kotlin.Result/suspend), Koin bootstrap enabled, FlowObserver/IosFlowWatcher for SwiftUI without SKIE. Onboarding + Home/Decks/DeckDetail/DeckEditor screens driven by the shared ViewModels. Builds and launches on the iPhone 16 simulator showing the VM-driven onboarding.
  • Two real iOS bugs fixed: Log.ios.kt used NSLog("%@", …) with Kotlin String varargs, which segfaults Kotlin/Native (crashed the app on launch and every iOS ViewModel test) → switched to println; duplicate AddTagSheet.

Tests

  • commonTest: FakePubkyClient + fakes, repository tests (Tag/Deck/Card/Media/Srs/Discovery), NexusClient, and ViewModel tests. 117 JVM tests + the iOS variant green via ./gradlew :shared:allTests.

Pubky Ring

  • Ring auth is wired and the UI flow is verified end-to-end on Android (Echo → Ring → authorize → "Authorization Successful"). Obtaining a live session is currently blocked by an upstream pkarr/pubky-core TLS path on the emulator — see journeys/RESULTS.md. Six Android journey specs are authored under journeys/.

Depends on

The regenerated bindings come from pubky-core-ffi-fork (feat/port-missing-methods): put_bytes/get_bytes, put_bytes_with_session, list pagination, and create_tag_id. That FFI work is PR'd separately.

Test plan

  • ./gradlew :shared:allTests — green (JVM + iOS).
  • ./gradlew :composeApp:assembleDebug — green; installs and runs on the emulator.
  • iOS: xcodebuild … -scheme iosApp — green; launches on the iPhone 16 simulator.

🤖 Generated with Claude Code

jvsena42 and others added 8 commits June 12, 2026 18:48
…n Discover

- regenerate pubky bindings (put_bytes/get_bytes, put_bytes_with_session,
  list pagination, create_tag_id)
- media blobs now stored raw on the homeserver instead of Base64 text
- pubky.app tag records written/removed on deck publish/delete so Nexus
  indexes Echo decks network-wide
- deck delete sweeps the whole deck dir (cards, media, srs) via list
- DiscoverViewModel merges Nexus trending tags after first paint
- fix IosDependencies Koin resolution (GlobalContext does not resolve on
  Kotlin/Native; use KoinPlatform.getKoin())

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…recovery

- deck detail loads foreign decks via (deckId, author) route param; owner-only
  edit/delete affordances; delete with confirmation sweeping the homeserver
- 10s publish undo window per spec §5.6 (countdown, undo restores the draft)
- input validation: deck title/description and card text max lengths
- settings screen (identity, copy pubky, homeserver, version, sign out)
- session-expiry recovery: terminal re-auth failures sign out to onboarding
  instead of dead error states (Home/Profile)
- pull-to-refresh on Home, Decks, Discover; trending tag empty state
- mastered % computed from SRS mature intervals (was a placeholder)
- Android journey specs for the six main flows under journeys/

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Shared business-logic tests (commonTest): FakePubkyClient/fakes + repository
tests (Tag/Deck/Card/Media/Srs/Discovery), NexusClient, and ViewModel tests
(Home/Study/Discover/PublishDeck). 117 JVM tests + the iOS variant green via
:shared:allTests.

iOS: Swift RawPubkyClient pass-through + Kotlin IosPubkyClientAdapter bridge;
Koin bootstrap enabled in iOSApp.swift; FlowObserver/IosFlowWatcher for
SwiftUI without SKIE; OnboardingScreen + Home/Decks/DeckDetail/DeckEditor
screen wrappers wired to shared ViewModels. App builds and launches on the
iPhone 16 simulator showing the VM-driven onboarding.

Two real iOS bugs fixed:
- Log.ios.kt used NSLog with %@ and Kotlin String varargs, which segfaults
  Kotlin/Native (EXC_BAD_ACCESS in objc_opt_respondsToSelector). This crashed
  the app on launch and every iOS ViewModel unit test. Switched to println.
- Duplicate AddTagSheet declaration; renamed the inline one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Journey 01's Ring auth UI verified end-to-end through Ring's authorization;
02-06 blocked on a live session by an upstream pkarr/pubky-core TLS path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- regenerate the arm64 pubkycore .so with the patched pubky SDK (bundled webpki
  roots for ICANN TLS); this fixes the rustls-platform-verifier panic that made
  every Ring sign-in fail with 'auth request expired'. Sign-in now completes
  end-to-end on the emulator against the staging identity.
- append Pubky Ring return-callbacks (x-success/x-cancel/x-error/x-source ->
  echo://login-callback) to the auth URL so Ring re-opens Echo after approval,
  per Ring's own xCallback convention
- MainActivity launchMode=singleTask so the callback refocuses the running
  instance instead of spawning a new one

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-end

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Enable testTagsAsResourceId at the app root so every Modifier.testTag surfaces
as a UiAutomator/adb resource-id, and tag the main-flow elements (onboarding,
tabs, home, decks, paste/publish, deck detail, study, discover, profile,
settings). Annotate the journey XMLs to target elements by resource-id instead
of pixel position. Verified the ids appear in the layout tree (uiautomator dump).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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