The UI half. Tapping somebody else's message in a group offers "Reply privately to <name>", which arms the composer; sending clears it. The riskiest thing about this feature is not cryptographic. It is somebody sending to the room what they meant for one person, or the reverse, and neither can be taken back once it is on the wire. So an armed composer carries three signals at once -- a chip naming the recipient, a placeholder that says "Private message to <name>" instead of "Say what now?", and a tinted field -- and the chip's close button is the single tap back to the room. The recipient is read and cleared together with the text before the send suspends, so a second message cannot inherit the first one's audience. Two renderings, because a direct message looks different depending on whether this device can open it. Readable: the ordinary bubble, plus a lock and "Private to <name>" (or "Private to you"). A private message must never pass for a public one, and the label names the other party because that is what a reader would otherwise assume was the whole room. Opaque: a system line -- "Alice sent a private message to Bob" -- in the shape of RitualNotice rather than a bubble. An empty bubble attributed to Alice would read as her having said nothing, and one with placeholder text would read as her having said the placeholder. It is not tappable; there is nothing behind it to open. Reply privately is offered only on somebody else's message in an MLS room. A NIP-17 room has no audience for a message to be private from, so there the option would be meaningless. Names resolve from the room's participants, which the view model already holds -- the recipient is a member by definition -- so neither line needs a join, and both fall back to a shortened key rather than to "unknown". A line that cannot say which member it means is worse than an ugly one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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…