Vectrum
Sign In
92 lines · 5.5 KB
# Android app

`apps/mobile/` is Vectrum for Android, in Kotlin with Jetpack Compose. It looks like the website: MetalUI's
bone and graphite colorways, raised plates and sunk wells, Geist and Geist Mono, engraved mono labels.

What it does:

- **Home**: a greeting, counts of your repositories, open issues and pull requests, your repositories, and the
  event feed of everything that happened in them (pushes, issues, reviews, workflow runs)
- **Explore**: search the repositories you can see
- **Repositories**: README, files and folders on any branch or tag, code with syntax highlighting and line
  numbers (or wrapped), images, Markdown previews, clone addresses
- **Issues**: grouped by status like the website's list, with Active / Backlog / All / Closed scopes; open one,
  comment, and (with push access) change its status, priority, assignee and labels
- **Pull requests**: conversation, reviews and line comment threads, commits, the diff of every file, checks
  and merge requirements; comment, approve or request changes, merge (merge commit or squash), close and reopen
- **Commits**: history by day with CI status, and each commit's diff
- **Actions**: workflow runs, their jobs and steps, and each step's log, live while it runs
- **Releases**: notes and files to download
- Light, dark or following the system, like the website's theme switch

## Building

Needs JDK 17+ and the Android SDK (platform 37, build tools 36). With `ANDROID_HOME` set (or `sdk.dir` in
`apps/mobile/local.properties`):

```bash
cd apps/mobile
./gradlew assembleDebug          # app/build/outputs/apk/debug/app-debug.apk
./gradlew installDebug           # onto a connected device or emulator
./gradlew assembleRelease        # minified, signed with the debug key; sign with your own key to publish
```

From the repository root, `bun run mobile:build` and `bun run mobile:deploy` do the first two.

The app runs on Android 8.0 (API 26) and newer.

## Setting up

The first time it opens, the app walks through three steps:

1. **Welcome**: what Vectrum is and what the app does.
2. **Server**: *Vectrum Cloud* (vectrum.cc) or *Self-hosted*, with the server's address
   (`git.example.com`, `192.168.1.10:3000`). Plain HTTP is fine; without `http://` or `https://`, HTTPS is
   tried first, then HTTP. The app checks that the server is a Vectrum new enough for the app before going on.
3. **Account**: sign in with a username or email and password (and a code from your authenticator app if
   two-factor authentication is on), or create an account when the server takes sign-ups (not with `SIGNUPS_DISABLED`).

After signing out, the app opens on the account step for the same server, with *Change* to pick another.
From the Android emulator, the computer running `bun dev` is `http://10.0.2.2:3000`.

The app gets the same 30-day session token the website keeps in its cookie, and stores it in the app's private
storage; *Sign Out* forgets it. Changing your password ends the session, as on the website. Plain `http://` is
allowed for self-hosted servers on a local network; the sign-in screen warns before a password goes to a
non-local server without HTTPS.

## The API

The app talks to `/api/v1` (`apps/web/app/api/v1/[...path]/route.ts`, `apps/web/lib/app-api.ts`): JSON, with the session token
as `Authorization: Bearer <token>`. It acts as the signed-in person and applies the website's permission
checks, so the app can't do anything the website wouldn't let that person do: private repositories are "not
found" to others, anyone who can read a repository may open issues and comment, and changing issues,
approving, requesting changes and merging need push access and the base branch's rules.

| Request | |
| --- | --- |
| `GET /api/v1/server` | `{ name, api, signups }`: what the setup checks before signing in |
| `POST /api/v1/session` | `{ login, password, code? }` → `{ token, user }`. With two-factor authentication on, it answers 401 `{ twoFactor: true }` until `code` (from the authenticator app, or a recovery code) is sent too |
| `POST /api/v1/signup` | `{ username, email, password, name }` → `{ token, user }`, when sign-ups are open |
| `GET /api/v1/user`, `/home`, `/explore?q=`, `/users/<name>` | |
| `GET /api/v1/repos/<owner>/<repo>` | the repository, its README, root files, branches and counts |
| `GET …/tree?ref=&path=`, `…/blob?ref=&path=`, `…/raw?ref=&path=`, `…/image` | |
| `GET …/commits?ref=&page=`, `…/commits/<sha>`, `…/branches`, `…/events` | |
| `GET …/issues`, `GET, PATCH …/issues/<n>`, `POST …/issues`, `POST …/issues/<n>/comments` | |
| `GET …/pulls?state=`, `GET, PATCH …/pulls/<n>`, `GET …/pulls/<n>/files` | |
| `POST …/pulls/<n>/comments`, `…/pulls/<n>/reviews`, `…/pulls/<n>/merge` | |
| `GET …/runs?page=`, `…/runs/<n>`, `…/releases` | |

Errors are `{ "error": "…" }` with a 4xx status; 401 means the session ended.

## Code layout

In `apps/mobile/app/src/main/java/cc/vectrum/app/`:

- `data/`: the API's models (`Models.kt`), the HTTP client (`Api.kt`) and the stored session (`Session.kt`)
- `ui/theme/`: MetalUI's colorways (`Palette.kt`), type roles (`Type.kt`) and materials (`Materials.kt`:
  plates, wells and caps as stacks of shadows, from MetalUI's tokens)
- `ui/icons/`: the website's glyphs as vectors
- `ui/components/`: buttons, fields, the segmented control, pills and LEDs, avatars and identicons, Markdown,
  the code highlighter and viewer, diffs, sheets and toasts
- `ui/screens/`: one file per area; `ui/App.kt` holds the navigation and the tab bar
- `ui/Loader.kt`: screen data kept in view models, with pull to refresh and paging