Phase 4 of docs/multiple-profiles.md. One pushed screen, and the startup selector brought up to match it. ProfilesRoute, reached from the profile tab's row -- "Switch profile", where it said "Change account" and went to the pending screen -- and drawn by ProfilesScreen: a top app bar with a back button, and WalletsSelector as its whole content with the open profile above the divider and the others below. Tapping another row is switchToIdentity and nothing else; the observer restarts the app as that identity through its lock gate, so the screen is never around to see the result. Tapping the open row does nothing. Three states, said so: loading while the list is read, which in practice is never seen; an unreadable key file, through ErrorState with a retry that re-lists; and the list. Empty cannot happen while something is signed in and the screen does not pretend it can. One column at every width. The row is offered to every kind: a read-only identity can leave for another profile the same way a signing one can. ProfilesViewModel joins the flows the app already holds -- the listing state, the identities, the active identity, the wallet metadata -- to the profile rows for the listed keys, through NostrRepository.observeProfilesOf, one map that moves when any of them does. Nothing in it writes. What a row shows changes. WalletsSelector showed "Default name" over a random emoji for every row, because nothing in this app writes the wallet metadata's name, and two profiles side by side both called Default name and told apart by a bech32 string is not a switcher. The row now takes the nostr profile -- humanReadableNameOrPubkey over ProfileAvatar, the widgets the profile tab draws itself with -- with the npub on the second line, and falls back to the npub over the metadata's emoji for a key the device has no kind 0 for yet: one just created, or a read-only one that was never found. And it says what the device holds for the profile before the tap: "Wallet" for a seed attached, beside the "Read only" that was already there, and nothing for the plain case of a bare key. It is the difference between a profile that can be signed out of and one whose sign-out is a wallet question, and between two profiles with the same name. The startup screen draws the same widget and gets the same map through rememberProfilesOf, so both lists look the same. The selector's dead globalPrefs parameter goes with the rewrite. Tests: ProfilesScreenJvmTest, on the unmerged tree, with a wallet-attached A open, a bare B and a read-only C -- A by its nostr name with its npub and "Wallet" under it, B and C by their npub with only C labelled, no "Default name" anywhere, the open one drawn above the others; tapping A calls nothing and tapping B calls the switch once with B's id; an unreadable key file is an error whose retry is counted; the bar has a back button. ReadOnlyEntrancesJvmTest asserts "Switch profile" for both kinds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Pulled-From: curated/curated@25464595f4
This is a Kotlin Multiplatform project targeting Android, iOS, Desktop (JVM).
-
/composeApp is for code that will be shared across your Compose Multiplatform applications. It contains several subfolders:
- commonMain is for code that’s common for all targets.
- Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. For example, if you want to use Apple’s CoreCrypto for the iOS part of your Kotlin app, the iosMain folder would be the right place for such calls. Similarly, if you want to edit the Desktop (JVM) specific part, the jvmMain folder is the appropriate location.
-
/iosApp contains iOS applications. Even if you’re sharing your UI with Compose Multiplatform, you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.
Build and Run Android Application
To build and run the development version of the Android app, use the run configuration from the run widget in your IDE’s toolbar or build it directly from the terminal:
- on macOS/Linux
./gradlew :composeApp:assembleDebug - on Windows
.\gradlew.bat :composeApp:assembleDebug
Build and Run Desktop (JVM) Application
To build and run the development version of the desktop app, use the run configuration from the run widget in your IDE’s toolbar or run it directly from the terminal:
- on macOS/Linux
./gradlew :composeApp:run - on Windows
.\gradlew.bat :composeApp:run
Build and Run iOS Application
To build and run the development version of the iOS app, use the run configuration from the run widget in your IDE’s toolbar or open the /iosApp directory in Xcode and run it from there.
Learn more about Kotlin Multiplatform…