diff --git a/composeApp/src/commonMain/kotlin/press/mantra/compose/extensions/Member.kt b/composeApp/src/commonMain/kotlin/press/mantra/compose/extensions/Member.kt new file mode 100644 index 00000000..b3fdf441 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/press/mantra/compose/extensions/Member.kt @@ -0,0 +1,30 @@ +package press.mantra.compose.extensions + +import press.mantra.compose.database.model.intermdiate.LocalParticipant +import com.vitorpamplona.quartz.nip01Core.core.HexKey + +/** Enough of a public key to tell two members apart when neither has a profile yet. */ +private const val SHORTENED_PUBLIC_KEY_LENGTH = 8 + +/** A public key cut to something readable, for where no profile has arrived yet. */ +fun HexKey.shortened(): String = take(SHORTENED_PUBLIC_KEY_LENGTH) + +/** + * What to call the member behind this public key on screen. + * + * Resolved from the profiles joined onto the room rather than stored anywhere, so + * it follows a rename and is never stuck on the "LOADING..." placeholder a member + * is given the moment they are first seen. Falls back to a short key, which is at + * least stable and distinguishing, when there is no profile at all yet. + */ +fun HexKey.memberName( + members: List, + activeUserPublicKey: HexKey +): String = if (this == activeUserPublicKey) { + "You" +} else { + members.firstOrNull { it.participant.participantPublicKey == this } + ?.profile + ?.humanReadableNameOrPubkey() + ?: shortened() +} diff --git a/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/composable/DkgRitualScreen.kt b/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/composable/DkgRitualScreen.kt index 45d9bf91..1e253804 100644 --- a/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/composable/DkgRitualScreen.kt +++ b/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/composable/DkgRitualScreen.kt @@ -37,8 +37,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel @@ -47,6 +50,7 @@ import press.mantra.compose.database.model.DkgSession import press.mantra.compose.database.model.intermdiate.LocalChatRoom import press.mantra.compose.database.model.intermdiate.LocalParticipant import press.mantra.compose.database.model.types.DkgRitualStage +import press.mantra.compose.extensions.memberName import press.mantra.compose.managers.ChillDkgRitualManager import press.mantra.compose.repository.ChatRepository import press.mantra.compose.repository.DkgRepository @@ -237,6 +241,19 @@ private fun RitualProgress( val round1Count = round1Participants.size val round2Count = round2Participants.size + // Above the failure branch so it holds in every state. Any member can open a + // ceremony and it settles the group's signing quorum for good, so who opened + // this one is worth saying whether it is running, finished or abandoned. + Text( + text = buildAnnotatedString { + withStyle(SpanStyle(color = ProfileColor.fromPublicKey(session.coordinatorPublicKey))) { + append(session.coordinatorPublicKey.memberName(members, activeUserPublicKey)) + } + append(" started this ceremony.") + }, + style = MaterialTheme.typography.bodyMedium + ) + if (stage == DkgRitualStage.FAILED) { Card( modifier = Modifier.fillMaxWidth(), @@ -394,11 +411,7 @@ private fun RitualRoster( Text( modifier = Modifier.weight(1f), - text = if (publicKey == activeUserPublicKey) { - "You" - } else { - member.profile?.humanReadableNameOrPubkey() ?: publicKey.take(8) - }, + text = publicKey.memberName(members, activeUserPublicKey), maxLines = 1, overflow = TextOverflow.MiddleEllipsis, color = ProfileColor.fromPublicKey(publicKey), diff --git a/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/view/model/ChatMessageListViewModel.kt b/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/view/model/ChatMessageListViewModel.kt index 0c55289c..68842759 100755 --- a/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/view/model/ChatMessageListViewModel.kt +++ b/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/view/model/ChatMessageListViewModel.kt @@ -56,6 +56,7 @@ import press.mantra.compose.database.model.NegentropySynchronizeRequest import press.mantra.compose.database.model.intermdiate.LocalChatMessage import press.mantra.compose.database.model.intermdiate.LocalChatRoom import press.mantra.compose.database.model.types.SynchronizationFilter +import press.mantra.compose.extensions.shortened import press.mantra.compose.extensions.toFormattedTimeAndDateString import press.mantra.compose.nostr.Relays import press.mantra.compose.repository.ChatRepository @@ -530,7 +531,7 @@ private fun RitualNotice( "You" } else { localChatMessage.profile?.humanReadableNameOrPubkey() - ?: chatMessage.senderPublicKey.take(SHORTENED_PUBLIC_KEY_LENGTH) + ?: chatMessage.senderPublicKey.shortened() } ) } @@ -557,6 +558,3 @@ private fun RitualNotice( ) } } - -/** Enough of a public key to tell two members apart when neither has a profile yet. */ -private const val SHORTENED_PUBLIC_KEY_LENGTH = 8