Kgothatso Ngako 116cc96de4 feat: give a group's proposals a screen of their own
The transcript is where a proposal is met, and it is a bad place to keep one. It
answers one question -- is this still asking something of you -- in the middle of
everything else the room said that day, and then it scrolls. Until now the only
other way in was a signing screen that resolved "the room's live session", so a
room with two proposals open had one of them reachable and the group's history
had none of it.

So: one row per session, newest first, live. The ones still waiting on the reader
are gathered under "Waiting for you" and the rest follow, because they are two
different kinds of thing to read -- the rest is what the group has done, those
are what it is waiting on this member for -- and because burying the second open
proposal under the first one's history is the whole failure this screen exists
to answer.

**Each row carries its own state, from the same rule the signing screen uses.**
Whether a proposal still has a decision in it is asked of
`FrostSigningManager.isAwaitingApproval` rather than worked out again here, so
the list and the screen behind it cannot come to different answers about the same
session. The rest of the status is the session's own stage said briefly: you
agreed and it is waiting on n of m, you are one of the signers, enough members
took part without you, signed, or -- for an abandoned one -- its failure reason,
because "declined on this device" and "the group could not agree" are different
things to have happened and the reason is the whole content of the ending.

**Named after the lead item.** A batch's first item is the one the rest hang off
-- a chapter's chunks carry the chapter's id -- so the chapter names the row and
the count says the rest of it. An item that could not be read is said on the row
rather than left for the screen behind it: a batch is all-or-nothing, so an
unreadable item is a reason to refuse the whole proposal.

**One query, not one per row.** `LocalFrostSigningSession` embeds the session and
relates its items and its proposer, and `observeSessionsForChatRoom` becomes a
`@Transaction` query over it -- the repository already declared that method and
nothing called it, so this is the shape it should have had rather than a second
query beside it. The model sorts the items rather than the query: Room does not
order a relation, and item order is protocol rather than presentation, since two
devices reading a batch in different orders aggregate against different messages.
The proposer is joined for the reason the transcript joins a sender -- so a
rename follows, and a member seen only as a pubkey is not stuck on the
placeholder their profile was created with.

**Derived once per emission.** Every row's events come out of stored JSON. That
is not work to repeat on each recomposition of a scrolling list, so the view
model does it when the flow emits and the screen renders what it is handed.

Reachable from the group's detail screen, beside Shared Key and gated the same
way: a shared threshold key only means anything in a room where every member is
an equal admin, and a room with no key to sign with has nothing to propose.

**Tests.** FrostSigningSessionDaoJvmTest covers what the list reads -- two
sessions in a room come back newest first, each with its items in `itemIndex`
order despite the relation's own order, and with the proposer resolved to a name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 12:42:29 +02:00
2026-07-05 23:21:02 +02:00
2026-07-28 09:10:20 +02:00
2026-03-24 04:47:19 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00

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 thats 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 Apples 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 youre 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 IDEs 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 IDEs 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 IDEs toolbar or open the /iosApp directory in Xcode and run it from there.


Learn more about Kotlin Multiplatform

Description
Mantra as a kotlin multiplatform project
https://mantra.press
Readme 8.2 MiB
Languages
Kotlin 100%