a love letter to tangled (android, iOS, and a search API)
at main 100 lines 4.6 kB view raw view rendered
1--- 2title: Mobile App Reference 3updated: 2026-03-25 4--- 5 6Twisted is an Ionic Vue mobile app for browsing Tangled, a git hosting platform built on the AT Protocol. It targets iOS and Android via Capacitor (no web target). 7 8## Tech Stack 9 10- **Vue 3** with TypeScript and Composition API 11- **Ionic Vue** for native-feeling UI components 12- **Capacitor** for iOS/Android builds 13- **Pinia** for state management 14- **TanStack Query** for async data with caching 15- **@atcute/client** and **@atcute/tangled** for AT Protocol XRPC 16 17TypeScript files use `.js` extensions in imports. Package management via pnpm. 18 19## Architecture 20 21Three-layer design: 22 23**Presentation** — Vue components and pages using Ionic's component library. Five-tab navigation: Home, Explore, Activity, Bookmarks/Profile, Settings. Repo detail uses segmented tabs: Overview, Files, Issues, PRs. 24 25**Domain** — TypeScript types modeling the app's data: UserSummary, RepoSummary, RepoDetail, RepoFile, PullRequestSummary, IssueSummary, ActivityItem. These are app-internal representations, decoupled from API response shapes. 26 27**Data** — Service layer that fetches from external sources and normalizes into domain types. The flow is: Vue component → composable → TanStack Query hook → service function → XRPC call → normalizer → domain model. 28 29## Directory Structure 30 31```sh 32src/ 33 app/ — App shell, router, global config 34 core/ — Shared utilities, constants 35 services/ — API clients and data fetching 36 atproto/ — @atcute client setup, error handling 37 tangled/ — Endpoints, normalizers, TanStack Query hooks 38 domain/ — TypeScript type definitions 39 features/ — Feature modules (home, explore, repo, etc.) 40 components/ — Shared UI components 41``` 42 43## Data Sources 44 45The app reads from multiple sources depending on what's needed: 46 47- **Knots** (Tangled XRPC servers) — Git data: file trees, blobs, commits, branches, diffs. Each repo is hosted on a specific knot. 48- **PDS** (Personal Data Servers) — AT Protocol records: profiles, issues, PRs, comments, stars, follows. Accessed via `com.atproto.repo.getRecord` and `com.atproto.repo.listRecords`. 49- **Twister API** — Search and index-backed summaries (when available). 50- **Constellation** — Social signal counts and backlinks (stars, followers, reactions). 51 52The app calls the Twister API for app data. Twister proxies knot and PDS reads, 53handle resolution, Constellation counts, and the Jetstream activity stream. 54 55## Completed Features 56 57### Navigation & Shell (Phase 1) 58 59Five-tab layout with Vue Router, skeleton loaders, placeholder pages, and a 60local Bookmarks area for repos, strings, and saved files. 61 62### Public Browsing (Phase 2) 63 64All release-mode browsing works without authentication: 65 66**Repository browsing** — Metadata display, README rendering (markdown), file tree navigation, file viewer with syntax context, commit log with pagination, branch listing. 67 68**Profile browsing** — Avatar, bio, links fetched from PDS. User's repos listed. 69 70**Issues** — List view with open/closed state filter, detail view with threaded comments. 71 72**Pull Requests** — List view with status filter (open/closed/merged), detail view with comments. 73 74**Caching** — TanStack Query configured with per-data-type stale times. 75Persistence currently uses IndexedDB via `idb-keyval`, with a separate local 76bookmarks store for saved repos, strings, files, and READMEs. 77 78## Storage Migration 79 80IndexedDB is the current implementation for cache and bookmark persistence, but 81it should be treated as an interim step. 82 83- Local cache and offline-content storage should migrate toward SQLite so larger 84 saved payloads, better inspection, and more explicit schema management are 85 available on-device. 86- Sensitive auth/session material should migrate toward Ionic secure storage 87 instead of living in browser-style storage primitives. 88- Until that migration lands, IndexedDB should stay limited to non-sensitive 89 cached data and user-saved offline reading content. 90 91## Auth Split 92 93Production builds are read-only and hide auth entry points. Dev builds keep the 94current OAuth flow available for testing future authenticated features. 95 96## Routing 97 98The app resolves identities through AT Protocol: handle → DID (via PDS resolution) → records. For repo git data, the knot hostname is extracted from the repo's DID document. 99 100Home tab currently provides direct handle-based browsing: enter a known handle to view their profile and repos. This works without any index or search dependency.