diff --git a/.gitmodules b/.gitmodules index 47eeb21..56522f9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "third_party/Compose-HeatMap"] path = third_party/Compose-HeatMap - url = https://github.com/Derrakuma/Compose-HeatMap + url = https://github.com/Derakkuma/Compose-HeatMap diff --git a/ATPROTO.md b/ATPROTO.md index 1e24b01..07dd000 100644 --- a/ATPROTO.md +++ b/ATPROTO.md @@ -1,12 +1,12 @@ -# Derrakuma ATProto Integration Plan +# Derakkuma ATProto Integration Plan -Publish maimai DX play data to AT Protocol via `com.derrakuma.*` lexicons, backed by HappyView AppView. +Publish maimai DX play data to AT Protocol via `com.derakkuma.*` lexicons, backed by HappyView AppView. ## Architecture ``` ┌──────────────┐ writes ┌─────────┐ -│ Derrakuma │ ──────────────────► │ User's │ +│ Derakkuma │ ──────────────────► │ User's │ │ App (KMP) │ │ PDS │ │ │ ◄── reads ───────── │ │ └──────┬───────┘ reads └────┬────┘ @@ -14,7 +14,7 @@ Publish maimai DX play data to AT Protocol via `com.derrakuma.*` lexicons, backe │ ┌───────────────┐ │ │ │ HappyView │◄────────┤ firehose │ │ (AppView) │ sync │ - │ │ derrakuma.com│ │ + │ │ derakkuma.com│ │ │ └───────┬───────┘ │ │ │ │ └───── reads ─────┘ │ @@ -22,26 +22,26 @@ Publish maimai DX play data to AT Protocol via `com.derrakuma.*` lexicons, backe │ ┌─────────────────────────────────┘ │ DNS authority resolution - │ _lexicon.derrakuma.com TXT → DID - │ → com.derrakuma.* lexicon schemas + │ _lexicon.derakkuma.com TXT → DID + │ → com.derakkuma.* lexicon schemas ``` **App → PDS**: writes go directly to the user's PDS via `com.atproto.repo.createRecord` / `putRecord` -**HappyView → PDS**: subscribes to the firehose (Jetstream), indexes all `com.derrakuma.*` records into SQLite/Postgres +**HappyView → PDS**: subscribes to the firehose (Jetstream), indexes all `com.derakkuma.*` records into SQLite/Postgres **App → HappyView**: reads go through HappyView for indexed queries (friend feeds, leaderboards) ## Lexicon Definitions -### com.derrakuma.profile +### com.derakkuma.profile Player identity record. One per user, `key: "literal"` (fixed rkey = `self`). ```json { "lexicon": 1, - "id": "com.derrakuma.profile", + "id": "com.derakkuma.profile", "defs": { "main": { "type": "record", @@ -74,14 +74,14 @@ Player identity record. One per user, `key: "literal"` (fixed rkey = `self`). } ``` -### com.derrakuma.play +### com.derakkuma.play Individual play log entry. One record per play. `key: "tid"` (auto-generated TID). ```json { "lexicon": 1, - "id": "com.derrakuma.play", + "id": "com.derakkuma.play", "defs": { "main": { "type": "record", @@ -159,14 +159,14 @@ Individual play log entry. One record per play. `key: "tid"` (auto-generated TID } ``` -### com.derrakuma.best +### com.derakkuma.best Personal best score per song+difficulty combination. Updated in place (not appended). `key: "any"` (custom rkey = `{songId}-{difficulty}` for upsert). ```json { "lexicon": 1, - "id": "com.derrakuma.best", + "id": "com.derakkuma.best", "defs": { "main": { "type": "record", @@ -208,14 +208,14 @@ Personal best score per song+difficulty combination. Updated in place (not appen } ``` -### com.derrakuma.friend +### com.derakkuma.friend A friend link/snapshot, optionally resolved to another player's DID. `key: "any"`. Do not publish maimai friend codes, friend/detail idx values, or any other opaque SEGA identifier in either the record body or rkey. ```json { "lexicon": 1, - "id": "com.derrakuma.friend", + "id": "com.derakkuma.friend", "defs": { "main": { "type": "record", @@ -296,12 +296,12 @@ implementation("io.github.kikin81.atproto:at-protocol-oauth:") We DON'T use `at-protocol-models` (pre-generated Bluesky lexicons). Instead: - Use `at-protocol-runtime`'s `XrpcClient` directly for raw XRPC calls -- Define our own Kotlin data classes for `com.derrakuma.*` records +- Define our own Kotlin data classes for `com.derakkuma.*` records - Call `com.atproto.repo.createRecord` / `putRecord` with our record JSON ### Why not use the code generator? -The atproto-kotlin generator reads the upstream `@atproto/lex` corpus (Bluesky's lexicons). Our `com.derrakuma.*` lexicons aren't in that corpus. We'd need to either: +The atproto-kotlin generator reads the upstream `@atproto/lex` corpus (Bluesky's lexicons). Our `com.derakkuma.*` lexicons aren't in that corpus. We'd need to either: - Fork the generator pipeline and add our lexicons (complex, overkill) - Write thin Kotlin data classes + XrpcClient calls manually (simple, works) @@ -318,9 +318,9 @@ xrpcClient.procedure( nsid = "com.atproto.repo.createRecord", input = mapOf( "repo" to userDid, - "collection" to "com.derrakuma.play", + "collection" to "com.derakkuma.play", "record" to mapOf( - "$type" to "com.derrakuma.play", + "$type" to "com.derakkuma.play", "songName" to "Oshama Scramble!", "difficulty" to "MASTER", "achievement" to 100.4532, @@ -334,10 +334,10 @@ xrpcClient.procedure( nsid = "com.atproto.repo.putRecord", input = mapOf( "repo" to userDid, - "collection" to "com.derrakuma.best", + "collection" to "com.derakkuma.best", "rkey" to "song123-MASTER", "record" to mapOf( - "$type" to "com.derrakuma.best", + "$type" to "com.derakkuma.best", "songName" to "Oshama Scramble!", "difficulty" to "MASTER", "achievement" to 100.4532, @@ -352,7 +352,7 @@ xrpcClient.procedure( ### When to publish - **On data fetch** — when MaimaiClient fetches fresh data from SEGA, also publish to ATProto if linked + toggled on -- **Profile sync** — after `fetchDashboard()` or `fetchProfile()`, if profile toggle on, `putRecord` to `com.derrakuma.profile` (rkey `self`) +- **Profile sync** — after `fetchDashboard()` or `fetchProfile()`, if profile toggle on, `putRecord` to `com.derakkuma.profile` (rkey `self`) - **Play log sync** — after `fetchDashboard()` gets new `RecentPlay` entries, `createRecord` for each new play (compare against already-published plays via CacheStore tracking) - **Best scores sync** — after `fetchMusicDetail()` or `fetchDashboard()`, `putRecord` for each best score (upsert by rkey) - **Manual sync button** — in settings, "Sync to ATProto" forces full re-publish @@ -373,58 +373,58 @@ Best scores use custom rkeys (`{songId}-{difficulty}`) so `putRecord` naturally ### Deployment -1. **DNS setup** — `milk.derrakuma.com` A/CNAME record pointing to HappyView server -2. **DID for lexicon authority** — `_lexicon.derrakuma.com` resolves to the lexicon authority DID -3. **Lexicon publication** — publish `com.derrakuma.*` schemas as `com.atproto.lexicon.schema` records in the authority DID's repo +1. **DNS setup** — `milk.derakkuma.com` A/CNAME record pointing to HappyView server +2. **DID for lexicon authority** — `_lexicon.derakkuma.com` resolves to the lexicon authority DID +3. **Lexicon publication** — publish `com.derakkuma.*` schemas as `com.atproto.lexicon.schema` records in the authority DID's repo 4. **HappyView binary** — single binary, SQLite for small scale ```bash happyview serve \ - --hostname milk.derrakuma.com \ + --hostname milk.derakkuma.com \ --port 443 \ --admin-key ``` 5. **Upload lexicons** — via admin API or dashboard ```bash # Upload each lexicon schema - curl -X POST https://milk.derrakuma.com/xrpc/admin.uploadLexicon \ + curl -X POST https://milk.derakkuma.com/xrpc/admin.uploadLexicon \ -H "Authorization: Bearer " \ - -d @com.derrakuma.profile.json + -d @com.derakkuma.profile.json ``` -6. **Backfill** — trigger historical backfill for `com.derrakuma.*` collections +6. **Backfill** — trigger historical backfill for `com.derakkuma.*` collections ```bash - curl -X POST https://milk.derrakuma.com/xrpc/admin.startBackfill \ + curl -X POST https://milk.derakkuma.com/xrpc/admin.startBackfill \ -H "Authorization: Bearer " \ - -d '{"collections": ["com.derrakuma.profile", "com.derrakuma.play", "com.derrakuma.best", "com.derrakuma.friend"]}' + -d '{"collections": ["com.derakkuma.profile", "com.derakkuma.play", "com.derakkuma.best", "com.derakkuma.friend"]}' ``` ### Custom Queries (Lua Scripts) HappyView auto-generates CRUD endpoints from lexicons. For social features, add Lua scripts: -**Get friends' recent plays** (`com.derrakuma.getFriendsPlays`): +**Get friends' recent plays** (`com.derakkuma.getFriendsPlays`): ```lua -- Input: actor DID, limit, cursor --- 1. Look up com.derrakuma.friend records for actor --- 2. For each friend DID, get recent com.derrakuma.play records +-- 1. Look up com.derakkuma.friend records for actor +-- 2. For each friend DID, get recent com.derakkuma.play records -- 3. Merge and sort by createdAt descending -- 4. Return paginated results ``` -**Get leaderboard** (`com.derrakuma.getLeaderboard`): +**Get leaderboard** (`com.derakkuma.getLeaderboard`): ```lua -- Input: songName, difficulty, limit, cursor --- 1. Query com.derrakuma.best records matching songName + difficulty +-- 1. Query com.derakkuma.best records matching songName + difficulty -- 2. Sort by achievement descending --- 3. Join with com.derrakuma.profile for player names +-- 3. Join with com.derakkuma.profile for player names -- 4. Return paginated leaderboard ``` -**Get player stats** (`com.derrakuma.getPlayerStats`): +**Get player stats** (`com.derakkuma.getPlayerStats`): ```lua -- Input: actor DID --- 1. Get com.derrakuma.profile for actor --- 2. Count com.derrakuma.play records --- 3. Aggregate com.derrakuma.best records (count, avg achievement, etc.) +-- 1. Get com.derakkuma.profile for actor +-- 2. Count com.derakkuma.play records +-- 3. Aggregate com.derakkuma.best records (count, avg achievement, etc.) -- 4. Return combined stats ``` @@ -447,7 +447,7 @@ Fire on record changes for real-time processing: ### Phase 1: Foundation - [ ] Add atproto-kotlin dependency (runtime + oauth) -- [ ] Define Kotlin data classes for `com.derrakuma.*` records +- [ ] Define Kotlin data classes for `com.derakkuma.*` records - [ ] Implement OAuth flow in app (browser-based auth, token storage) - [ ] Add "Link ATProto Account" section to SettingsTab - [ ] Add publish toggle switches (profile, plays, bests, friends) @@ -461,7 +461,7 @@ Fire on record changes for real-time processing: - [ ] Add sync status indicator (last sync time, record count) ### Phase 3: HappyView -- [ ] Set up DNS for derrakuma.com (A record + _atproto TXT) +- [ ] Set up DNS for derakkuma.com (A record + _atproto TXT) - [ ] Deploy HappyView (single binary, SQLite) - [ ] Upload lexicon schemas via admin API - [ ] Trigger backfill for existing records @@ -483,7 +483,7 @@ Fire on record changes for real-time processing: ## Future ATProto Record Surface -The current core records are `com.derrakuma.profile`, `play`, `best`, and `friend`. These cover identity, score publication, and the maimai friend graph. The next likely ATProto surfaces split into two buckets: data we already parse/model in-app, and data that still needs client-side scraping/modeling. +The current core records are `com.derakkuma.profile`, `play`, `best`, and `friend`. These cover identity, score publication, and the maimai friend graph. The next likely ATProto surfaces split into two buckets: data we already parse/model in-app, and data that still needs client-side scraping/modeling. ### Already Modeled In-App @@ -497,7 +497,7 @@ Current structures: - `FavoriteUpdateList` - `FavoriteUpdateSong` -Candidate record: `com.derrakuma.favoriteSong` +Candidate record: `com.derakkuma.favoriteSong` Suggested fields: - `songName` @@ -523,8 +523,8 @@ Current structure: - `FriendVsPage` Candidate records: -- `com.derrakuma.friendVsSnapshot` -- possibly later `com.derrakuma.friendVsScore` if we parse per-song/per-genre rows. +- `com.derakkuma.friendVsSnapshot` +- possibly later `com.derakkuma.friendVsScore` if we parse per-song/per-genre rows. Suggested snapshot fields: - `friendMaimaiIdx` @@ -553,8 +553,8 @@ Current structures: - `CircleFestaHistory` Candidate records: -- `com.derrakuma.circle` — the publisher's current circle snapshot -- `com.derrakuma.circleMember` — one member snapshot per observed member +- `com.derakkuma.circle` — the publisher's current circle snapshot +- `com.derakkuma.circleMember` — one member snapshot per observed member Suggested `circle` fields: - `circleCode` @@ -581,49 +581,49 @@ Suggested `circleMember` fields: - `status` - `icon` blob - `subject` optional DID if resolved -- `profile` optional AT URI backlink to `com.derrakuma.profile/self` for the publisher's own circle member record +- `profile` optional AT URI backlink to `com.derakkuma.profile/self` for the publisher's own circle member record - `observedAt` Self member convention: - The publisher's own circle member record uses rkey `self`. -- It should include `subject = ` and `profile = at:///com.derrakuma.profile/self`. +- It should include `subject = ` and `profile = at:///com.derakkuma.profile/self`. - Other observed members use deterministic opaque member rkeys and only get `subject`/`profile` later if they are resolved to an ATProto identity. How multiple ATProto identities in one circle should work: - Do **not** treat a circle as owned by one DID. Treat `circleCode` as the shared external key. -- Each player publishes their own `com.derrakuma.circle` snapshot for the circle they belong to. +- Each player publishes their own `com.derakkuma.circle` snapshot for the circle they belong to. - HappyView clusters snapshots by `circleCode` and can merge observations across publishers. -- Members can be unresolved snapshots first (`displayName`, rating, icon, points), then later resolved to DIDs when a matching Derrakuma profile/friend-code claim appears. +- Members can be unresolved snapshots first (`displayName`, rating, icon, points), then later resolved to DIDs when a matching Derakkuma profile/friend-code claim appears. - If multiple ATProto users in the same circle publish snapshots, HappyView can show all resolved identities under the same circle while keeping non-ATProto members as unresolved member cards. Privacy notes: - Circle membership is semi-public inside maimai but still social graph data. Keep publishing opt-in. -- Avoid publishing member friend codes unless that member published their own code via `com.derrakuma.profile`. +- Avoid publishing member friend codes unless that member published their own code via `com.derakkuma.profile`. Draft / intentionally not active yet: -- Circle ranking is not a separate ATProto record. The publisher's own circle rank is included on `com.derrakuma.circle`; the full ranking table is leaderboard browsing data, not durable identity/social graph data for the publisher. +- Circle ranking is not a separate ATProto record. The publisher's own circle rank is included on `com.derakkuma.circle`; the full ranking table is leaderboard browsing data, not durable identity/social graph data for the publisher. - Circle rewards are not planned for ATProto right now; they are low-signal compared to circle identity/members. -- CiRCLE FESTA should stay a TODO/draft until the next active FESTA so we can inspect real current HTML/data shape before freezing a lexicon. Possible future record: `com.derrakuma.circleFesta` with top/ranking/history variants, but do not publish it yet. +- CiRCLE FESTA should stay a TODO/draft until the next active FESTA so we can inspect real current HTML/data shape before freezing a lexicon. Possible future record: `com.derakkuma.circleFesta` with top/ranking/history variants, but do not publish it yet. ### Needs New Scrapers / Models -These are good ATProto candidates, but Derrakuma does not currently have durable data structures/fetch paths for them. +These are good ATProto candidates, but Derakkuma does not currently have durable data structures/fetch paths for them. #### Collection Wanted records: -- `com.derrakuma.collectionItem` +- `com.derakkuma.collectionItem` - possibly typed variants later: `collectionIcon`, `collectionTitle`, `collectionPlate`, `collectionFrame`, `collectionPartner`. Notes: - Current `CollectionTab` is only a UI stub/category list. No parsed inventory yet. - Need scraper/model for owned items, selected item, item type, rarity/category, and image URLs. -- Once in-app item setting exists, profile sync should run after changing icon/partner/title/frame so `com.derrakuma.profile` stays current. +- Once in-app item setting exists, profile sync should run after changing icon/partner/title/frame so `com.derakkuma.profile` stays current. #### Stamp Card Wanted record: -- `com.derrakuma.stampCard` +- `com.derakkuma.stampCard` Notes: - Need scraper/model for active stamp card, progress, rewards, expiry/event name, and claimed/unclaimed state. @@ -632,8 +632,8 @@ Notes: #### Album Wanted records: -- `com.derrakuma.albumPhoto` -- possibly `com.derrakuma.album` if SEGA groups photos into album pages/events. +- `com.derakkuma.albumPhoto` +- possibly `com.derakkuma.album` if SEGA groups photos into album pages/events. Notes: - Need scraper/model for album entries, image URLs, capture/play date, song/event metadata, and privacy controls. @@ -643,8 +643,8 @@ Notes: #### Shop / Purchases Wanted records: -- `com.derrakuma.purchase` -- `com.derrakuma.shopItem` only if we want catalog snapshots. +- `com.derakkuma.purchase` +- `com.derakkuma.shopItem` only if we want catalog snapshots. Notes: - Need scraper/model for shop inventory and purchase confirmations/history. @@ -654,7 +654,7 @@ Notes: #### Area Wanted record: -- `com.derrakuma.areaProgress` +- `com.derakkuma.areaProgress` Notes: - Need scraper/model for current area, progress, distance/steps/points, rewards, and event/season metadata. diff --git a/README.md b/README.md index 776e078..932684d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -derrakumanet +derakkumanet -# derrakuma +# derakkuma unofficial maimai DX NET client. built with compose multiplatform (android + ios). @@ -38,11 +38,11 @@ all fetching and parsing is client-side, your SEGA cookies never leave your devi ## how it works -derrakuma is a pure client-side scraper: it fetches HTML pages from `maimaidx-eng.com` using the user's session cookies, then parses them with custom Kotlin HTML parsers. there is no backend server. +derakkuma is a pure client-side scraper: it fetches HTML pages from `maimaidx-eng.com` using the user's session cookies, then parses them with custom Kotlin HTML parsers. there is no backend server. ### scraping flow -1. **auth**: user logs in via SEGA's aime gateway. derrakuma stores the resulting session cookies (`_t`, `userId`) plus a `clal` cookie for automatic session refresh +1. **auth**: user logs in via SEGA's aime gateway. derakkuma stores the resulting session cookies (`_t`, `userId`) plus a `clal` cookie for automatic session refresh 2. **fetch**: ktor HTTP client (with mobile chrome UA) requests pages like `/home/`, `/playLog/`, `/rating/` etc. redirects are disabled so session expiry can be detected 3. **parse**: raw HTML is handed to parser functions in `Parsers.kt` which extract structured data using regex + string operations 4. **cache**: parsed results are stored in sql_delight with per-section TTL (profile: 1hr, gameplay data: 1min). stale cache is served during maintenance mode @@ -85,19 +85,19 @@ these idx values are extracted from the HTML during the initial list parse and s ### ATProto publishing -ATProto support is opt-in and publishes custom `com.derrakuma.*` records to the user's PDS +ATProto support is opt-in and publishes custom `com.derakkuma.*` records to the user's PDS Active records: | Record | RKey strategy | Notes | |--------|---------------|-------| -| `com.derrakuma.profile` | literal `self` | player profile; friend code is optional and separately confirmed | -| `com.derrakuma.play` | PDS-generated key via `createRecord` | append-only play events; remote dedupe prevents repeat sync duplicates | -| `com.derrakuma.best` | deterministic opaque `best` | one mutable best per song+difficulty/variant | -| `com.derrakuma.friend` | deterministic opaque `friend` | friend/detail idx is only used as private hash salt; never published raw | -| `com.derrakuma.favoriteSong` | deterministic opaque `fav` | current favorite list; unfavorited songs are deleted remotely | -| `com.derrakuma.circle` | deterministic opaque `circle` | current circle snapshot; includes your circle rank/points | -| `com.derrakuma.circleMember` | `self` for your member record, deterministic opaque member key for others | self member links back to `at:///com.derrakuma.profile/self` | +| `com.derakkuma.profile` | literal `self` | player profile; friend code is optional and separately confirmed | +| `com.derakkuma.play` | PDS-generated key via `createRecord` | append-only play events; remote dedupe prevents repeat sync duplicates | +| `com.derakkuma.best` | deterministic opaque `best` | one mutable best per song+difficulty/variant | +| `com.derakkuma.friend` | deterministic opaque `friend` | friend/detail idx is only used as private hash salt; never published raw | +| `com.derakkuma.favoriteSong` | deterministic opaque `fav` | current favorite list; unfavorited songs are deleted remotely | +| `com.derakkuma.circle` | deterministic opaque `circle` | current circle snapshot; includes your circle rank/points | +| `com.derakkuma.circleMember` | `self` for your member record, deterministic opaque member key for others | self member links back to `at:///com.derakkuma.profile/self` | ### feature parity progress @@ -204,7 +204,7 @@ Active records: - [x] Circle Ranking - [x] Parse - [x] UI - - [x] ATProto (not a separate record; your circle's rank/points live on `com.derrakuma.circle`) + - [x] ATProto (not a separate record; your circle's rank/points live on `com.derakkuma.circle`) - [x] Circle Members - [x] Parse - [x] UI diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 22add98..b55319d 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -93,11 +93,11 @@ kotlin { } android { - namespace = "com.derrakuma" + namespace = "com.derakkuma" compileSdk = 35 defaultConfig { - applicationId = "com.derrakuma" + applicationId = "com.derakkuma" minSdk = 26 targetSdk = 35 versionCode = 1 @@ -116,8 +116,8 @@ android { sqldelight { databases { - create("DerrakumaDb") { - packageName.set("com.derrakuma.db") + create("DerakkumaDb") { + packageName.set("com.derakkuma.db") version = 3 } } diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml index 86689e0..5d04d66 100644 --- a/composeApp/src/androidMain/AndroidManifest.xml +++ b/composeApp/src/androidMain/AndroidManifest.xml @@ -4,14 +4,14 @@ @@ -23,14 +23,14 @@ - + - + diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/DerrakumaApp.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/DerrakumaApp.kt index 966ef1d..38ea1fc 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/DerrakumaApp.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/DerrakumaApp.kt @@ -1,10 +1,10 @@ -package com.derrakuma +package com.derakkuma import android.app.Application -import com.derrakuma.atproto.AtProtoClient -import com.derrakuma.kamaitachi.KamaitachiClient +import com.derakkuma.atproto.AtProtoClient +import com.derakkuma.kamaitachi.KamaitachiClient -class DerrakumaApp : Application() { +class DerakkumaApp : Application() { override fun onCreate() { super.onCreate() context = applicationContext diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/MainActivity.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/MainActivity.kt index f576a82..473fb1b 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/MainActivity.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/MainActivity.kt @@ -1,4 +1,4 @@ -package com.derrakuma +package com.derakkuma import android.content.Intent import android.os.Bundle @@ -6,16 +6,16 @@ import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import com.derrakuma.atproto.AndroidAtProtoClient -import com.derrakuma.atproto.configureAtProtoBackgroundSync -import com.derrakuma.atproto.getAtProtoAccount -import com.derrakuma.atproto.getAtProtoPublishSettings -import com.derrakuma.data.AuthStore -import com.derrakuma.data.CacheStore -import com.derrakuma.data.DriverFactory -import com.derrakuma.data.PlayStore -import com.derrakuma.kamaitachi.KamaitachiClient -import com.derrakuma.ui.images.initCoil +import com.derakkuma.atproto.AndroidAtProtoClient +import com.derakkuma.atproto.configureAtProtoBackgroundSync +import com.derakkuma.atproto.getAtProtoAccount +import com.derakkuma.atproto.getAtProtoPublishSettings +import com.derakkuma.data.AuthStore +import com.derakkuma.data.CacheStore +import com.derakkuma.data.DriverFactory +import com.derakkuma.data.PlayStore +import com.derakkuma.kamaitachi.KamaitachiClient +import com.derakkuma.ui.images.initCoil import io.github.samuolis.posthog.PostHog import io.github.samuolis.posthog.PostHogConfig import io.github.samuolis.posthog.PostHogContext @@ -33,7 +33,7 @@ class MainActivity : ComponentActivity() { PostHog.setup( config = PostHogConfig( apiKey = Secrets.POSTHOG_API_KEY, - host = "https://salt.derrakuma.com", + host = "https://salt.derakkuma.com", captureApplicationLifecycleEvents = true, captureScreenViews = false, ), @@ -59,8 +59,8 @@ class MainActivity : ComponentActivity() { private fun handleDeepLink(intent: Intent) { val uri = intent.data ?: return - if (uri.scheme == "com.derrakuma" && uri.path == "/oauth-redirect") { - val client = DerrakumaApp.atProtoClient as? AndroidAtProtoClient ?: return + if (uri.scheme == "com.derakkuma" && uri.path == "/oauth-redirect") { + val client = DerakkumaApp.atProtoClient as? AndroidAtProtoClient ?: return val redirectUri = uri.toString() Log.d("MainActivity", "OAuth redirect received: $redirectUri") CoroutineScope(Dispatchers.Main).launch { @@ -73,8 +73,8 @@ class MainActivity : ComponentActivity() { Log.e("MainActivity", "OAuth login failed", e) } } - } else if (uri.scheme == "com.derrakuma" && uri.path == "/kamai") { - val client: KamaitachiClient = DerrakumaApp.kamaitachiClient ?: return + } else if (uri.scheme == "com.derakkuma" && uri.path == "/kamai") { + val client: KamaitachiClient = DerakkumaApp.kamaitachiClient ?: return val redirectUri = uri.toString() Log.d("MainActivity", "Kamaitachi redirect received") CoroutineScope(Dispatchers.Main).launch { diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AndroidAtProtoClient.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AndroidAtProtoClient.kt index 90d3374..10e1893 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AndroidAtProtoClient.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AndroidAtProtoClient.kt @@ -1,12 +1,12 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto import android.content.Context import android.util.Log -import com.derrakuma.atproto.vendor.oauth.AtOAuth -import com.derrakuma.atproto.vendor.oauth.OAuthSession -import com.derrakuma.atproto.vendor.oauth.OAuthSessionStore -import com.derrakuma.atproto.vendor.runtime.NoXrpcParams -import com.derrakuma.atproto.vendor.runtime.XrpcClient +import com.derakkuma.atproto.vendor.oauth.AtOAuth +import com.derakkuma.atproto.vendor.oauth.OAuthSession +import com.derakkuma.atproto.vendor.oauth.OAuthSessionStore +import com.derakkuma.atproto.vendor.runtime.NoXrpcParams +import com.derakkuma.atproto.vendor.runtime.XrpcClient import io.ktor.client.HttpClient import io.ktor.client.engine.okhttp.OkHttp import io.ktor.http.ContentType @@ -45,7 +45,7 @@ class AndroidAtProtoClient( redirectUri = "$redirectScheme:/oauth-redirect", sessionStore = sessionStore, httpClient = httpClient, - scope = DERRAKUMA_ATPROTO_SCOPE, + scope = DERAKKUMA_ATPROTO_SCOPE, ) private var xrpcClient: XrpcClient? = null diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSync.android.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSync.android.kt index 2fad3d8..ae640a9 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSync.android.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSync.android.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto import android.app.AlarmManager import android.app.PendingIntent @@ -7,21 +7,21 @@ import android.content.Intent import android.net.Uri import android.os.Build import android.provider.Settings -import com.derrakuma.DerrakumaApp +import com.derakkuma.DerakkumaApp private const val REQUEST_CODE = 0xD377A private const val INTERVAL_MS = 12L * 60L * 60L * 1000L -internal const val ACTION_ATPROTO_BACKGROUND_SYNC = "com.derrakuma.atproto.BACKGROUND_SYNC" +internal const val ACTION_ATPROTO_BACKGROUND_SYNC = "com.derakkuma.atproto.BACKGROUND_SYNC" actual fun configureAtProtoBackgroundSync(enabled: Boolean) { - AtProtoBackgroundSyncScheduler.configure(DerrakumaApp.context, enabled) + AtProtoBackgroundSyncScheduler.configure(DerakkumaApp.context, enabled) } -actual fun canScheduleAtProtoBackgroundSync(): Boolean = AtProtoBackgroundSyncScheduler.canScheduleExact(DerrakumaApp.context) +actual fun canScheduleAtProtoBackgroundSync(): Boolean = AtProtoBackgroundSyncScheduler.canScheduleExact(DerakkumaApp.context) actual fun openAtProtoBackgroundSyncSettings() { - val context = DerrakumaApp.context + val context = DerakkumaApp.context val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM, Uri.parse("package:${context.packageName}")) } else { diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSyncReceiver.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSyncReceiver.kt index 534f262..bb80604 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSyncReceiver.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSyncReceiver.kt @@ -1,14 +1,14 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.util.Log -import com.derrakuma.data.AuthStore -import com.derrakuma.data.CacheStore -import com.derrakuma.data.DriverFactory -import com.derrakuma.data.MaimaiClient -import com.derrakuma.data.PlayStore +import com.derakkuma.data.AuthStore +import com.derakkuma.data.CacheStore +import com.derakkuma.data.DriverFactory +import com.derakkuma.data.MaimaiClient +import com.derakkuma.data.PlayStore import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -41,8 +41,8 @@ class AtProtoBackgroundSyncReceiver : BroadcastReceiver() { ) val atProtoClient = AndroidAtProtoClient( context = appContext, - clientMetadataUrl = "https://derrakuma.com/oauth/client-metadata.json", - redirectScheme = "com.derrakuma", + clientMetadataUrl = "https://derakkuma.com/oauth/client-metadata.json", + redirectScheme = "com.derakkuma", ) if (atProtoClient.account == null) return@launch shouldReschedule = true diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoFactory.android.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoFactory.android.kt index 43b2235..ce65982 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoFactory.android.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoFactory.android.kt @@ -1,11 +1,11 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto actual fun createAtProtoClient(): AtProtoClient { // Android gets the real client — needs Context from app initialization // Called lazily; the AndroidApp companion provides the context return AndroidAtProtoClient( - context = com.derrakuma.DerrakumaApp.context, - clientMetadataUrl = "https://derrakuma.com/oauth/client-metadata.json", - redirectScheme = "com.derrakuma", + context = com.derakkuma.DerakkumaApp.context, + clientMetadataUrl = "https://derakkuma.com/oauth/client-metadata.json", + redirectScheme = "com.derakkuma", ) } diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoRegistry.android.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoRegistry.android.kt index 7d8176c..e6556b7 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoRegistry.android.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/AtProtoRegistry.android.kt @@ -1,7 +1,7 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto -import com.derrakuma.DerrakumaApp +import com.derakkuma.DerakkumaApp actual fun registerAtProtoClient(client: AtProtoClient) { - DerrakumaApp.atProtoClient = client + DerakkumaApp.atProtoClient = client } diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/OpenOAuthUrl.android.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/OpenOAuthUrl.android.kt index 8b48854..b8e9772 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/OpenOAuthUrl.android.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/OpenOAuthUrl.android.kt @@ -1,12 +1,12 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto import android.content.Intent import android.net.Uri import android.util.Log -import com.derrakuma.DerrakumaApp +import com.derakkuma.DerakkumaApp internal actual fun openOAuthUrl(url: String) { Log.d("AtProtoOAuth", "opening OAuth URL: $url") val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - DerrakumaApp.context.startActivity(intent) + DerakkumaApp.context.startActivity(intent) } diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/vendor/oauth/CryptoProvider.android.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/vendor/oauth/CryptoProvider.android.kt index afe5f1e..2ebe995 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/vendor/oauth/CryptoProvider.android.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/atproto/vendor/oauth/CryptoProvider.android.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.oauth +package com.derakkuma.atproto.vendor.oauth import dev.whyoleg.cryptography.CryptographyProvider import dev.whyoleg.cryptography.providers.jdk.JDK diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/data/AuthStore.android.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/data/AuthStore.android.kt index 0bd5651..d357503 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/data/AuthStore.android.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/data/AuthStore.android.kt @@ -1,10 +1,10 @@ -package com.derrakuma.data +package com.derakkuma.data import android.content.Context actual class AuthStore(private val context: Context) { private val prefs by lazy { - context.getSharedPreferences("derrakuma_auth", Context.MODE_PRIVATE) + context.getSharedPreferences("derakkuma_auth", Context.MODE_PRIVATE) } actual suspend fun save(auth: AuthState) { diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/data/DriverFactory.android.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/data/DriverFactory.android.kt index 4d12d7e..e85d1bd 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/data/DriverFactory.android.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/data/DriverFactory.android.kt @@ -1,10 +1,10 @@ -package com.derrakuma.data +package com.derakkuma.data import android.content.Context import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.android.AndroidSqliteDriver -import com.derrakuma.db.DerrakumaDb +import com.derakkuma.db.DerakkumaDb actual class DriverFactory(private val context: Context) { - actual fun createDriver(): SqlDriver = AndroidSqliteDriver(DerrakumaDb.Schema, context, "derrakuma.db") + actual fun createDriver(): SqlDriver = AndroidSqliteDriver(DerakkumaDb.Schema, context, "derakkuma.db") } diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/kamaitachi/KamaitachiRegistry.android.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/kamaitachi/KamaitachiRegistry.android.kt index d2da493..38cc10c 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/kamaitachi/KamaitachiRegistry.android.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/kamaitachi/KamaitachiRegistry.android.kt @@ -1,7 +1,7 @@ -package com.derrakuma.kamaitachi +package com.derakkuma.kamaitachi -import com.derrakuma.DerrakumaApp +import com.derakkuma.DerakkumaApp actual fun registerKamaitachiClient(client: KamaitachiClient) { - DerrakumaApp.kamaitachiClient = client + DerakkumaApp.kamaitachiClient = client } diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/ui/auth/AuthScreen.android.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/ui/auth/AuthScreen.android.kt index 8eecc3b..3ba417a 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/ui/auth/AuthScreen.android.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/ui/auth/AuthScreen.android.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.auth +package com.derakkuma.ui.auth import android.annotation.SuppressLint import android.view.ViewGroup @@ -15,7 +15,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.viewinterop.AndroidView -import com.derrakuma.data.AuthState +import com.derakkuma.data.AuthState import kotlinx.coroutines.delay import kotlin.time.Duration.Companion.milliseconds diff --git a/composeApp/src/androidMain/kotlin/com/derrakuma/ui/util/Clipboard.android.kt b/composeApp/src/androidMain/kotlin/com/derrakuma/ui/util/Clipboard.android.kt index b0b38ad..4b46e12 100644 --- a/composeApp/src/androidMain/kotlin/com/derrakuma/ui/util/Clipboard.android.kt +++ b/composeApp/src/androidMain/kotlin/com/derrakuma/ui/util/Clipboard.android.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.util +package com.derakkuma.ui.util import android.content.ClipData import androidx.compose.ui.platform.ClipEntry diff --git a/composeApp/src/androidMain/res/values/strings.xml b/composeApp/src/androidMain/res/values/strings.xml index 5718095..dc0b882 100644 --- a/composeApp/src/androidMain/res/values/strings.xml +++ b/composeApp/src/androidMain/res/values/strings.xml @@ -1,4 +1,4 @@ - Derrakuma + Derakkuma diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/App.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/App.kt index 2cb4348..1ad171d 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/App.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/App.kt @@ -1,4 +1,4 @@ -package com.derrakuma +package com.derakkuma import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.animateFloat @@ -33,27 +33,27 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.toRoute -import com.derrakuma.atproto.AtProtoClient -import com.derrakuma.atproto.createAtProtoClient -import com.derrakuma.atproto.registerAtProtoClient -import com.derrakuma.atproto.syncCachedDashboardToAtProto -import com.derrakuma.data.AuthState -import com.derrakuma.data.AuthStore -import com.derrakuma.data.CacheStore -import com.derrakuma.data.HistoricalCrawler -import com.derrakuma.data.MaimaiClient -import com.derrakuma.data.MaintenanceMode -import com.derrakuma.data.PlayStore -import com.derrakuma.kamaitachi.KamaitachiClient -import com.derrakuma.kamaitachi.registerKamaitachiClient -import com.derrakuma.ui.auth.AuthScreen -import com.derrakuma.ui.components.MaintenanceBanner -import com.derrakuma.ui.icons.Diversity3 -import com.derrakuma.ui.tabs.* -import com.derrakuma.ui.theme.DarkText -import com.derrakuma.ui.theme.DerrakumaTheme -import com.derrakuma.ui.theme.MaimaiBlue -import com.derrakuma.ui.theme.MutedText +import com.derakkuma.atproto.AtProtoClient +import com.derakkuma.atproto.createAtProtoClient +import com.derakkuma.atproto.registerAtProtoClient +import com.derakkuma.atproto.syncCachedDashboardToAtProto +import com.derakkuma.data.AuthState +import com.derakkuma.data.AuthStore +import com.derakkuma.data.CacheStore +import com.derakkuma.data.HistoricalCrawler +import com.derakkuma.data.MaimaiClient +import com.derakkuma.data.MaintenanceMode +import com.derakkuma.data.PlayStore +import com.derakkuma.kamaitachi.KamaitachiClient +import com.derakkuma.kamaitachi.registerKamaitachiClient +import com.derakkuma.ui.auth.AuthScreen +import com.derakkuma.ui.components.MaintenanceBanner +import com.derakkuma.ui.icons.Diversity3 +import com.derakkuma.ui.tabs.* +import com.derakkuma.ui.theme.DarkText +import com.derakkuma.ui.theme.DerakkumaTheme +import com.derakkuma.ui.theme.MaimaiBlue +import com.derakkuma.ui.theme.MutedText import kotlinx.coroutines.launch import kotlinx.serialization.Serializable @@ -169,8 +169,8 @@ fun App(authStore: AuthStore, cacheStore: CacheStore, playStore: PlayStore) { checkingAuth = false } - DerrakumaTheme { - if (checkingAuth) return@DerrakumaTheme + DerakkumaTheme { + if (checkingAuth) return@DerakkumaTheme if (auth == null && !refreshingSession) { AuthScreen( @@ -389,7 +389,7 @@ fun App(authStore: AuthStore, cacheStore: CacheStore, playStore: PlayStore) { private fun ScrollingMaimaiPark(animate: Boolean = true) { val bitmap = remember { mutableStateOf(null) } LaunchedEffect(Unit) { - bitmap.value = com.derrakuma.ui.images.MaimaiAssets.load("back_area_dx.png") + bitmap.value = com.derakkuma.ui.images.MaimaiAssets.load("back_area_dx.png") } val img = bitmap.value ?: return diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSync.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSync.kt index 6bbfea3..98a1c05 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSync.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSync.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto expect fun configureAtProtoBackgroundSync(enabled: Boolean) diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoClient.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoClient.kt index ca49087..9aafce5 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoClient.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoClient.kt @@ -1,25 +1,25 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto import kotlinx.serialization.json.JsonObject -const val DERRAKUMA_ATPROTO_SCOPE = +const val DERAKKUMA_ATPROTO_SCOPE = "atproto " + "blob:*/* " + - "repo:com.derrakuma.profile?action=create " + - "repo:com.derrakuma.profile?action=update " + - "repo:com.derrakuma.play?action=create " + - "repo:com.derrakuma.best?action=create " + - "repo:com.derrakuma.best?action=update " + - "repo:com.derrakuma.friend?action=create " + - "repo:com.derrakuma.friend?action=update " + - "repo:com.derrakuma.friend?action=delete " + - "repo:com.derrakuma.favoriteSong?action=create " + - "repo:com.derrakuma.favoriteSong?action=update " + - "repo:com.derrakuma.favoriteSong?action=delete " + - "repo:com.derrakuma.circle?action=create " + - "repo:com.derrakuma.circle?action=update " + - "repo:com.derrakuma.circleMember?action=create " + - "repo:com.derrakuma.circleMember?action=update " + + "repo:com.derakkuma.profile?action=create " + + "repo:com.derakkuma.profile?action=update " + + "repo:com.derakkuma.play?action=create " + + "repo:com.derakkuma.best?action=create " + + "repo:com.derakkuma.best?action=update " + + "repo:com.derakkuma.friend?action=create " + + "repo:com.derakkuma.friend?action=update " + + "repo:com.derakkuma.friend?action=delete " + + "repo:com.derakkuma.favoriteSong?action=create " + + "repo:com.derakkuma.favoriteSong?action=update " + + "repo:com.derakkuma.favoriteSong?action=delete " + + "repo:com.derakkuma.circle?action=create " + + "repo:com.derakkuma.circle?action=update " + + "repo:com.derakkuma.circleMember?action=create " + + "repo:com.derakkuma.circleMember?action=update " + "repo:app.bsky.graph.follow?action=create" /** diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoFactory.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoFactory.kt index 83cdeca..3076cae 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoFactory.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto /** * Factory to create the platform-appropriate AtProtoClient. diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoModels.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoModels.kt index 93fffaa..cdecf7c 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoModels.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoModels.kt @@ -1,9 +1,9 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonObject -// --- com.derrakuma.profile --- +// --- com.derakkuma.profile --- @Serializable data class AtProfileRecord( @@ -23,7 +23,7 @@ data class AtProfileRecord( val updatedAt: String = "", ) -// --- com.derrakuma.play --- +// --- com.derakkuma.play --- @Serializable data class DxScore( @@ -75,7 +75,7 @@ data class AtPlayRecord( val createdAt: String = "", ) -// --- com.derrakuma.best --- +// --- com.derakkuma.best --- @Serializable data class AtBestRecord( @@ -95,7 +95,7 @@ data class AtBestRecord( val updatedAt: String = "", ) -// --- com.derrakuma.friend --- +// --- com.derakkuma.friend --- @Serializable data class AtFriendRecord( @@ -116,7 +116,7 @@ data class AtFriendRecord( val updatedAt: String = "", ) -// --- com.derrakuma.favoriteSong --- +// --- com.derakkuma.favoriteSong --- @Serializable data class AtFavoriteSongRecord( @@ -132,7 +132,7 @@ data class AtFavoriteSongRecord( val updatedAt: String = "", ) -// --- com.derrakuma.circle* --- +// --- com.derakkuma.circle* --- @Serializable data class AtCircleRecord( diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoOfflineQueue.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoOfflineQueue.kt index 8f4d008..8714937 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoOfflineQueue.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoOfflineQueue.kt @@ -1,6 +1,6 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto -import com.derrakuma.data.CacheStore +import com.derakkuma.data.CacheStore import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoRegistry.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoRegistry.kt index 02cbe52..78f343b 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoRegistry.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoRegistry.kt @@ -1,8 +1,8 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto /** * Platform-specific hook to register the AtProtoClient after creation. - * Android: stores it in DerrakumaApp for access from MainActivity. + * Android: stores it in DerakkumaApp for access from MainActivity. * iOS: no-op (StubAtProtoClient doesn't need registration). */ expect fun registerAtProtoClient(client: AtProtoClient) diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoSettings.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoSettings.kt index 1a0ba08..c79b117 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoSettings.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoSettings.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween @@ -18,13 +18,13 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.data.CacheStore -import com.derrakuma.data.MaimaiClient -import com.derrakuma.data.MaimaiUserCard -import com.derrakuma.data.PlayStore -import com.derrakuma.ui.images.MaimaiRemoteImage -import com.derrakuma.ui.theme.* -import com.derrakuma.util.formatScore +import com.derakkuma.data.CacheStore +import com.derakkuma.data.MaimaiClient +import com.derakkuma.data.MaimaiUserCard +import com.derakkuma.data.PlayStore +import com.derakkuma.ui.images.MaimaiRemoteImage +import com.derakkuma.ui.theme.* +import com.derakkuma.util.formatScore import io.ktor.client.HttpClient import io.ktor.client.request.get import io.ktor.client.request.parameter @@ -534,7 +534,7 @@ fun AtProtoSection( AlertDialog( onDismissRequest = { showFriendCodePublishConfirm = false }, title = { Text("Publish friend code?") }, - text = { Text("This will add your maimai friend code to your public AT Protocol profile, which lets Bluesky friends discover you and send maimai friend requests through Derrakuma. Even if you remove it later, other apps or indexers may have already copied it.") }, + text = { Text("This will add your maimai friend code to your public AT Protocol profile, which lets Bluesky friends discover you and send maimai friend requests through Derakkuma. Even if you remove it later, other apps or indexers may have already copied it.") }, confirmButton = { TextButton( onClick = { @@ -615,7 +615,7 @@ private fun AtProtoActivityCard( } Text("Friends' recent plays", fontSize = 12.sp, fontWeight = FontWeight.SemiBold, color = DarkText) if (feed.isEmpty()) { - Text("No friend activity yet... get 'em on Derrakuma! :D", fontSize = 12.sp, color = MutedText) + Text("No friend activity yet... get 'em on Derakkuma! :D", fontSize = 12.sp, color = MutedText) } else { feed.take(5).forEach { play -> Column(Modifier.fillMaxWidth().padding(vertical = 2.dp)) { @@ -647,7 +647,7 @@ private fun SocialBridgeCard( ) { Column(verticalArrangement = Arrangement.spacedBy(10.dp)) { Text("Social Bridge", fontSize = 13.sp, fontWeight = FontWeight.Medium, color = DarkText) - Text("Discovery uses published Derrakuma profiles.", fontSize = 11.sp, color = MutedText) + Text("Discovery uses published Derakkuma profiles.", fontSize = 11.sp, color = MutedText) if (loading) LinearProgressIndicator(modifier = Modifier.fillMaxWidth(), color = PrimaryBlue) if (error != null) Text(error, fontSize = 12.sp, color = MaterialTheme.colorScheme.error) @@ -669,7 +669,7 @@ private fun SocialBridgeCard( Text("Bluesky follows on maimai", fontSize = 12.sp, fontWeight = FontWeight.SemiBold, color = DarkText) if (bskyFollowMatches.isEmpty()) { - Text(if (loading) "Looking for matches..." else "No Bluesky follows matched a published Derrakuma profile yet.", fontSize = 12.sp, color = MutedText) + Text(if (loading) "Looking for matches..." else "No Bluesky follows matched a published Derakkuma profile yet.", fontSize = 12.sp, color = MutedText) } else { bskyFollowMatches.forEach { match -> val label = match.profile.playerName.ifBlank { match.follow.displayName.ifBlank { match.follow.handle } } diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoSync.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoSync.kt index d24c090..a4ebe3c 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoSync.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoSync.kt @@ -1,21 +1,21 @@ -package com.derrakuma.atproto - -import com.derrakuma.atproto.vendor.oauth.provider -import com.derrakuma.data.CacheStore -import com.derrakuma.data.CircleHome -import com.derrakuma.data.CircleMember -import com.derrakuma.data.CircleMembersPage -import com.derrakuma.data.DashboardData -import com.derrakuma.data.DifficultyScore -import com.derrakuma.data.FavoriteSong -import com.derrakuma.data.MaimaiUserCard -import com.derrakuma.data.MusicDetail -import com.derrakuma.data.NoteBreakdown -import com.derrakuma.data.NoteJudgement -import com.derrakuma.data.PlayLogDetail -import com.derrakuma.data.Profile -import com.derrakuma.data.RecentPlay -import com.derrakuma.ui.images.currentCoilImageLoader +package com.derakkuma.atproto + +import com.derakkuma.atproto.vendor.oauth.provider +import com.derakkuma.data.CacheStore +import com.derakkuma.data.CircleHome +import com.derakkuma.data.CircleMember +import com.derakkuma.data.CircleMembersPage +import com.derakkuma.data.DashboardData +import com.derakkuma.data.DifficultyScore +import com.derakkuma.data.FavoriteSong +import com.derakkuma.data.MaimaiUserCard +import com.derakkuma.data.MusicDetail +import com.derakkuma.data.NoteBreakdown +import com.derakkuma.data.NoteJudgement +import com.derakkuma.data.PlayLogDetail +import com.derakkuma.data.Profile +import com.derakkuma.data.RecentPlay +import com.derakkuma.ui.images.currentCoilImageLoader import dev.whyoleg.cryptography.algorithms.SHA256 import io.ktor.client.HttpClient import io.ktor.client.call.body @@ -33,7 +33,7 @@ import kotlin.math.roundToLong import kotlin.time.Clock /** - * Syncs derrakuma data to ATProto PDS. + * Syncs derakkuma data to ATProto PDS. * Called after data fetches from MaimaiClient when ATProto is linked + toggled on. */ class AtProtoSync( @@ -55,7 +55,7 @@ class AtProtoSync( if (!profile.courseImageUrl.isNullOrBlank() && courseImage == null) return false if (!profile.classImageUrl.isNullOrBlank() && classImage == null) return false val record = buildJsonObject { - put("\$type", "com.derrakuma.profile") + put("\$type", "com.derakkuma.profile") put("playerName", profile.name) put("title", profile.title) put("rating", profile.rating) @@ -71,8 +71,8 @@ class AtProtoSync( put("createdAt", syncState.lastSyncTime.ifEmpty { Clock.System.now().toString() }) put("updatedAt", Clock.System.now().toString()) } - val ok = client.putRecord("com.derrakuma.profile", "self", record) - if (!ok) cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derrakuma.profile", "self", record)) + val ok = client.putRecord("com.derakkuma.profile", "self", record) + if (!ok) cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derakkuma.profile", "self", record)) return ok } @@ -81,7 +81,7 @@ class AtProtoSync( val coverArt = uploadImageBlob(play.albumArtUrl) if (!play.albumArtUrl.isNullOrBlank() && coverArt == null) return false val record = buildJsonObject { - put("\$type", "com.derrakuma.play") + put("\$type", "com.derakkuma.play") put("songName", play.name) put("difficulty", play.difficulty.toAtProtoDifficulty()) put("level", play.level) @@ -98,10 +98,10 @@ class AtProtoSync( coverArt?.let { put("coverArt", it) } put("createdAt", Clock.System.now().toString()) } - val ok = client.createRecord("com.derrakuma.play", record) != null + val ok = client.createRecord("com.derakkuma.play", record) != null if (!ok) { cacheStore?.putAtProtoLastError(client.lastError.orEmpty()) - cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("create", "com.derrakuma.play", record = record)) + cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("create", "com.derakkuma.play", record = record)) } return ok } @@ -111,7 +111,7 @@ class AtProtoSync( val coverArt = uploadImageBlob(detail.albumArtUrl) if (!detail.albumArtUrl.isNullOrBlank() && coverArt == null) return false val record = buildJsonObject { - put("\$type", "com.derrakuma.play") + put("\$type", "com.derakkuma.play") put("songName", detail.name) put("difficulty", detail.difficulty.toAtProtoDifficulty()) put("level", detail.level) @@ -135,10 +135,10 @@ class AtProtoSync( detail.ratingDelta?.let { put("ratingDelta", it) } put("createdAt", Clock.System.now().toString()) } - val ok = client.createRecord("com.derrakuma.play", record) != null + val ok = client.createRecord("com.derakkuma.play", record) != null if (!ok) { cacheStore?.putAtProtoLastError(client.lastError.orEmpty()) - cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("create", "com.derrakuma.play", record = record)) + cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("create", "com.derakkuma.play", record = record)) } return ok } @@ -150,7 +150,7 @@ class AtProtoSync( val coverArtBlob = uploadImageBlob(coverArt) if (!coverArt.isNullOrBlank() && coverArtBlob == null) return false val record = buildJsonObject { - put("\$type", "com.derrakuma.best") + put("\$type", "com.derakkuma.best") put("songName", songName) put("difficulty", normalizedDifficulty) put("level", score.level) @@ -172,8 +172,8 @@ class AtProtoSync( put("createdAt", syncState.lastSyncTime.ifEmpty { Clock.System.now().toString() }) put("updatedAt", Clock.System.now().toString()) } - val ok = client.putRecord("com.derrakuma.best", rkey, record) - if (!ok) cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derrakuma.best", rkey, record)) + val ok = client.putRecord("com.derakkuma.best", rkey, record) + if (!ok) cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derakkuma.best", rkey, record)) return ok } @@ -184,7 +184,7 @@ class AtProtoSync( val coverArtBlob = uploadImageBlob(play.albumArtUrl) if (!play.albumArtUrl.isNullOrBlank() && coverArtBlob == null) return false val record = buildJsonObject { - put("\$type", "com.derrakuma.best") + put("\$type", "com.derakkuma.best") put("songName", play.name) put("difficulty", normalizedDifficulty) put("level", play.level) @@ -199,8 +199,8 @@ class AtProtoSync( put("createdAt", syncState.lastSyncTime.ifEmpty { Clock.System.now().toString() }) put("updatedAt", Clock.System.now().toString()) } - val ok = client.putRecord("com.derrakuma.best", rkey, record) - if (!ok) cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derrakuma.best", rkey, record)) + val ok = client.putRecord("com.derakkuma.best", rkey, record) + if (!ok) cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derakkuma.best", rkey, record)) return ok } @@ -215,7 +215,7 @@ class AtProtoSync( val rkey = subjectDid?.let { hashedRkey("did", it) } ?: friendRkey(friend) val record = buildJsonObject { - put("\$type", "com.derrakuma.friend") + put("\$type", "com.derakkuma.friend") subjectDid?.takeIf { it.isNotBlank() }?.let { put("subject", it) } put("displayName", friend.name) friend.title.takeIf { it.isNotBlank() }?.let { put("title", it) } @@ -232,10 +232,10 @@ class AtProtoSync( put("createdAt", Clock.System.now().toString()) put("updatedAt", Clock.System.now().toString()) } - val ok = client.putRecord("com.derrakuma.friend", rkey, record) + val ok = client.putRecord("com.derakkuma.friend", rkey, record) if (!ok) { cacheStore?.putAtProtoLastError(client.lastError ?: "Failed to publish friend ${friend.name}") - cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derrakuma.friend", rkey, record)) + cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derakkuma.friend", rkey, record)) } return ok } @@ -256,7 +256,7 @@ class AtProtoSync( val rkey = favoriteSongRkey(song) val now = Clock.System.now().toString() val record = buildJsonObject { - put("\$type", "com.derrakuma.favoriteSong") + put("\$type", "com.derakkuma.favoriteSong") put("songName", song.name) song.artist.takeIf { it.isNotBlank() }?.let { put("artist", it) } song.musicId.takeIf { it.isNotBlank() }?.let { put("musicId", it) } @@ -268,8 +268,8 @@ class AtProtoSync( put("createdAt", syncState.lastSyncTime.ifEmpty { now }) put("updatedAt", now) } - val ok = client.putRecord("com.derrakuma.favoriteSong", rkey, record) - if (!ok) cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derrakuma.favoriteSong", rkey, record)) + val ok = client.putRecord("com.derakkuma.favoriteSong", rkey, record) + if (!ok) cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derakkuma.favoriteSong", rkey, record)) return ok } @@ -286,7 +286,7 @@ class AtProtoSync( if (!home.backgroundImageUrl.isNullOrBlank() && backgroundImage == null) return false val now = Clock.System.now().toString() val record = buildJsonObject { - put("\$type", "com.derrakuma.circle") + put("\$type", "com.derakkuma.circle") put("circleKey", key) home.code.takeIf { it.isNotBlank() }?.let { put("circleCode", it) } put("name", home.name) @@ -306,8 +306,8 @@ class AtProtoSync( put("updatedAt", now) } val rkey = deterministicRkey("circle", key) - val ok = client.putRecord("com.derrakuma.circle", rkey, record) - if (!ok) cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derrakuma.circle", rkey, record)) + val ok = client.putRecord("com.derakkuma.circle", rkey, record) + if (!ok) cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derakkuma.circle", rkey, record)) return ok } @@ -325,7 +325,7 @@ class AtProtoSync( circleCode = home.code, member = member, subjectDid = if (isSelf) account.did else null, - profileUri = if (isSelf) "at://${account.did}/com.derrakuma.profile/self" else null, + profileUri = if (isSelf) "at://${account.did}/com.derakkuma.profile/self" else null, rkey = if (isSelf) "self" else null, ) ) { @@ -349,7 +349,7 @@ class AtProtoSync( val now = Clock.System.now().toString() val memberRkey = rkey ?: deterministicRkey("cm", "$circleKey|${member.name}|${member.title}|${member.rating}") val record = buildJsonObject { - put("\$type", "com.derrakuma.circleMember") + put("\$type", "com.derakkuma.circleMember") put("circleKey", circleKey) circleCode.takeIf { it.isNotBlank() }?.let { put("circleCode", it) } subjectDid?.takeIf { it.isNotBlank() }?.let { put("subject", it) } @@ -364,8 +364,8 @@ class AtProtoSync( put("createdAt", syncState.lastSyncTime.ifEmpty { now }) put("updatedAt", now) } - val ok = client.putRecord("com.derrakuma.circleMember", memberRkey, record) - if (!ok) cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derrakuma.circleMember", memberRkey, record)) + val ok = client.putRecord("com.derakkuma.circleMember", memberRkey, record) + if (!ok) cacheStore?.enqueueAtProtoWrite(PendingAtProtoWrite("put", "com.derakkuma.circleMember", memberRkey, record)) return ok } diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoSyncHelpers.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoSyncHelpers.kt index b486cdc..c9c224c 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoSyncHelpers.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/AtProtoSyncHelpers.kt @@ -1,21 +1,21 @@ -package com.derrakuma.atproto - -import com.derrakuma.data.CacheStore -import com.derrakuma.data.CircleHome -import com.derrakuma.data.CircleMembersPage -import com.derrakuma.data.DashboardData -import com.derrakuma.data.FavoriteList -import com.derrakuma.data.FavoriteSong -import com.derrakuma.data.FriendsPage -import com.derrakuma.data.MaimaiClient -import com.derrakuma.data.MaimaiUserCard -import com.derrakuma.data.MusicDetail -import com.derrakuma.data.PlayCount -import com.derrakuma.data.PlayLogDetail -import com.derrakuma.data.PlayStore -import com.derrakuma.data.RatingData -import com.derrakuma.data.RecentPlay -import com.derrakuma.data.SessionExpiredException +package com.derakkuma.atproto + +import com.derakkuma.data.CacheStore +import com.derakkuma.data.CircleHome +import com.derakkuma.data.CircleMembersPage +import com.derakkuma.data.DashboardData +import com.derakkuma.data.FavoriteList +import com.derakkuma.data.FavoriteSong +import com.derakkuma.data.FriendsPage +import com.derakkuma.data.MaimaiClient +import com.derakkuma.data.MaimaiUserCard +import com.derakkuma.data.MusicDetail +import com.derakkuma.data.PlayCount +import com.derakkuma.data.PlayLogDetail +import com.derakkuma.data.PlayStore +import com.derakkuma.data.RatingData +import com.derakkuma.data.RecentPlay +import com.derakkuma.data.SessionExpiredException import io.github.samuolis.posthog.PostHog import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive @@ -95,7 +95,7 @@ suspend fun syncFavoriteListToAtProto( val flushed = flushAtProtoQueue(client, cacheStore) val sync = AtProtoSync(client, settings, previousState, cacheStore) val songs = dedupeFavoriteSongs(sync, favorites.songs).take(MAX_RECORDS_PER_SYNC) - val existingFavorites = client.listRecords("com.derrakuma.favoriteSong", limit = 1_000) + val existingFavorites = client.listRecords("com.derakkuma.favoriteSong", limit = 1_000) .mapNotNull { record -> record.stringField("\$rkey")?.let { it to record } } .toMap() val currentRkeys = songs.map { sync.favoriteSongRkey(it) }.toSet() @@ -133,7 +133,7 @@ suspend fun syncFriendsPageToAtProto( val flushed = flushAtProtoQueue(client, cacheStore) val sync = AtProtoSync(client, settings, previousState, cacheStore) val friends = dedupeFriends(sync, friendsPage.friends).take(MAX_RECORDS_PER_SYNC) - val existingFriends = client.listRecords("com.derrakuma.friend", limit = 1_000) + val existingFriends = client.listRecords("com.derakkuma.friend", limit = 1_000) .mapNotNull { record -> record.stringField("\$rkey")?.let { it to record } } .toMap() val currentFriendRkeys = friends.associateWith { sync.friendRkey(it) } @@ -224,7 +224,7 @@ suspend fun syncDashboardToAtProto( var failedPlays = 0 var currentCookie = cookie if (settings.publishPlays) { - val existingPlayKeys = client.listRecords("com.derrakuma.play", limit = 1_000) + val existingPlayKeys = client.listRecords("com.derakkuma.play", limit = 1_000) .mapNotNull { it.playDedupeKey() } .toMutableSet() val playsToPublish = dashboard.playLog @@ -285,7 +285,7 @@ suspend fun syncDashboardToAtProto( } if (settings.publishBests && playStore != null) { - val existingBestRecords = client.listRecords("com.derrakuma.best", limit = 1_000) + val existingBestRecords = client.listRecords("com.derakkuma.best", limit = 1_000) val existingBestKeys = existingBestRecords.mapNotNull { it.bestDedupeKey() }.toSet() val existingBestRkeys = existingBestRecords.mapNotNull { it.stringField("\$rkey") }.toSet() val historicalBests = playStore.getHistoricalPlays() @@ -315,7 +315,7 @@ suspend fun syncDashboardToAtProto( ).onFailure { cacheStore.putAtProtoLastError("Failed to fetch friends: ${it.message ?: it::class.simpleName}") } .getOrNull() val friends = dedupeFriends(sync, friendsPage?.friends.orEmpty()).take(MAX_RECORDS_PER_SYNC) - val existingFriends = client.listRecords("com.derrakuma.friend", limit = 1_000) + val existingFriends = client.listRecords("com.derakkuma.friend", limit = 1_000) .mapNotNull { record -> record.stringField("\$rkey")?.let { it to record } } .toMap() val currentFriendRkeys = friends.associateWith { sync.friendRkey(it) } @@ -341,7 +341,7 @@ suspend fun syncDashboardToAtProto( ).onFailure { cacheStore.putAtProtoLastError("Failed to fetch favorite songs: ${it.message ?: it::class.simpleName}") } .getOrNull() val songs = dedupeFavoriteSongs(sync, favorites?.songs.orEmpty()).take(MAX_RECORDS_PER_SYNC) - val existingFavorites = client.listRecords("com.derrakuma.favoriteSong", limit = 1_000) + val existingFavorites = client.listRecords("com.derakkuma.favoriteSong", limit = 1_000) .mapNotNull { record -> record.stringField("\$rkey")?.let { it to record } } .toMap() val currentFavoriteRkeys = songs.map { sync.favoriteSongRkey(it) }.toSet() @@ -413,11 +413,11 @@ private suspend fun reconcileFavoriteSongDeletes( cacheStore: CacheStore, currentRkeys: Set, ) { - val existing = client.listRecords("com.derrakuma.favoriteSong", limit = 1_000) + val existing = client.listRecords("com.derakkuma.favoriteSong", limit = 1_000) for (record in existing) { val rkey = record.stringField("\$rkey") ?: continue if (rkey in currentRkeys) continue - if (!client.deleteRecord("com.derrakuma.favoriteSong", rkey)) { + if (!client.deleteRecord("com.derakkuma.favoriteSong", rkey)) { cacheStore.putAtProtoLastError(client.lastError ?: "Failed to delete old favorite $rkey") } } @@ -447,13 +447,13 @@ private suspend fun reconcileFriendDuplicates( val currentFingerprints = currentRkeys.keys.map { it.friendFingerprint() }.toSet() if (currentFingerprints.isEmpty()) return - val existing = client.listRecords("com.derrakuma.friend", limit = 1_000) + val existing = client.listRecords("com.derakkuma.friend", limit = 1_000) for (record in existing) { val rkey = record.stringField("\$rkey") ?: continue if (rkey in currentRkeySet) continue if (record.stringField("subject") != null) continue if (record.friendFingerprint() !in currentFingerprints) continue - if (!client.deleteRecord("com.derrakuma.friend", rkey)) { + if (!client.deleteRecord("com.derakkuma.friend", rkey)) { cacheStore.putAtProtoLastError(client.lastError ?: "Failed to delete duplicate friend $rkey") } } @@ -603,7 +603,7 @@ suspend fun syncPlayDetailToAtProto( if (cacheStore.getSetting(publishedKey) == "true") return val detailPlayKey = playDedupeKey(detail.datetime, detail.name, detail.difficulty) - val alreadyPublished = client.listRecords("com.derrakuma.play", limit = 1_000) + val alreadyPublished = client.listRecords("com.derakkuma.play", limit = 1_000) .mapNotNull { it.playDedupeKey() } .any { it == detailPlayKey } if (alreadyPublished) { @@ -679,7 +679,7 @@ suspend fun deleteAtProtoFriend( cacheStore = cacheStore, ) val candidateRkeys = setOf(sync.existingFriendRkey(friend)) - val remoteRkeys = client.listRecords("com.derrakuma.friend", limit = 1_000) + val remoteRkeys = client.listRecords("com.derakkuma.friend", limit = 1_000) .filter { record -> val rkey = record.stringField("\$rkey") ?: return@filter false rkey in candidateRkeys || @@ -694,7 +694,7 @@ suspend fun deleteAtProtoFriend( var ok = true for (rkey in remoteRkeys) { - if (!client.deleteRecord("com.derrakuma.friend", rkey)) { + if (!client.deleteRecord("com.derakkuma.friend", rkey)) { ok = false cacheStore.putAtProtoLastError(client.lastError ?: "Failed to delete friend record $rkey") } diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/HappyViewSocialClient.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/HappyViewSocialClient.kt index 1d82ff1..c1f93ac 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/HappyViewSocialClient.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/HappyViewSocialClient.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto import io.ktor.client.HttpClient import io.ktor.client.request.get @@ -8,7 +8,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject -private const val HAPPYVIEW_BASE_URL = "https://milk.derrakuma.com" +private const val HAPPYVIEW_BASE_URL = "https://milk.derakkuma.com" private const val HAPPYVIEW_CLIENT_KEY = "hvc_cfd543519c512cb3328ad4cbf735a542" private val happyViewJson = Json { @@ -22,7 +22,7 @@ class HappyViewSocialClient( ) { suspend fun getFriendsPlays(actor: String, limit: Int = 25): List { val response = getXrpc( - "com.derrakuma.getFriendsPlays", + "com.derakkuma.getFriendsPlays", mapOf("actor" to actor, "limit" to limit.toString()), ) return happyViewJson.decodeFromString(response).records @@ -30,7 +30,7 @@ class HappyViewSocialClient( suspend fun getLeaderboard(songName: String, difficulty: String, limit: Int = 10): List { val response = getXrpc( - "com.derrakuma.getLeaderboard", + "com.derakkuma.getLeaderboard", mapOf( "songName" to songName, "difficulty" to difficulty.toAtProtoDifficultyName(), @@ -41,13 +41,13 @@ class HappyViewSocialClient( } suspend fun getPlayerStats(actor: String): AtProtoPlayerStats { - val response = getXrpc("com.derrakuma.getPlayerStats", mapOf("actor" to actor)) + val response = getXrpc("com.derakkuma.getPlayerStats", mapOf("actor" to actor)) return happyViewJson.decodeFromString(response) } suspend fun searchProfiles(query: String, limit: Int = 20): List { val response = getXrpc( - "com.derrakuma.searchProfiles", + "com.derakkuma.searchProfiles", mapOf("query" to query, "limit" to limit.toString()), ) return happyViewJson.decodeFromString(response).records diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/AtOAuth.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/AtOAuth.kt index 1de4839..d6a7956 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/AtOAuth.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/AtOAuth.kt @@ -1,6 +1,6 @@ -package com.derrakuma.atproto.vendor.oauth +package com.derakkuma.atproto.vendor.oauth -import com.derrakuma.atproto.vendor.runtime.XrpcClient +import com.derakkuma.atproto.vendor.runtime.XrpcClient import dev.whyoleg.cryptography.random.CryptographyRandom import io.ktor.client.HttpClient import io.ktor.client.request.forms.submitForm diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DerDpopKeyPairSerializer.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DerDpopKeyPairSerializer.kt index 57f22da..b03eed2 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DerDpopKeyPairSerializer.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DerDpopKeyPairSerializer.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.oauth +package com.derakkuma.atproto.vendor.oauth import kotlinx.serialization.KSerializer import kotlinx.serialization.builtins.ListSerializer diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DiscoveryChain.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DiscoveryChain.kt index 0011f9a..1014aa9 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DiscoveryChain.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DiscoveryChain.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.oauth +package com.derakkuma.atproto.vendor.oauth import io.ktor.client.HttpClient import io.ktor.client.request.get diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DpopAuthProvider.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DpopAuthProvider.kt index a985d13..2343936 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DpopAuthProvider.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DpopAuthProvider.kt @@ -1,6 +1,6 @@ -package com.derrakuma.atproto.vendor.oauth +package com.derakkuma.atproto.vendor.oauth -import com.derrakuma.atproto.vendor.runtime.AuthProvider +import com.derakkuma.atproto.vendor.runtime.AuthProvider import io.ktor.client.HttpClient import io.ktor.client.request.forms.submitForm import io.ktor.client.statement.bodyAsText diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DpopKeyPair.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DpopKeyPair.kt index 18d1610..523707f 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DpopKeyPair.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DpopKeyPair.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.oauth +package com.derakkuma.atproto.vendor.oauth import dev.whyoleg.cryptography.CryptographyProvider import dev.whyoleg.cryptography.CryptographyProviderApi diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DpopSigner.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DpopSigner.kt index 5f4b746..041d66c 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DpopSigner.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/DpopSigner.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.oauth +package com.derakkuma.atproto.vendor.oauth import dev.whyoleg.cryptography.algorithms.ECDSA import dev.whyoleg.cryptography.algorithms.SHA256 diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/OAuthDiscoveryException.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/OAuthDiscoveryException.kt index 02a2d19..7b8b80a 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/OAuthDiscoveryException.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/OAuthDiscoveryException.kt @@ -1,3 +1,3 @@ -package com.derrakuma.atproto.vendor.oauth +package com.derakkuma.atproto.vendor.oauth class OAuthDiscoveryException(message: String, cause: Throwable? = null) : RuntimeException(message, cause) diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/OAuthExceptions.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/OAuthExceptions.kt index 6ae3219..7fca86b 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/OAuthExceptions.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/OAuthExceptions.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.oauth +package com.derakkuma.atproto.vendor.oauth class OAuthAccountMismatchException(message: String) : RuntimeException(message) diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/OAuthSession.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/OAuthSession.kt index 38ee0d0..e96606f 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/OAuthSession.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/OAuthSession.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.oauth +package com.derakkuma.atproto.vendor.oauth import kotlinx.serialization.Serializable diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/Pkce.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/Pkce.kt index e437beb..fb1c13e 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/Pkce.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/oauth/Pkce.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.oauth +package com.derakkuma.atproto.vendor.oauth import dev.whyoleg.cryptography.algorithms.SHA256 import dev.whyoleg.cryptography.random.CryptographyRandom diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/AtField.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/AtField.kt index 25487d4..d4a7972 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/AtField.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/AtField.kt @@ -1,6 +1,6 @@ @file:OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) -package com.derrakuma.atproto.vendor.runtime +package com.derakkuma.atproto.vendor.runtime import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/AtUriParser.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/AtUriParser.kt index 44a63f5..815f0d4 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/AtUriParser.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/AtUriParser.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.runtime +package com.derakkuma.atproto.vendor.runtime /** * The structural decomposition of an [AtUri] into its constituent parts. diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/AuthProvider.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/AuthProvider.kt index 0d42429..fae5cad 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/AuthProvider.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/AuthProvider.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.runtime +package com.derakkuma.atproto.vendor.runtime /** * Supplies authentication headers for XRPC requests. Attached at client diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/Blob.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/Blob.kt index d97439f..05fdd13 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/Blob.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/Blob.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.runtime +package com.derakkuma.atproto.vendor.runtime import kotlinx.serialization.EncodeDefault import kotlinx.serialization.ExperimentalSerializationApi diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/NoXrpcParams.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/NoXrpcParams.kt index 898338f..0804848 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/NoXrpcParams.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/NoXrpcParams.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.runtime +package com.derakkuma.atproto.vendor.runtime import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/OpenUnion.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/OpenUnion.kt index 485bfa0..3c8904b 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/OpenUnion.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/OpenUnion.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.runtime +package com.derakkuma.atproto.vendor.runtime import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException @@ -149,7 +149,7 @@ public abstract class OpenUnionSerializer( ) : KSerializer { override val descriptor: SerialDescriptor = buildClassSerialDescriptor( - "com.derrakuma.atproto.vendor.runtime.OpenUnion<${baseClass.simpleName ?: "?"}>", + "com.derakkuma.atproto.vendor.runtime.OpenUnion<${baseClass.simpleName ?: "?"}>", ) /** diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/Pagination.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/Pagination.kt index 5f61c2f..e6f473a 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/Pagination.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/Pagination.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.runtime +package com.derakkuma.atproto.vendor.runtime import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/RecordDecoder.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/RecordDecoder.kt index d786f1b..b801e24 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/RecordDecoder.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/RecordDecoder.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.runtime +package com.derakkuma.atproto.vendor.runtime import kotlinx.serialization.KSerializer import kotlinx.serialization.json.Json diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/StringFormats.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/StringFormats.kt index 3f5e2ad..b39e900 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/StringFormats.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/StringFormats.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.runtime +package com.derakkuma.atproto.vendor.runtime import kotlinx.serialization.Serializable import kotlin.jvm.JvmInline diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/XrpcClient.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/XrpcClient.kt index dd964d1..38e5902 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/XrpcClient.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/XrpcClient.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.runtime +package com.derakkuma.atproto.vendor.runtime import io.ktor.client.HttpClient import io.ktor.client.request.HttpRequestBuilder diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/XrpcError.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/XrpcError.kt index 97f7364..17e3713 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/XrpcError.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/atproto/vendor/runtime/XrpcError.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.runtime +package com.derakkuma.atproto.vendor.runtime import kotlinx.serialization.Serializable diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/data/AuthStore.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/data/AuthStore.kt index 4a7e580..0aa7ed2 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/data/AuthStore.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/data/AuthStore.kt @@ -1,4 +1,4 @@ -package com.derrakuma.data +package com.derakkuma.data data class AuthState( val cookie: String, diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/data/CacheStore.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/data/CacheStore.kt index 25766e8..5a9e569 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/data/CacheStore.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/data/CacheStore.kt @@ -1,7 +1,7 @@ -package com.derrakuma.data +package com.derakkuma.data import app.cash.sqldelight.db.SqlDriver -import com.derrakuma.db.DerrakumaDb +import com.derakkuma.db.DerakkumaDb import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @@ -36,7 +36,7 @@ expect class DriverFactory { class CacheStore(driverFactory: DriverFactory? = null) { private val enabled = driverFactory != null - private val db by lazy { DerrakumaDb(driverFactory!!.createDriver()) } + private val db by lazy { DerakkumaDb(driverFactory!!.createDriver()) } private val queries by lazy { db.cacheQueries } private val json = Json { ignoreUnknownKeys = true } diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/data/HistoricalCrawler.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/data/HistoricalCrawler.kt index cc57111..fdaf929 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/data/HistoricalCrawler.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/data/HistoricalCrawler.kt @@ -1,4 +1,4 @@ -package com.derrakuma.data +package com.derakkuma.data import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/data/MaimaiClient.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/data/MaimaiClient.kt index cd8142b..5b8daaa 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/data/MaimaiClient.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/data/MaimaiClient.kt @@ -1,4 +1,4 @@ -package com.derrakuma.data +package com.derakkuma.data import io.ktor.client.* import io.ktor.client.call.* diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/data/MaintenanceMode.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/data/MaintenanceMode.kt index 5831d57..874a7a3 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/data/MaintenanceMode.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/data/MaintenanceMode.kt @@ -1,4 +1,4 @@ -package com.derrakuma.data +package com.derakkuma.data import kotlin.time.Clock import kotlin.time.Instant diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/data/Models.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/data/Models.kt index 9ffb3cf..d9a609a 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/data/Models.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/data/Models.kt @@ -1,4 +1,4 @@ -package com.derrakuma.data +package com.derakkuma.data import kotlinx.serialization.Serializable diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/data/Parsers.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/data/Parsers.kt index 0282824..d780540 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/data/Parsers.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/data/Parsers.kt @@ -1,4 +1,4 @@ -package com.derrakuma.data +package com.derakkuma.data import kotlinx.serialization.Serializable diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/data/PlayStore.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/data/PlayStore.kt index c2cc31e..84dbc10 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/data/PlayStore.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/data/PlayStore.kt @@ -1,8 +1,8 @@ -package com.derrakuma.data +package com.derakkuma.data import app.cash.sqldelight.db.SqlDriver -import com.derrakuma.db.DerrakumaDb -import com.derrakuma.db.Play_history +import com.derakkuma.db.DerakkumaDb +import com.derakkuma.db.Play_history import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import kotlinx.coroutines.withContext @@ -16,7 +16,7 @@ import kotlin.time.Clock * Combines recent plays (90-day log) with historical plays (backfilled from musicSort/musicMybest). */ class PlayStore(driverFactory: DriverFactory) { - private val db by lazy { DerrakumaDb(driverFactory.createDriver()) } + private val db by lazy { DerakkumaDb(driverFactory.createDriver()) } private val queries by lazy { db.playHistoryQueries } private val json = Json { ignoreUnknownKeys = true } diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/data/SongDatabase.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/data/SongDatabase.kt index 0ec2431..f11c2cb 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/data/SongDatabase.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/data/SongDatabase.kt @@ -1,4 +1,4 @@ -package com.derrakuma.data +package com.derakkuma.data import io.ktor.client.* import io.ktor.client.call.* diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/data/UserOption.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/data/UserOption.kt index fcf0a11..51cbe53 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/data/UserOption.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/data/UserOption.kt @@ -1,4 +1,4 @@ -package com.derrakuma.data +package com.derakkuma.data /** * Represents a single setting field parsed from the updateUserOption form. @@ -106,7 +106,7 @@ private val OPTION_LABEL_OVERRIDES: Map> = mapOf( 12 to "Splash", ), "breakSe" to mapOf( - 8 to "DERRAKUMA", + 8 to "DERAKKUMA", 9 to "LEMON BEAR & LIME BEAR", 10 to "SHAMA 1", 11 to "SHAMA 2", @@ -123,7 +123,7 @@ private val OPTION_LABEL_OVERRIDES: Map> = mapOf( ), "slideSe" to mapOf( 0 to "DEFAULT", - 8 to "DERRAKUMA", + 8 to "DERAKKUMA", 9 to "LEMON BEAR & LIME BEAR", 10 to "SHAMA 1", 11 to "SHAMA 2", diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/kamaitachi/KamaitachiClient.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/kamaitachi/KamaitachiClient.kt index e616a3a..ec08f07 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/kamaitachi/KamaitachiClient.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/kamaitachi/KamaitachiClient.kt @@ -1,15 +1,15 @@ -package com.derrakuma.kamaitachi +package com.derakkuma.kamaitachi import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import com.derrakuma.atproto.openOAuthUrl -import com.derrakuma.data.CacheStore -import com.derrakuma.data.MaimaiClient -import com.derrakuma.data.NoteBreakdown -import com.derrakuma.data.PlayLogDetail -import com.derrakuma.data.PlayStore -import com.derrakuma.data.RecentPlay +import com.derakkuma.atproto.openOAuthUrl +import com.derakkuma.data.CacheStore +import com.derakkuma.data.MaimaiClient +import com.derakkuma.data.NoteBreakdown +import com.derakkuma.data.PlayLogDetail +import com.derakkuma.data.PlayStore +import com.derakkuma.data.RecentPlay import io.ktor.client.HttpClient import io.ktor.client.request.bearerAuth import io.ktor.client.request.get @@ -18,6 +18,7 @@ import io.ktor.client.request.post import io.ktor.client.request.setBody import io.ktor.client.statement.bodyAsText import io.ktor.http.ContentType +import io.ktor.http.content.ByteArrayContent import io.ktor.http.content.TextContent import io.ktor.http.contentType import kotlinx.datetime.LocalDateTime @@ -36,7 +37,7 @@ import kotlinx.serialization.json.jsonObject private const val KAMAI_BASE_URL = "https://kamai.tachi.ac" private const val KAMAI_CLIENT_ID = "CI3ac34de46e2c566f7187782c26831fa3d829f589" private const val KAMAI_CLIENT_SECRET = "CSe6886f0679a0a22f10448ff22d27f19736fa33c9" -private const val KAMAI_REDIRECT_URI = "com.derrakuma:/kamai" +private const val KAMAI_REDIRECT_URI = "com.derakkuma:/kamai" private const val MAX_RECENT_IMPORT = 50 private val kamaiJson = Json { @@ -102,7 +103,20 @@ class KamaitachiClient( val recentPlays = (cacheStore.getPlayLogForce() ?: maimaiClient.fetchDashboard(cookie).playLog) .filter { it.detailIdx.isNotBlank() } .take(MAX_RECENT_IMPORT) - val historicalPlays = playStore.getHistoricalPlays() + val recentKeys = mutableSetOf() + val recentTimes = mutableMapOf() + for (play in recentPlays) { + val key = "${play.datetime.split(" ")[0]}:${play.name}:${play.difficulty}" + recentKeys.add(key) + recentTimes[key] = parseDatetimeToMinutes(play.datetime) + } + val historicalPlays = playStore.getHistoricalPlays().filter { play -> + val key = "${play.datetime.split(" ")[0]}:${play.songName}:${play.difficulty}" + if (key !in recentKeys) return@filter true + val recentMinutes = recentTimes[key] ?: 0L + val historicalMinutes = parseDatetimeToMinutes(play.datetime) + kotlin.math.abs(recentMinutes - historicalMinutes) > 20 + } val total = recentPlays.size + historicalPlays.size if (total == 0) throw IllegalStateException("No records to sync") @@ -150,11 +164,14 @@ class KamaitachiClient( val request = KamaitachiImportRequest( scores = scores, ) + val payload = kamaiJson.encodeToString(request) + val hasMeta = payload.contains("\"meta\"") + println("[kamai] submitting batch-manual: scores=${scores.size}, hasMeta=$hasMeta, prefix=${payload.take(120)}") val text = httpClient.post("$KAMAI_BASE_URL/ir/direct-manual/import") { bearerAuth(token) header("X-User-Intent", "true") contentType(ContentType.Application.Json) - setBody(TextContent(kamaiJson.encodeToString(request), ContentType.Application.Json)) + setBody(ByteArrayContent(payload.encodeToByteArray(), ContentType.Application.Json)) }.bodyAsText() val response = kamaiJson.parseToJsonElement(text).jsonObject if (response.boolean("success") == false) { @@ -228,7 +245,7 @@ private data class KamaitachiImportRequest( private data class KamaitachiImportMeta( val game: String = "maimaidx", val playtype: String = "Single", - val service: String = "derrakuma", + val service: String = "derakkuma", ) @Serializable @@ -342,6 +359,22 @@ private fun parseMaimaiTimestampMillis(value: String): Long? { ).toInstant(TimeZone.of("Asia/Tokyo")).toEpochMilliseconds() } +/** Match Records-tab recent-vs-historical dedupe: dashboard time and music-detail time can drift by minutes. */ +private fun parseDatetimeToMinutes(datetime: String): Long { + val parts = datetime.trim().split(" ") + if (parts.size < 2) return 0L + val dateParts = parts[0].split("/") + val timeParts = parts[1].split(":") + if (dateParts.size != 3 || timeParts.size != 2) return 0L + val year = dateParts[0].toIntOrNull() ?: 0 + val month = dateParts[1].toIntOrNull() ?: 0 + val day = dateParts[2].toIntOrNull() ?: 0 + val hour = timeParts[0].toIntOrNull() ?: 0 + val minute = timeParts[1].toIntOrNull() ?: 0 + val daysSince2020 = (year - 2020) * 365 + (month - 1) * 30 + day + return daysSince2020 * 24 * 60L + hour * 60L + minute +} + private fun JsonObject.string(key: String): String? = (this[key] as? JsonPrimitive)?.contentOrNull private fun JsonObject.boolean(key: String): Boolean? = (this[key] as? JsonPrimitive)?.booleanOrNull diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/kamaitachi/KamaitachiRegistry.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/kamaitachi/KamaitachiRegistry.kt index 3a7cf77..05bae4e 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/kamaitachi/KamaitachiRegistry.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/kamaitachi/KamaitachiRegistry.kt @@ -1,3 +1,3 @@ -package com.derrakuma.kamaitachi +package com.derakkuma.kamaitachi expect fun registerKamaitachiClient(client: KamaitachiClient) diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/auth/AuthScreen.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/auth/AuthScreen.kt index e281950..46f87f4 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/auth/AuthScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/auth/AuthScreen.kt @@ -1,7 +1,7 @@ -package com.derrakuma.ui.auth +package com.derakkuma.ui.auth import androidx.compose.runtime.Composable -import com.derrakuma.data.AuthState +import com.derakkuma.data.AuthState @Composable expect fun AuthScreen(onLoginSuccess: (AuthState) -> Unit, sessionExpired: Boolean = false) diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/LoadingContent.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/LoadingContent.kt index 6b20889..c719299 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/LoadingContent.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/LoadingContent.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.components +package com.derakkuma.ui.components import androidx.compose.foundation.layout.* import androidx.compose.material3.* @@ -8,7 +8,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.ui.theme.* +import com.derakkuma.ui.theme.* /** * Shared loading/error/empty content wrapper. diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/MaintenanceBanner.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/MaintenanceBanner.kt index 8197146..71e11db 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/MaintenanceBanner.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/MaintenanceBanner.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.components +package com.derakkuma.ui.components import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -24,10 +24,10 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.data.MaintenanceMode -import com.derrakuma.ui.images.MaimaiAssets -import com.derrakuma.ui.theme.CardWhite -import com.derrakuma.ui.theme.DangerRed +import com.derakkuma.data.MaintenanceMode +import com.derakkuma.ui.images.MaimaiAssets +import com.derakkuma.ui.theme.CardWhite +import com.derakkuma.ui.theme.DangerRed import kotlinx.coroutines.delay @Composable diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/PlayHeatmap.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/PlayHeatmap.kt index 9a137b1..f0e9657 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/PlayHeatmap.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/PlayHeatmap.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.components +package com.derakkuma.ui.components import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn @@ -16,8 +16,8 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.data.RecentPlay -import com.derrakuma.ui.theme.* +import com.derakkuma.data.RecentPlay +import com.derakkuma.ui.theme.* import com.fleeys.heatmap.HeatMap import com.fleeys.heatmap.model.Heat import com.fleeys.heatmap.style.DaysLabelStyle diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/SongRow.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/SongRow.kt index cb1e610..b82404d 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/SongRow.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/components/SongRow.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.components +package com.derakkuma.ui.components import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -14,9 +14,9 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.ui.images.* -import com.derrakuma.ui.theme.* -import com.derrakuma.util.formatScore +import com.derakkuma.ui.images.* +import com.derakkuma.ui.theme.* +import com.derakkuma.util.formatScore private const val IMG_BASE = "https://maimaidx-eng.com/maimai-mobile/img" diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/icons/MaterialSymbols.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/icons/MaterialSymbols.kt index 0ed7f3b..629cb02 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/icons/MaterialSymbols.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/icons/MaterialSymbols.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.icons +package com.derakkuma.ui.icons import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/images/CoilSetup.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/images/CoilSetup.kt index 289aac5..612f615 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/images/CoilSetup.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/images/CoilSetup.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.images +package com.derakkuma.ui.images import coil3.ImageLoader import coil3.PlatformContext diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/images/MaimaiImages.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/images/MaimaiImages.kt index 0dc4cdb..19921ef 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/images/MaimaiImages.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/images/MaimaiImages.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.images +package com.derakkuma.ui.images import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -11,7 +11,7 @@ import androidx.compose.ui.layout.ContentScale import coil3.compose.AsyncImage import coil3.compose.LocalPlatformContext import coil3.request.ImageRequest -import derrakuma.composeapp.generated.resources.Res +import derakkuma.composeapp.generated.resources.Res import kotlinx.serialization.Serializable import org.jetbrains.compose.resources.decodeToImageBitmap diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/AppSettingsTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/AppSettingsTab.kt index 1eabd87..6a8cec1 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/AppSettingsTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/AppSettingsTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState @@ -14,8 +14,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.data.CacheStore -import com.derrakuma.ui.theme.* +import com.derakkuma.data.CacheStore +import com.derakkuma.ui.theme.* import io.github.samuolis.posthog.PostHog import secrets.Secrets @@ -171,7 +171,7 @@ fun AppSettingsTab( // Text("Built with", fontSize = 13.sp, color = MutedText) // Text("Compose Multiplatform", fontSize = 13.sp, fontWeight = FontWeight.SemiBold, color = DarkText) // } - Text("Derrakuma is an unofficial client for maimai DX NET. Not affiliated with SEGA.", fontSize = 11.sp, color = MutedText) + Text("Derakkuma is an unofficial client for maimai DX NET. Not affiliated with SEGA.", fontSize = 11.sp, color = MutedText) } } } diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/AtmosphereTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/AtmosphereTab.kt index cb58571..f47b7e4 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/AtmosphereTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/AtmosphereTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -12,12 +12,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.atproto.AtProtoClient -import com.derrakuma.atproto.AtProtoSection -import com.derrakuma.data.CacheStore -import com.derrakuma.data.MaimaiClient -import com.derrakuma.data.PlayStore -import com.derrakuma.ui.theme.DarkText +import com.derakkuma.atproto.AtProtoClient +import com.derakkuma.atproto.AtProtoSection +import com.derakkuma.data.CacheStore +import com.derakkuma.data.MaimaiClient +import com.derakkuma.data.PlayStore +import com.derakkuma.ui.theme.DarkText @Composable fun AtmosphereTab( diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/CollectionTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/CollectionTab.kt index 21139a0..b20812f 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/CollectionTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/CollectionTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState @@ -18,13 +18,13 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.ui.theme.* +import com.derakkuma.ui.theme.* @Composable fun CollectionTab() { // TODO(atproto): when collection items can be applied in-app (icon, partner, // title, frame, etc.), refresh the cached home profile and call - // syncCachedProfileToAtProto so com.derrakuma.profile reflects remote/cab + // syncCachedProfileToAtProto so com.derakkuma.profile reflects remote/cab // profile changes immediately. Column( modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 12.dp, end = 12.dp, top = 12.dp, bottom = 88.dp), diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/FavoritesTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/FavoritesTab.kt index bb109c1..433bbcf 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/FavoritesTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/FavoritesTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -18,11 +18,11 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.atproto.AtProtoClient -import com.derrakuma.atproto.syncFavoriteListToAtProto -import com.derrakuma.data.* -import com.derrakuma.ui.images.* -import com.derrakuma.ui.theme.* +import com.derakkuma.atproto.AtProtoClient +import com.derakkuma.atproto.syncFavoriteListToAtProto +import com.derakkuma.data.* +import com.derakkuma.ui.images.* +import com.derakkuma.ui.theme.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/FriendsTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/FriendsTab.kt index 032939e..b3d0cba 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/FriendsTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/FriendsTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -23,15 +23,15 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.atproto.AtProtoClient -import com.derrakuma.atproto.deleteAtProtoFriend -import com.derrakuma.atproto.syncCirclePageToAtProto -import com.derrakuma.atproto.syncFriendsPageToAtProto -import com.derrakuma.data.* -import com.derrakuma.ui.images.MaimaiRemoteImage -import com.derrakuma.ui.theme.* -import com.derrakuma.ui.util.CopyButton -import com.derrakuma.util.formatScore +import com.derakkuma.atproto.AtProtoClient +import com.derakkuma.atproto.deleteAtProtoFriend +import com.derakkuma.atproto.syncCirclePageToAtProto +import com.derakkuma.atproto.syncFriendsPageToAtProto +import com.derakkuma.data.* +import com.derakkuma.ui.images.MaimaiRemoteImage +import com.derakkuma.ui.theme.* +import com.derakkuma.ui.util.CopyButton +import com.derakkuma.util.formatScore import kotlinx.coroutines.launch private enum class FriendsSection(val label: String) { diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/HomeTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/HomeTab.kt index 084e1eb..78d34c8 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/HomeTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/HomeTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.Image import androidx.compose.foundation.layout.* @@ -20,17 +20,17 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.atproto.AtProtoClient -import com.derrakuma.atproto.syncDashboardToAtProto -import com.derrakuma.data.* -import com.derrakuma.data.CacheStore -import com.derrakuma.data.MaintenanceException -import com.derrakuma.data.RatingData -import com.derrakuma.ui.components.BigStat -import com.derrakuma.ui.components.PlayHeatmap -import com.derrakuma.ui.images.* -import com.derrakuma.ui.theme.* -import com.derrakuma.ui.util.CopyButton +import com.derakkuma.atproto.AtProtoClient +import com.derakkuma.atproto.syncDashboardToAtProto +import com.derakkuma.data.* +import com.derakkuma.data.CacheStore +import com.derakkuma.data.MaintenanceException +import com.derakkuma.data.RatingData +import com.derakkuma.ui.components.BigStat +import com.derakkuma.ui.components.PlayHeatmap +import com.derakkuma.ui.images.* +import com.derakkuma.ui.theme.* +import com.derakkuma.ui.util.CopyButton import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/KamaitachiSettingsTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/KamaitachiSettingsTab.kt index e84d9f0..873bff6 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/KamaitachiSettingsTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/KamaitachiSettingsTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState @@ -12,11 +12,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.data.MaimaiClient -import com.derrakuma.data.PlayStore -import com.derrakuma.kamaitachi.KamaitachiClient -import com.derrakuma.kamaitachi.KamaitachiSyncProgress -import com.derrakuma.ui.theme.* +import com.derakkuma.data.MaimaiClient +import com.derakkuma.data.PlayStore +import com.derakkuma.kamaitachi.KamaitachiClient +import com.derakkuma.kamaitachi.KamaitachiSyncProgress +import com.derakkuma.ui.theme.* import kotlinx.coroutines.launch @Composable diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/MoreTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/MoreTab.kt index b675316..2cf42bb 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/MoreTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/MoreTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -15,12 +15,12 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.ui.icons.Kamaitachi -import com.derrakuma.ui.icons.MapSearch -import com.derrakuma.ui.icons.PersonPinCircle -import com.derrakuma.ui.icons.SocialLeaderboard -import com.derrakuma.ui.theme.* -import com.derrakuma.ui.theme.DangerRed +import com.derakkuma.ui.icons.Kamaitachi +import com.derakkuma.ui.icons.MapSearch +import com.derakkuma.ui.icons.PersonPinCircle +import com.derakkuma.ui.icons.SocialLeaderboard +import com.derakkuma.ui.theme.* +import com.derakkuma.ui.theme.DangerRed @Composable fun MoreMenu( diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/MusicDetailTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/MusicDetailTab.kt index f241319..e8bae1a 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/MusicDetailTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/MusicDetailTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState @@ -13,16 +13,16 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.atproto.AtProtoClient -import com.derrakuma.atproto.AtProtoLeaderboardEntry -import com.derrakuma.atproto.HappyViewSocialClient -import com.derrakuma.atproto.syncMusicDetailToAtProto -import com.derrakuma.atproto.toAtProtoDifficultyName -import com.derrakuma.data.* -import com.derrakuma.data.CacheStore -import com.derrakuma.ui.images.* -import com.derrakuma.ui.theme.* -import com.derrakuma.util.formatScore +import com.derakkuma.atproto.AtProtoClient +import com.derakkuma.atproto.AtProtoLeaderboardEntry +import com.derakkuma.atproto.HappyViewSocialClient +import com.derakkuma.atproto.syncMusicDetailToAtProto +import com.derakkuma.atproto.toAtProtoDifficultyName +import com.derakkuma.data.* +import com.derakkuma.data.CacheStore +import com.derakkuma.ui.images.* +import com.derakkuma.ui.theme.* +import com.derakkuma.util.formatScore @Composable fun MusicDetailTab(client: MaimaiClient, cookie: String, idx: String, maintenanceMode: Boolean = false, cacheStore: CacheStore = CacheStore(), atProtoClient: AtProtoClient? = null) { diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/PlayDetailTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/PlayDetailTab.kt index 5f7cbac..053cf56 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/PlayDetailTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/PlayDetailTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState @@ -13,11 +13,11 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.data.* -import com.derrakuma.data.CacheStore -import com.derrakuma.ui.images.* -import com.derrakuma.ui.theme.* -import com.derrakuma.util.formatScore +import com.derakkuma.data.* +import com.derakkuma.data.CacheStore +import com.derakkuma.ui.images.* +import com.derakkuma.ui.theme.* +import com.derakkuma.util.formatScore @Composable fun PlayDetailTab(client: MaimaiClient, cookie: String, idx: String, onMusicDetail: ((idx: String) -> Unit)? = null, maintenanceMode: Boolean = false, cacheStore: CacheStore = CacheStore(), playStore: PlayStore? = null) { diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/PlayerDataTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/PlayerDataTab.kt index 2c290bd..3e7cfd4 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/PlayerDataTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/PlayerDataTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween @@ -17,11 +17,11 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.data.* -import com.derrakuma.data.CacheStore -import com.derrakuma.ui.components.BigStat -import com.derrakuma.ui.images.* -import com.derrakuma.ui.theme.* +import com.derakkuma.data.* +import com.derakkuma.data.CacheStore +import com.derakkuma.ui.components.BigStat +import com.derakkuma.ui.images.* +import com.derakkuma.ui.theme.* @Composable fun PlayerDataTab(client: MaimaiClient, cookie: String, profile: Profile? = null, maintenanceMode: Boolean = false, cacheStore: CacheStore = CacheStore(), playStore: PlayStore? = null) { diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/PlayerNameTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/PlayerNameTab.kt index a5b44eb..7f42ed2 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/PlayerNameTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/PlayerNameTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState @@ -13,10 +13,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.atproto.AtProtoClient -import com.derrakuma.atproto.syncCachedProfileToAtProto -import com.derrakuma.data.* -import com.derrakuma.ui.theme.* +import com.derakkuma.atproto.AtProtoClient +import com.derakkuma.atproto.syncCachedProfileToAtProto +import com.derakkuma.data.* +import com.derakkuma.ui.theme.* import kotlinx.coroutines.launch @Composable diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/RankingTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/RankingTab.kt index 2ceeca5..7b05c38 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/RankingTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/RankingTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -13,9 +13,9 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.data.* -import com.derrakuma.ui.theme.* -import com.derrakuma.util.formatScore +import com.derakkuma.data.* +import com.derakkuma.ui.theme.* +import com.derakkuma.util.formatScore import kotlinx.coroutines.launch enum class RankingSection(val label: String) { diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/RatingTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/RatingTab.kt index b4ebeb4..2a2d8be 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/RatingTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/RatingTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -12,16 +12,16 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.data.CacheStore -import com.derrakuma.data.DashboardData -import com.derrakuma.data.MaimaiClient -import com.derrakuma.data.MaintenanceException -import com.derrakuma.data.PlayCount -import com.derrakuma.data.RatingData -import com.derrakuma.data.RatingSong -import com.derrakuma.data.SessionExpiredException -import com.derrakuma.ui.components.SongRow -import com.derrakuma.ui.theme.* +import com.derakkuma.data.CacheStore +import com.derakkuma.data.DashboardData +import com.derakkuma.data.MaimaiClient +import com.derakkuma.data.MaintenanceException +import com.derakkuma.data.PlayCount +import com.derakkuma.data.RatingData +import com.derakkuma.data.RatingSong +import com.derakkuma.data.SessionExpiredException +import com.derakkuma.ui.components.SongRow +import com.derakkuma.ui.theme.* @Composable fun RatingTab(client: MaimaiClient, cookie: String, maintenanceMode: Boolean = false, cacheStore: CacheStore = CacheStore()) { diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/ScoresTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/ScoresTab.kt index ad50ee6..4d76901 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/ScoresTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/ScoresTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -17,22 +17,22 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.atproto.AtProtoClient -import com.derrakuma.atproto.syncDashboardToAtProto -import com.derrakuma.data.CacheStore -import com.derrakuma.data.DashboardData -import com.derrakuma.data.HistoricalCrawler -import com.derrakuma.data.MaimaiClient -import com.derrakuma.data.MaintenanceException -import com.derrakuma.data.PlayCount -import com.derrakuma.data.PlayStore -import com.derrakuma.data.RatingData -import com.derrakuma.data.RecentPlay -import com.derrakuma.data.SessionExpiredException -import com.derrakuma.ui.components.SongRow -import com.derrakuma.ui.images.BundledIcon -import com.derrakuma.ui.images.BundledImage -import com.derrakuma.ui.theme.* +import com.derakkuma.atproto.AtProtoClient +import com.derakkuma.atproto.syncDashboardToAtProto +import com.derakkuma.data.CacheStore +import com.derakkuma.data.DashboardData +import com.derakkuma.data.HistoricalCrawler +import com.derakkuma.data.MaimaiClient +import com.derakkuma.data.MaintenanceException +import com.derakkuma.data.PlayCount +import com.derakkuma.data.PlayStore +import com.derakkuma.data.RatingData +import com.derakkuma.data.RecentPlay +import com.derakkuma.data.SessionExpiredException +import com.derakkuma.ui.components.SongRow +import com.derakkuma.ui.images.BundledIcon +import com.derakkuma.ui.images.BundledImage +import com.derakkuma.ui.theme.* import kotlinx.coroutines.launch import kotlin.time.Clock diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/SettingsTab.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/SettingsTab.kt index 3bc4d30..c07d7a3 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/SettingsTab.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/tabs/SettingsTab.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.tabs +package com.derakkuma.ui.tabs import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -21,13 +21,13 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.derrakuma.data.* -import com.derrakuma.ui.icons.DoubleArrow -import com.derrakuma.ui.icons.MobiledataArrows -import com.derrakuma.ui.icons.ReplayFlipped -import com.derrakuma.ui.images.MaimaiAssets -import com.derrakuma.ui.images.rememberBundledImage -import com.derrakuma.ui.theme.* +import com.derakkuma.data.* +import com.derakkuma.ui.icons.DoubleArrow +import com.derakkuma.ui.icons.MobiledataArrows +import com.derakkuma.ui.icons.ReplayFlipped +import com.derakkuma.ui.images.MaimaiAssets +import com.derakkuma.ui.images.rememberBundledImage +import com.derakkuma.ui.theme.* import kotlinx.coroutines.launch @Composable diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/theme/Theme.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/theme/Theme.kt index 403a69c..7e8d421 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/theme/Theme.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/theme/Theme.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.theme +package com.derakkuma.ui.theme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Typography @@ -7,8 +7,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight -import derrakuma.composeapp.generated.resources.Res -import derrakuma.composeapp.generated.resources.segamaru_db +import derakkuma.composeapp.generated.resources.Res +import derakkuma.composeapp.generated.resources.segamaru_db import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.Font @@ -76,7 +76,7 @@ fun SegaMaruTypography() = Typography().run { } @Composable -fun DerrakumaTheme(content: @Composable () -> Unit) { +fun DerakkumaTheme(content: @Composable () -> Unit) { MaterialTheme( colorScheme = colorScheme, typography = SegaMaruTypography(), diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/util/Clipboard.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/util/Clipboard.kt index 6647616..882dbac 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/util/Clipboard.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/util/Clipboard.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.util +package com.derakkuma.ui.util import androidx.compose.ui.platform.Clipboard diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/util/CopyButton.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/util/CopyButton.kt index 95beb8c..68291c0 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/ui/util/CopyButton.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/ui/util/CopyButton.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.util +package com.derakkuma.ui.util import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ContentCopy @@ -8,7 +8,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalClipboard -import com.derrakuma.ui.theme.PrimaryBlue +import com.derakkuma.ui.theme.PrimaryBlue import kotlinx.coroutines.launch @Composable diff --git a/composeApp/src/commonMain/kotlin/com/derrakuma/util/Utils.kt b/composeApp/src/commonMain/kotlin/com/derrakuma/util/Utils.kt index dbc196a..fbaeff6 100644 --- a/composeApp/src/commonMain/kotlin/com/derrakuma/util/Utils.kt +++ b/composeApp/src/commonMain/kotlin/com/derrakuma/util/Utils.kt @@ -1,4 +1,4 @@ -package com.derrakuma.util +package com.derakkuma.util /** KMP-compatible score formatting (replaces String.format("%.Nf", x)). */ fun formatScore(value: Double, decimals: Int = 4): String { diff --git a/composeApp/src/iosMain/kotlin/com/derrakuma/MainViewController.kt b/composeApp/src/iosMain/kotlin/com/derrakuma/MainViewController.kt index a05c998..97a2bd2 100644 --- a/composeApp/src/iosMain/kotlin/com/derrakuma/MainViewController.kt +++ b/composeApp/src/iosMain/kotlin/com/derrakuma/MainViewController.kt @@ -1,12 +1,12 @@ -package com.derrakuma +package com.derakkuma import androidx.compose.ui.window.ComposeUIViewController import coil3.compose.LocalPlatformContext -import com.derrakuma.data.AuthStore -import com.derrakuma.data.CacheStore -import com.derrakuma.data.DriverFactory -import com.derrakuma.data.PlayStore -import com.derrakuma.ui.images.initCoil +import com.derakkuma.data.AuthStore +import com.derakkuma.data.CacheStore +import com.derakkuma.data.DriverFactory +import com.derakkuma.data.PlayStore +import com.derakkuma.ui.images.initCoil import io.github.samuolis.posthog.PostHog import io.github.samuolis.posthog.PostHogConfig import io.github.samuolis.posthog.PostHogContext @@ -20,7 +20,7 @@ fun MainViewController() = ComposeUIViewController { PostHog.setup( config = PostHogConfig( apiKey = Secrets.POSTHOG_API_KEY, - host = "https://salt.derrakuma.com", + host = "https://salt.derakkuma.com", captureApplicationLifecycleEvents = true, captureScreenViews = false, ), diff --git a/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSync.ios.kt b/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSync.ios.kt index 3162ebd..7ba6d9a 100644 --- a/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSync.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/AtProtoBackgroundSync.ios.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto actual fun configureAtProtoBackgroundSync(enabled: Boolean) = Unit diff --git a/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/AtProtoFactory.ios.kt b/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/AtProtoFactory.ios.kt index 361d0b2..05d5577 100644 --- a/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/AtProtoFactory.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/AtProtoFactory.ios.kt @@ -1,6 +1,6 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto actual fun createAtProtoClient(): AtProtoClient = IosAtProtoClient( - clientMetadataUrl = "https://derrakuma.com/oauth/client-metadata.json", - redirectScheme = "com.derrakuma", + clientMetadataUrl = "https://derakkuma.com/oauth/client-metadata.json", + redirectScheme = "com.derakkuma", ) diff --git a/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/AtProtoRegistry.ios.kt b/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/AtProtoRegistry.ios.kt index 8f5a495..34a2e99 100644 --- a/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/AtProtoRegistry.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/AtProtoRegistry.ios.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto internal object IosAtProtoRegistry { var client: AtProtoClient? = null diff --git a/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/IosAtProtoClient.kt b/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/IosAtProtoClient.kt index cdbab0f..4401059 100644 --- a/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/IosAtProtoClient.kt +++ b/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/IosAtProtoClient.kt @@ -1,10 +1,10 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto -import com.derrakuma.atproto.vendor.oauth.AtOAuth -import com.derrakuma.atproto.vendor.oauth.OAuthSession -import com.derrakuma.atproto.vendor.oauth.OAuthSessionStore -import com.derrakuma.atproto.vendor.runtime.NoXrpcParams -import com.derrakuma.atproto.vendor.runtime.XrpcClient +import com.derakkuma.atproto.vendor.oauth.AtOAuth +import com.derakkuma.atproto.vendor.oauth.OAuthSession +import com.derakkuma.atproto.vendor.oauth.OAuthSessionStore +import com.derakkuma.atproto.vendor.runtime.NoXrpcParams +import com.derakkuma.atproto.vendor.runtime.XrpcClient import io.ktor.client.HttpClient import io.ktor.client.engine.darwin.Darwin import io.ktor.http.ContentType @@ -37,7 +37,7 @@ class IosAtProtoClient( redirectUri = "$redirectScheme:/oauth-redirect", sessionStore = sessionStore, httpClient = httpClient, - scope = DERRAKUMA_ATPROTO_SCOPE, + scope = DERAKKUMA_ATPROTO_SCOPE, ) private var xrpcClient: XrpcClient? = null diff --git a/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/OpenOAuthUrl.ios.kt b/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/OpenOAuthUrl.ios.kt index 5f28b79..81fcd31 100644 --- a/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/OpenOAuthUrl.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/OpenOAuthUrl.ios.kt @@ -1,6 +1,6 @@ -package com.derrakuma.atproto +package com.derakkuma.atproto -import com.derrakuma.kamaitachi.IosKamaitachiRegistry +import com.derakkuma.kamaitachi.IosKamaitachiRegistry import kotlinx.cinterop.BetaInteropApi import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.coroutines.MainScope @@ -17,7 +17,7 @@ import platform.darwin.NSObject import platform.darwin.dispatch_async import platform.darwin.dispatch_get_main_queue -private const val OAUTH_CALLBACK_SCHEME = "com.derrakuma" +private const val OAUTH_CALLBACK_SCHEME = "com.derakkuma" /** * Fallback entry point for native iOS URL callbacks. @@ -33,7 +33,7 @@ fun handleAtProtoOAuthRedirect(url: String) { /** Stable Swift-callable object for native iOS URL callback glue. */ object AtProtoRedirect { fun handleOAuthRedirect(url: String) { - if (url.startsWith("com.derrakuma:/kamai")) { + if (url.startsWith("com.derakkuma:/kamai")) { val client = IosKamaitachiRegistry.client ?: return MainScope().launch { client.completeLogin(url) } return diff --git a/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/vendor/oauth/CryptoProvider.ios.kt b/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/vendor/oauth/CryptoProvider.ios.kt index f759f3f..0bdb080 100644 --- a/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/vendor/oauth/CryptoProvider.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/derrakuma/atproto/vendor/oauth/CryptoProvider.ios.kt @@ -1,4 +1,4 @@ -package com.derrakuma.atproto.vendor.oauth +package com.derakkuma.atproto.vendor.oauth import dev.whyoleg.cryptography.CryptographyProvider import dev.whyoleg.cryptography.providers.apple.Apple diff --git a/composeApp/src/iosMain/kotlin/com/derrakuma/data/AuthStore.ios.kt b/composeApp/src/iosMain/kotlin/com/derrakuma/data/AuthStore.ios.kt index c4ec90f..c4a85b7 100644 --- a/composeApp/src/iosMain/kotlin/com/derrakuma/data/AuthStore.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/derrakuma/data/AuthStore.ios.kt @@ -1,4 +1,4 @@ -package com.derrakuma.data +package com.derakkuma.data import platform.Foundation.NSUserDefaults diff --git a/composeApp/src/iosMain/kotlin/com/derrakuma/data/DriverFactory.ios.kt b/composeApp/src/iosMain/kotlin/com/derrakuma/data/DriverFactory.ios.kt index ed1000e..fb06290 100644 --- a/composeApp/src/iosMain/kotlin/com/derrakuma/data/DriverFactory.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/derrakuma/data/DriverFactory.ios.kt @@ -1,9 +1,9 @@ -package com.derrakuma.data +package com.derakkuma.data import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.native.NativeSqliteDriver -import com.derrakuma.db.DerrakumaDb +import com.derakkuma.db.DerakkumaDb actual class DriverFactory { - actual fun createDriver(): SqlDriver = NativeSqliteDriver(DerrakumaDb.Schema, "derrakuma.db") + actual fun createDriver(): SqlDriver = NativeSqliteDriver(DerakkumaDb.Schema, "derakkuma.db") } diff --git a/composeApp/src/iosMain/kotlin/com/derrakuma/kamaitachi/KamaitachiRegistry.ios.kt b/composeApp/src/iosMain/kotlin/com/derrakuma/kamaitachi/KamaitachiRegistry.ios.kt index 3de7607..b3069d3 100644 --- a/composeApp/src/iosMain/kotlin/com/derrakuma/kamaitachi/KamaitachiRegistry.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/derrakuma/kamaitachi/KamaitachiRegistry.ios.kt @@ -1,4 +1,4 @@ -package com.derrakuma.kamaitachi +package com.derakkuma.kamaitachi object IosKamaitachiRegistry { var client: KamaitachiClient? = null diff --git a/composeApp/src/iosMain/kotlin/com/derrakuma/ui/auth/AuthScreen.ios.kt b/composeApp/src/iosMain/kotlin/com/derrakuma/ui/auth/AuthScreen.ios.kt index 73439b4..0553cfe 100644 --- a/composeApp/src/iosMain/kotlin/com/derrakuma/ui/auth/AuthScreen.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/derrakuma/ui/auth/AuthScreen.ios.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.auth +package com.derakkuma.ui.auth import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize @@ -9,7 +9,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.viewinterop.UIKitView -import com.derrakuma.data.AuthState +import com.derakkuma.data.AuthState import io.ktor.client.HttpClient import io.ktor.client.engine.darwin.Darwin import io.ktor.client.request.get diff --git a/composeApp/src/iosMain/kotlin/com/derrakuma/ui/util/Clipboard.ios.kt b/composeApp/src/iosMain/kotlin/com/derrakuma/ui/util/Clipboard.ios.kt index cfd504b..0c4e1f5 100644 --- a/composeApp/src/iosMain/kotlin/com/derrakuma/ui/util/Clipboard.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/derrakuma/ui/util/Clipboard.ios.kt @@ -1,4 +1,4 @@ -package com.derrakuma.ui.util +package com.derakkuma.ui.util import androidx.compose.ui.platform.Clipboard import platform.UIKit.UIPasteboard diff --git a/deploy/happyview/README.md b/deploy/happyview/README.md index fef8672..54cb894 100644 --- a/deploy/happyview/README.md +++ b/deploy/happyview/README.md @@ -1,8 +1,8 @@ -# Derrakuma HappyView deploy +# Derakkuma HappyView deploy -Privileged deploy assets for the Derrakuma HappyView AppView. +Privileged deploy assets for the Derakkuma HappyView AppView. -The public AppView domain is `milk.derrakuma.com`. Keep hostnames, server IPs, +The public AppView domain is `milk.derakkuma.com`. Keep hostnames, server IPs, SSH users, and machine-local paths out of this repo. ## DNS required @@ -10,12 +10,12 @@ SSH users, and machine-local paths out of this repo. Configure DNS outside of git: ```dns -milk.derrakuma.com. A/AAAA -www.milk.derrakuma.com. CNAME milk.derrakuma.com. -_lexicon.derrakuma.com. TXT "did=" +milk.derakkuma.com. A/AAAA +www.milk.derakkuma.com. CNAME milk.derakkuma.com. +_lexicon.derakkuma.com. TXT "did=" ``` -`_lexicon.derrakuma.com` is required for lexicon authority resolution. +`_lexicon.derakkuma.com` is required for lexicon authority resolution. ## Install on server @@ -23,7 +23,7 @@ Copy `deploy/happyview/` to the server by whatever deploy path you use, then run the root install script from the deployed app directory: ```bash -sudo DOMAIN=milk.derrakuma.com \ +sudo DOMAIN=milk.derakkuma.com \ EMAIL= \ REPO_DIR= \ APP_USER= \ @@ -34,21 +34,21 @@ sudo DOMAIN=milk.derrakuma.com \ The app `.env` should advertise the public domain, not any private host/IP: ```dotenv -PUBLIC_URL=https://milk.derrakuma.com -CLIENT_URI=https://milk.derrakuma.com +PUBLIC_URL=https://milk.derakkuma.com +CLIENT_URI=https://milk.derakkuma.com ``` The script installs: - `/etc/systemd/system/happyview.service` -- `/etc/nginx/sites-available/milk.derrakuma.com` -- certbot TLS cert for `milk.derrakuma.com` + `www.milk.derrakuma.com` +- `/etc/nginx/sites-available/milk.derakkuma.com` +- certbot TLS cert for `milk.derakkuma.com` + `www.milk.derakkuma.com` ## Post-install verification ```bash -curl https://milk.derrakuma.com/oauth/client-metadata.json -curl 'https://milk.derrakuma.com/xrpc/com.derrakuma.searchProfiles?query=test&client_key=dev' +curl https://milk.derakkuma.com/oauth/client-metadata.json +curl 'https://milk.derakkuma.com/xrpc/com.derakkuma.searchProfiles?query=test&client_key=dev' systemctl status happyview journalctl -u happyview -f ``` @@ -56,11 +56,11 @@ journalctl -u happyview -f ## Publish lexicon schema records After obtaining an access token for the lexicon authority account, run from the -Derrakuma repo root: +Derakkuma repo root: ```bash ATP_AUTH_TOKEN=... REPO_DID= ./scripts/publish_lexicon_records.sh ``` -This publishes `lexicons/schema-records/com.derrakuma.*.json` as +This publishes `lexicons/schema-records/com.derakkuma.*.json` as `com.atproto.lexicon.schema` records. diff --git a/deploy/happyview/happyview.service b/deploy/happyview/happyview.service index 90b024c..bbf6770 100644 --- a/deploy/happyview/happyview.service +++ b/deploy/happyview/happyview.service @@ -1,5 +1,5 @@ [Unit] -Description=HappyView AppView for Derrakuma +Description=HappyView AppView for Derakkuma After=network-online.target Wants=network-online.target diff --git a/deploy/happyview/lexicons/com.derrakuma.getFriendsPlays.lua b/deploy/happyview/lexicons/com.derrakuma.getFriendsPlays.lua index 4fb0fc6..c96d1eb 100644 --- a/deploy/happyview/lexicons/com.derrakuma.getFriendsPlays.lua +++ b/deploy/happyview/lexicons/com.derrakuma.getFriendsPlays.lua @@ -9,16 +9,16 @@ function handle() return { records = toarray({}) } end - local friends = db.query({ collection = 'com.derrakuma.friend', did = actor, limit = 100 }).records + local friends = db.query({ collection = 'com.derakkuma.friend', did = actor, limit = 100 }).records local out = {} for _, friend in ipairs(friends) do local friend_value = value_of(friend) local subject = friend_value.subject if subject ~= nil and subject ~= '' then - local profiles = db.query({ collection = 'com.derrakuma.profile', did = subject, limit = 1 }).records + local profiles = db.query({ collection = 'com.derakkuma.profile', did = subject, limit = 1 }).records local profile = nil if #profiles > 0 then profile = value_of(profiles[1]) end - local plays = db.query({ collection = 'com.derrakuma.play', did = subject, limit = limit }).records + local plays = db.query({ collection = 'com.derakkuma.play', did = subject, limit = limit }).records for _, play in ipairs(plays) do local play_value = value_of(play) play_value.did = play.did diff --git a/deploy/happyview/lexicons/com.derrakuma.getLeaderboard.lua b/deploy/happyview/lexicons/com.derrakuma.getLeaderboard.lua index 75774d3..a4c5e24 100644 --- a/deploy/happyview/lexicons/com.derrakuma.getLeaderboard.lua +++ b/deploy/happyview/lexicons/com.derrakuma.getLeaderboard.lua @@ -6,7 +6,7 @@ function handle() local song = params.songName or '' local diff = params.difficulty or '' local limit = params.limit or 50 - local rows = db.query({ collection = 'com.derrakuma.best', limit = 100 }).records + local rows = db.query({ collection = 'com.derakkuma.best', limit = 100 }).records local out = {} for _, row in ipairs(rows) do @@ -14,7 +14,7 @@ function handle() if value.songName == song and value.difficulty == diff then value.did = row.did value.uri = row.uri - local profiles = db.query({ collection = 'com.derrakuma.profile', did = row.did, limit = 1 }).records + local profiles = db.query({ collection = 'com.derakkuma.profile', did = row.did, limit = 1 }).records if #profiles > 0 then local profile = value_of(profiles[1]) value.playerName = profile.playerName diff --git a/deploy/happyview/lexicons/com.derrakuma.getPlayerStats.lua b/deploy/happyview/lexicons/com.derrakuma.getPlayerStats.lua index 0f258b4..ab8f54c 100644 --- a/deploy/happyview/lexicons/com.derrakuma.getPlayerStats.lua +++ b/deploy/happyview/lexicons/com.derrakuma.getPlayerStats.lua @@ -8,7 +8,7 @@ function handle() return { profile = nil, playCount = 0, bestCount = 0, averageAchievement = 0 } end - local profiles = db.query({ collection = 'com.derrakuma.profile', did = actor, limit = 1 }).records + local profiles = db.query({ collection = 'com.derakkuma.profile', did = actor, limit = 1 }).records local profile = nil if #profiles > 0 then profile = value_of(profiles[1]) @@ -16,8 +16,8 @@ function handle() profile.did = profiles[1].did end - local play_count = db.count('com.derrakuma.play', actor) - local bests = db.query({ collection = 'com.derrakuma.best', did = actor, limit = 100 }).records + local play_count = db.count('com.derakkuma.play', actor) + local bests = db.query({ collection = 'com.derakkuma.best', did = actor, limit = 100 }).records local total = 0 for _, best in ipairs(bests) do local value = value_of(best) diff --git a/deploy/happyview/lexicons/com.derrakuma.searchProfiles.lua b/deploy/happyview/lexicons/com.derrakuma.searchProfiles.lua index 2f8670c..85639be 100644 --- a/deploy/happyview/lexicons/com.derrakuma.searchProfiles.lua +++ b/deploy/happyview/lexicons/com.derrakuma.searchProfiles.lua @@ -9,7 +9,7 @@ function handle() return { records = toarray({}) } end - local rows = db.search({ collection = 'com.derrakuma.profile', field = 'playerName', query = q, limit = limit }).records + local rows = db.search({ collection = 'com.derakkuma.profile', field = 'playerName', query = q, limit = limit }).records local out = {} for _, row in ipairs(rows) do local value = value_of(row) diff --git a/deploy/happyview/milk.derrakuma.com.nginx b/deploy/happyview/milk.derrakuma.com.nginx index 1d7b85b..2029a7e 100644 --- a/deploy/happyview/milk.derrakuma.com.nginx +++ b/deploy/happyview/milk.derrakuma.com.nginx @@ -1,35 +1,35 @@ server { listen 80; listen [::]:80; - server_name milk.derrakuma.com www.milk.derrakuma.com; + server_name milk.derakkuma.com www.milk.derakkuma.com; location ^~ /.well-known/acme-challenge/ { root /var/www/html; } location / { - return 301 https://milk.derrakuma.com$request_uri; + return 301 https://milk.derakkuma.com$request_uri; } } server { listen 443 ssl http2; listen [::]:443 ssl http2; - server_name www.milk.derrakuma.com; + server_name www.milk.derakkuma.com; - ssl_certificate /etc/letsencrypt/live/milk.derrakuma.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/milk.derrakuma.com/privkey.pem; + ssl_certificate /etc/letsencrypt/live/milk.derakkuma.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/milk.derakkuma.com/privkey.pem; - return 301 https://milk.derrakuma.com$request_uri; + return 301 https://milk.derakkuma.com$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; - server_name milk.derrakuma.com; + server_name milk.derakkuma.com; - ssl_certificate /etc/letsencrypt/live/milk.derrakuma.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/milk.derrakuma.com/privkey.pem; + ssl_certificate /etc/letsencrypt/live/milk.derakkuma.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/milk.derakkuma.com/privkey.pem; client_max_body_size 50m; diff --git a/deploy/happyview/scripts/install-root.sh b/deploy/happyview/scripts/install-root.sh index e05c7be..0a1d690 100755 --- a/deploy/happyview/scripts/install-root.sh +++ b/deploy/happyview/scripts/install-root.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -DOMAIN="${DOMAIN:-milk.derrakuma.com}" +DOMAIN="${DOMAIN:-milk.derakkuma.com}" WWW_DOMAIN="${WWW_DOMAIN:-www.$DOMAIN}" SERVICE_NAME="${SERVICE_NAME:-happyview}" APP_USER="${APP_USER:?set APP_USER to the systemd service user}" @@ -19,7 +19,7 @@ install -d -o "$APP_USER" -g "$APP_GROUP" "$REPO_DIR/logs" "$REPO_DIR/data" cat >/etc/systemd/system/$SERVICE_NAME.service </dev/null -curl -fsS "https://$DOMAIN/xrpc/com.derrakuma.searchProfiles?query=test&client_key=dev" >/dev/null +curl -fsS "https://$DOMAIN/xrpc/com.derakkuma.searchProfiles?query=test&client_key=dev" >/dev/null diff --git a/deploy/happyview/scripts/install_happyview_social_lexicons.py b/deploy/happyview/scripts/install_happyview_social_lexicons.py index c21f830..4d785c9 100644 --- a/deploy/happyview/scripts/install_happyview_social_lexicons.py +++ b/deploy/happyview/scripts/install_happyview_social_lexicons.py @@ -8,9 +8,9 @@ SCRIPT_DIR = Path(__file__).resolve().parent LUA_DIR = SCRIPT_DIR.parent / "lexicons" LEXICONS = { - "com.derrakuma.getFriendsPlays": { + "com.derakkuma.getFriendsPlays": { "lexicon": 1, - "id": "com.derrakuma.getFriendsPlays", + "id": "com.derakkuma.getFriendsPlays", "defs": { "main": { "type": "query", @@ -26,9 +26,9 @@ LEXICONS = { } }, }, - "com.derrakuma.getLeaderboard": { + "com.derakkuma.getLeaderboard": { "lexicon": 1, - "id": "com.derrakuma.getLeaderboard", + "id": "com.derakkuma.getLeaderboard", "defs": { "main": { "type": "query", @@ -46,9 +46,9 @@ LEXICONS = { } }, }, - "com.derrakuma.getPlayerStats": { + "com.derakkuma.getPlayerStats": { "lexicon": 1, - "id": "com.derrakuma.getPlayerStats", + "id": "com.derakkuma.getPlayerStats", "defs": { "main": { "type": "query", @@ -62,9 +62,9 @@ LEXICONS = { } }, }, - "com.derrakuma.searchProfiles": { + "com.derakkuma.searchProfiles": { "lexicon": 1, - "id": "com.derrakuma.searchProfiles", + "id": "com.derakkuma.searchProfiles", "defs": { "main": { "type": "query", diff --git a/deploy/happyview/scripts/publish_lexicon_records.sh b/deploy/happyview/scripts/publish_lexicon_records.sh index 7b820ed..0626236 100755 --- a/deploy/happyview/scripts/publish_lexicon_records.sh +++ b/deploy/happyview/scripts/publish_lexicon_records.sh @@ -5,7 +5,7 @@ set -euo pipefail : "${REPO_DID:?set REPO_DID to the lexicon authority DID}" PDS_URL="${PDS_URL:-https://npmx.social}" -for file in lexicons/schema-records/com.derrakuma.*.json; do +for file in lexicons/schema-records/com.derakkuma.*.json; do nsid="$(basename "$file" .json)" body="$(jq -n \ --arg repo "$REPO_DID" \ diff --git a/iosApp/Derrakuma.xcodeproj/project.pbxproj b/iosApp/Derrakuma.xcodeproj/project.pbxproj index 3f37ce5..892a7b9 100644 --- a/iosApp/Derrakuma.xcodeproj/project.pbxproj +++ b/iosApp/Derrakuma.xcodeproj/project.pbxproj @@ -7,14 +7,14 @@ objects = { /* Begin PBXBuildFile section */ - 0B04339D3CF760A278AAAF8B /* DerrakumaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2BB1FDDB9BF3A21FF8B5E4 /* DerrakumaApp.swift */; }; + 0B04339D3CF760A278AAAF8B /* DerakkumaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2BB1FDDB9BF3A21FF8B5E4 /* DerakkumaApp.swift */; }; 362AED2AD979BD6FB0779718 /* ComposeAppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74BE81D47A274F8FB70B79E8 /* ComposeAppView.swift */; }; D6C363593CE547CD20E23778 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7CC0A8C5F565BE5CBED9AE64 /* Media.xcassets */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 5A2BB1FDDB9BF3A21FF8B5E4 /* DerrakumaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DerrakumaApp.swift; sourceTree = ""; }; - 634181FCA63A8FD58AE737B5 /* Derrakuma.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Derrakuma.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 5A2BB1FDDB9BF3A21FF8B5E4 /* DerakkumaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DerakkumaApp.swift; sourceTree = ""; }; + 634181FCA63A8FD58AE737B5 /* Derakkuma.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Derakkuma.app; sourceTree = BUILT_PRODUCTS_DIR; }; 74BE81D47A274F8FB70B79E8 /* ComposeAppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeAppView.swift; sourceTree = ""; }; 7CC0A8C5F565BE5CBED9AE64 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; C7281C9D619BDC754E80B9A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; @@ -24,7 +24,7 @@ 52613C8DA7D49973F35652DF /* Products */ = { isa = PBXGroup; children = ( - 634181FCA63A8FD58AE737B5 /* Derrakuma.app */, + 634181FCA63A8FD58AE737B5 /* Derakkuma.app */, ); name = Products; sourceTree = ""; @@ -42,7 +42,7 @@ isa = PBXGroup; children = ( 74BE81D47A274F8FB70B79E8 /* ComposeAppView.swift */, - 5A2BB1FDDB9BF3A21FF8B5E4 /* DerrakumaApp.swift */, + 5A2BB1FDDB9BF3A21FF8B5E4 /* DerakkumaApp.swift */, C7281C9D619BDC754E80B9A0 /* Info.plist */, ); path = iosApp; @@ -51,9 +51,9 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 0F60706EDF8254269A207360 /* Derrakuma */ = { + 0F60706EDF8254269A207360 /* Derakkuma */ = { isa = PBXNativeTarget; - buildConfigurationList = B1F1593A65D06BC2D3886E39 /* Build configuration list for PBXNativeTarget "Derrakuma" */; + buildConfigurationList = B1F1593A65D06BC2D3886E39 /* Build configuration list for PBXNativeTarget "Derakkuma" */; buildPhases = ( 083575C459DD3F5DF8220DD9 /* Embed ComposeApp framework */, 2E4C4AF2481567118335F0D3 /* Sources */, @@ -63,11 +63,11 @@ ); dependencies = ( ); - name = Derrakuma; + name = Derakkuma; packageProductDependencies = ( ); - productName = Derrakuma; - productReference = 634181FCA63A8FD58AE737B5 /* Derrakuma.app */; + productName = Derakkuma; + productReference = 634181FCA63A8FD58AE737B5 /* Derakkuma.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -79,7 +79,7 @@ BuildIndependentTargetsInParallel = YES; LastUpgradeCheck = 1430; }; - buildConfigurationList = 504AE667925EAE0DE14C223B /* Build configuration list for PBXProject "Derrakuma" */; + buildConfigurationList = 504AE667925EAE0DE14C223B /* Build configuration list for PBXProject "Derakkuma" */; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -93,7 +93,7 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 0F60706EDF8254269A207360 /* Derrakuma */, + 0F60706EDF8254269A207360 /* Derakkuma */, ); }; /* End PBXProject section */ @@ -137,7 +137,7 @@ buildActionMask = 2147483647; files = ( 362AED2AD979BD6FB0779718 /* ComposeAppView.swift in Sources */, - 0B04339D3CF760A278AAAF8B /* DerrakumaApp.swift in Sources */, + 0B04339D3CF760A278AAAF8B /* DerakkumaApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -231,7 +231,7 @@ ComposeApp, "-lsqlite3", ); - PRODUCT_BUNDLE_IDENTIFIER = com.derrakuma; + PRODUCT_BUNDLE_IDENTIFIER = com.derakkuma; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -317,7 +317,7 @@ ComposeApp, "-lsqlite3", ); - PRODUCT_BUNDLE_IDENTIFIER = com.derrakuma; + PRODUCT_BUNDLE_IDENTIFIER = com.derakkuma; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -326,7 +326,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 504AE667925EAE0DE14C223B /* Build configuration list for PBXProject "Derrakuma" */ = { + 504AE667925EAE0DE14C223B /* Build configuration list for PBXProject "Derakkuma" */ = { isa = XCConfigurationList; buildConfigurations = ( 3AD887D897CE91469D0AA23B /* Debug */, @@ -335,7 +335,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; }; - B1F1593A65D06BC2D3886E39 /* Build configuration list for PBXNativeTarget "Derrakuma" */ = { + B1F1593A65D06BC2D3886E39 /* Build configuration list for PBXNativeTarget "Derakkuma" */ = { isa = XCConfigurationList; buildConfigurations = ( 61338C9FE0924CEAADD4E699 /* Debug */, diff --git a/iosApp/Derrakuma.xcodeproj/xcuserdata/kainoa.xcuserdatad/xcschemes/xcschememanagement.plist b/iosApp/Derrakuma.xcodeproj/xcuserdata/kainoa.xcuserdatad/xcschemes/xcschememanagement.plist index 5f88771..c709537 100644 --- a/iosApp/Derrakuma.xcodeproj/xcuserdata/kainoa.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/iosApp/Derrakuma.xcodeproj/xcuserdata/kainoa.xcuserdatad/xcschemes/xcschememanagement.plist @@ -4,7 +4,7 @@ SchemeUserState - Derrakuma.xcscheme_^#shared#^_ + Derakkuma.xcscheme_^#shared#^_ orderHint 0 diff --git a/iosApp/iosApp/DerrakumaApp.swift b/iosApp/iosApp/DerrakumaApp.swift index 5e8cbdd..b547d8b 100644 --- a/iosApp/iosApp/DerrakumaApp.swift +++ b/iosApp/iosApp/DerrakumaApp.swift @@ -1,21 +1,21 @@ import SwiftUI import ComposeApp -class DerrakumaAppDelegate: NSObject, UIApplicationDelegate { +class DerakkumaAppDelegate: NSObject, UIApplicationDelegate { func application( _ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:] ) -> Bool { - guard url.scheme == "com.derrakuma" else { return false } + guard url.scheme == "com.derakkuma" else { return false } AtProtoRedirect.shared.handleOAuthRedirect(url: url.absoluteString) return true } } @main -struct DerrakumaApp: App { - @UIApplicationDelegateAdaptor(DerrakumaAppDelegate.self) private var appDelegate +struct DerakkumaApp: App { + @UIApplicationDelegateAdaptor(DerakkumaAppDelegate.self) private var appDelegate var body: some Scene { WindowGroup { ComposeAppView() diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist index c863692..b3b714d 100644 --- a/iosApp/iosApp/Info.plist +++ b/iosApp/iosApp/Info.plist @@ -7,7 +7,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Derrakuma + Derakkuma CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -24,10 +24,10 @@ CFBundleURLName - Derrakuma OAuth + Derakkuma OAuth CFBundleURLSchemes - com.derrakuma + com.derakkuma diff --git a/iosApp/project.yml b/iosApp/project.yml index 55c48f7..a410005 100644 --- a/iosApp/project.yml +++ b/iosApp/project.yml @@ -1,4 +1,4 @@ -name: Derrakuma +name: Derakkuma options: bundleIdPrefix: com deploymentTarget: @@ -8,7 +8,7 @@ settings: SWIFT_VERSION: "5.0" ENABLE_USER_SCRIPT_SANDBOXING: NO targets: - Derrakuma: + Derakkuma: type: application platform: iOS deploymentTarget: "16.0" @@ -18,18 +18,18 @@ targets: info: path: iosApp/Info.plist properties: - CFBundleDisplayName: Derrakuma + CFBundleDisplayName: Derakkuma CFBundleShortVersionString: "0.1.0" CFBundleVersion: "1" UILaunchScreen: {} CADisableMinimumFrameDurationOnPhone: true CFBundleURLTypes: - - CFBundleURLName: Derrakuma OAuth + - CFBundleURLName: Derakkuma OAuth CFBundleURLSchemes: - - com.derrakuma + - com.derakkuma settings: base: - PRODUCT_BUNDLE_IDENTIFIER: com.derrakuma + PRODUCT_BUNDLE_IDENTIFIER: com.derakkuma DEVELOPMENT_TEAM: "" ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS: YES diff --git a/lexicons/com.derrakuma.best.json b/lexicons/com.derrakuma.best.json index 9f532d8..f5bd9bc 100644 --- a/lexicons/com.derrakuma.best.json +++ b/lexicons/com.derrakuma.best.json @@ -1,6 +1,6 @@ { "lexicon": 1, - "id": "com.derrakuma.best", + "id": "com.derakkuma.best", "defs": { "main": { "type": "record", diff --git a/lexicons/com.derrakuma.circle.json b/lexicons/com.derrakuma.circle.json index d598d24..bd37ba7 100644 --- a/lexicons/com.derrakuma.circle.json +++ b/lexicons/com.derrakuma.circle.json @@ -1,6 +1,6 @@ { "lexicon": 1, - "id": "com.derrakuma.circle", + "id": "com.derakkuma.circle", "defs": { "main": { "type": "record", diff --git a/lexicons/com.derrakuma.circleMember.json b/lexicons/com.derrakuma.circleMember.json index d28eb9e..9ead2b6 100644 --- a/lexicons/com.derrakuma.circleMember.json +++ b/lexicons/com.derrakuma.circleMember.json @@ -1,6 +1,6 @@ { "lexicon": 1, - "id": "com.derrakuma.circleMember", + "id": "com.derakkuma.circleMember", "defs": { "main": { "type": "record", @@ -29,7 +29,7 @@ "profile": { "type": "string", "format": "at-uri", - "description": "AT URI of the member profile record when this member is resolved to a Derrakuma ATProto profile." + "description": "AT URI of the member profile record when this member is resolved to a Derakkuma ATProto profile." }, "displayName": { "type": "string", diff --git a/lexicons/com.derrakuma.favoriteSong.json b/lexicons/com.derrakuma.favoriteSong.json index e71dba4..1c99820 100644 --- a/lexicons/com.derrakuma.favoriteSong.json +++ b/lexicons/com.derrakuma.favoriteSong.json @@ -1,6 +1,6 @@ { "lexicon": 1, - "id": "com.derrakuma.favoriteSong", + "id": "com.derakkuma.favoriteSong", "defs": { "main": { "type": "record", diff --git a/lexicons/com.derrakuma.friend.json b/lexicons/com.derrakuma.friend.json index 1977a89..ae07403 100644 --- a/lexicons/com.derrakuma.friend.json +++ b/lexicons/com.derrakuma.friend.json @@ -1,6 +1,6 @@ { "lexicon": 1, - "id": "com.derrakuma.friend", + "id": "com.derakkuma.friend", "defs": { "main": { "type": "record", diff --git a/lexicons/com.derrakuma.play.json b/lexicons/com.derrakuma.play.json index d423462..55a425b 100644 --- a/lexicons/com.derrakuma.play.json +++ b/lexicons/com.derrakuma.play.json @@ -1,6 +1,6 @@ { "lexicon": 1, - "id": "com.derrakuma.play", + "id": "com.derakkuma.play", "defs": { "main": { "type": "record", diff --git a/lexicons/com.derrakuma.profile.json b/lexicons/com.derrakuma.profile.json index 151348a..bee1d1c 100644 --- a/lexicons/com.derrakuma.profile.json +++ b/lexicons/com.derrakuma.profile.json @@ -1,6 +1,6 @@ { "lexicon": 1, - "id": "com.derrakuma.profile", + "id": "com.derakkuma.profile", "defs": { "main": { "type": "record", diff --git a/lexicons/schema-records/com.derrakuma.best.json b/lexicons/schema-records/com.derrakuma.best.json index 7a3b3f3..87827f9 100644 --- a/lexicons/schema-records/com.derrakuma.best.json +++ b/lexicons/schema-records/com.derrakuma.best.json @@ -1,7 +1,7 @@ { "$type": "com.atproto.lexicon.schema", "lexicon": 1, - "id": "com.derrakuma.best", + "id": "com.derakkuma.best", "defs": { "main": { "type": "record", diff --git a/lexicons/schema-records/com.derrakuma.circle.json b/lexicons/schema-records/com.derrakuma.circle.json index 91352ce..18ad79c 100644 --- a/lexicons/schema-records/com.derrakuma.circle.json +++ b/lexicons/schema-records/com.derrakuma.circle.json @@ -1,7 +1,7 @@ { "": "com.atproto.lexicon.schema", "lexicon": 1, - "id": "com.derrakuma.circle", + "id": "com.derakkuma.circle", "defs": { "main": { "type": "record", diff --git a/lexicons/schema-records/com.derrakuma.circleMember.json b/lexicons/schema-records/com.derrakuma.circleMember.json index 17783af..4effc23 100644 --- a/lexicons/schema-records/com.derrakuma.circleMember.json +++ b/lexicons/schema-records/com.derrakuma.circleMember.json @@ -1,7 +1,7 @@ { "": "com.atproto.lexicon.schema", "lexicon": 1, - "id": "com.derrakuma.circleMember", + "id": "com.derakkuma.circleMember", "defs": { "main": { "type": "record", @@ -30,7 +30,7 @@ "profile": { "type": "string", "format": "at-uri", - "description": "AT URI of the member profile record when this member is resolved to a Derrakuma ATProto profile." + "description": "AT URI of the member profile record when this member is resolved to a Derakkuma ATProto profile." }, "displayName": { "type": "string", diff --git a/lexicons/schema-records/com.derrakuma.favoriteSong.json b/lexicons/schema-records/com.derrakuma.favoriteSong.json index 3de4fec..524833a 100644 --- a/lexicons/schema-records/com.derrakuma.favoriteSong.json +++ b/lexicons/schema-records/com.derrakuma.favoriteSong.json @@ -1,7 +1,7 @@ { "": "com.atproto.lexicon.schema", "lexicon": 1, - "id": "com.derrakuma.favoriteSong", + "id": "com.derakkuma.favoriteSong", "defs": { "main": { "type": "record", diff --git a/lexicons/schema-records/com.derrakuma.friend.json b/lexicons/schema-records/com.derrakuma.friend.json index adc0602..4ac3ee8 100644 --- a/lexicons/schema-records/com.derrakuma.friend.json +++ b/lexicons/schema-records/com.derrakuma.friend.json @@ -1,7 +1,7 @@ { "$type": "com.atproto.lexicon.schema", "lexicon": 1, - "id": "com.derrakuma.friend", + "id": "com.derakkuma.friend", "defs": { "main": { "type": "record", diff --git a/lexicons/schema-records/com.derrakuma.play.json b/lexicons/schema-records/com.derrakuma.play.json index aff61c4..04b9d22 100644 --- a/lexicons/schema-records/com.derrakuma.play.json +++ b/lexicons/schema-records/com.derrakuma.play.json @@ -1,7 +1,7 @@ { "$type": "com.atproto.lexicon.schema", "lexicon": 1, - "id": "com.derrakuma.play", + "id": "com.derakkuma.play", "defs": { "main": { "type": "record", diff --git a/lexicons/schema-records/com.derrakuma.profile.json b/lexicons/schema-records/com.derrakuma.profile.json index 4c1eabd..22c5cd7 100644 --- a/lexicons/schema-records/com.derrakuma.profile.json +++ b/lexicons/schema-records/com.derrakuma.profile.json @@ -1,7 +1,7 @@ { "$type": "com.atproto.lexicon.schema", "lexicon": 1, - "id": "com.derrakuma.profile", + "id": "com.derakkuma.profile", "defs": { "main": { "type": "record", diff --git a/settings.gradle.kts b/settings.gradle.kts index d381675..f28b8d0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,4 @@ -rootProject.name = "Derrakuma" +rootProject.name = "Derakkuma" pluginManagement { repositories { diff --git a/site/README.md b/site/README.md index 2f7c744..6211ebb 100644 --- a/site/README.md +++ b/site/README.md @@ -1,12 +1,12 @@ -# derrakuma.com static site +# derakkuma.com static site -Static files for the root `derrakuma.com` host. +Static files for the root `derakkuma.com` host. -Upload the contents of this directory to the static host serving `https://derrakuma.com`. +Upload the contents of this directory to the static host serving `https://derakkuma.com`. Required for ATProto mobile OAuth and authority discovery: -- `https://derrakuma.com/oauth/client-metadata.json` -- `https://derrakuma.com/.well-known/atproto-did` +- `https://derakkuma.com/oauth/client-metadata.json` +- `https://derakkuma.com/.well-known/atproto-did` -The HappyView AppView/dashboard remains hosted at `https://milk.derrakuma.com`. +The HappyView AppView/dashboard remains hosted at `https://milk.derakkuma.com`. diff --git a/site/index.html b/site/index.html index 39b4107..cee45ae 100644 --- a/site/index.html +++ b/site/index.html @@ -3,16 +3,16 @@ - Derrakuma - + Derakkuma + -

Derrakuma

-

AT Protocol metadata for Derrakuma.

+

Derakkuma

+

AT Protocol metadata for Derakkuma.

diff --git a/site/oauth/client-metadata.json b/site/oauth/client-metadata.json index 473ea54..cee277e 100644 --- a/site/oauth/client-metadata.json +++ b/site/oauth/client-metadata.json @@ -1,10 +1,10 @@ { - "client_id": "https://derrakuma.com/oauth/client-metadata.json", - "client_name": "Derrakuma", - "client_uri": "https://derrakuma.com", + "client_id": "https://derakkuma.com/oauth/client-metadata.json", + "client_name": "Derakkuma", + "client_uri": "https://derakkuma.com", "application_type": "native", "redirect_uris": [ - "com.derrakuma:/oauth-redirect" + "com.derakkuma:/oauth-redirect" ], "grant_types": [ "authorization_code", @@ -13,7 +13,7 @@ "response_types": [ "code" ], - "scope": "atproto blob:*/* repo:com.derrakuma.profile?action=create repo:com.derrakuma.profile?action=update repo:com.derrakuma.play?action=create repo:com.derrakuma.best?action=create repo:com.derrakuma.best?action=update repo:com.derrakuma.friend?action=create repo:com.derrakuma.friend?action=update repo:com.derrakuma.favoriteSong?action=create repo:com.derrakuma.favoriteSong?action=update repo:com.derrakuma.circle?action=create repo:com.derrakuma.circle?action=update repo:com.derrakuma.circleMember?action=create repo:com.derrakuma.circleMember?action=update repo:com.derrakuma.friend?action=delete repo:com.derrakuma.favoriteSong?action=delete repo:app.bsky.graph.follow?action=create", + "scope": "atproto blob:*/* repo:com.derakkuma.profile?action=create repo:com.derakkuma.profile?action=update repo:com.derakkuma.play?action=create repo:com.derakkuma.best?action=create repo:com.derakkuma.best?action=update repo:com.derakkuma.friend?action=create repo:com.derakkuma.friend?action=update repo:com.derakkuma.favoriteSong?action=create repo:com.derakkuma.favoriteSong?action=update repo:com.derakkuma.circle?action=create repo:com.derakkuma.circle?action=update repo:com.derakkuma.circleMember?action=create repo:com.derakkuma.circleMember?action=update repo:com.derakkuma.friend?action=delete repo:com.derakkuma.favoriteSong?action=delete repo:app.bsky.graph.follow?action=create", "token_endpoint_auth_method": "none", "dpop_bound_access_tokens": true }