feat: auto-publish key package on MarmotGroupListScreen entry

Replace the manual KeyPackage publish button with a LaunchedEffect
that auto-publishes if no key package exists yet. The subscription
already correctly downloads our own kind:30443 via
ownKeyPackageFilter() on home relays, and rotation is handled
reactively when Welcome messages are processed.

https://claude.ai/code/session_01LhfCp8DHqNVx6mSiYfpQny
This commit is contained in:
Claude
2026-04-08 01:03:18 +00:00
parent f6538d4c59
commit 6fe2852011
@@ -36,8 +36,6 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.VpnKey
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.HorizontalDivider
@@ -45,8 +43,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
@@ -54,12 +50,10 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
@@ -71,8 +65,6 @@ import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -81,10 +73,6 @@ fun MarmotGroupListScreen(
nav: INav,
) {
var groupList by remember { mutableStateOf(listOf<Pair<HexKey, MarmotGroupChatroom>>()) }
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
var isPublishing by remember { mutableStateOf(false) }
var hasPublishedKeyPackage by remember { mutableStateOf(accountViewModel.hasPublishedKeyPackage()) }
// Load group list
LaunchedEffect(Unit) {
@@ -98,6 +86,17 @@ fun MarmotGroupListScreen(
}
}
// Auto-publish KeyPackage if none exists yet
LaunchedEffect(Unit) {
if (!accountViewModel.hasPublishedKeyPackage()) {
try {
accountViewModel.publishMarmotKeyPackage()
} catch (_: Exception) {
// Silently retry on next screen visit
}
}
}
Scaffold(
topBar = {
TopAppBar(
@@ -110,47 +109,8 @@ fun MarmotGroupListScreen(
}
},
title = { Text("Marmot Groups") },
actions = {
if (isPublishing) {
CircularProgressIndicator(
modifier = Modifier.padding(12.dp).size(24.dp),
strokeWidth = 2.dp,
strokeCap = StrokeCap.Round,
)
} else {
IconButton(
onClick = {
isPublishing = true
scope.launch(Dispatchers.IO) {
try {
accountViewModel.publishMarmotKeyPackage()
hasPublishedKeyPackage = true
snackbarHostState.showSnackbar("KeyPackage published successfully")
} catch (e: Exception) {
snackbarHostState.showSnackbar("Failed to publish KeyPackage: ${e.message}")
} finally {
isPublishing = false
}
}
},
) {
Icon(
imageVector = Icons.Default.VpnKey,
contentDescription =
if (hasPublishedKeyPackage) "Republish KeyPackage" else "Publish KeyPackage",
tint =
if (hasPublishedKeyPackage) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.onSurfaceVariant
},
)
}
}
},
)
},
snackbarHost = { SnackbarHost(snackbarHostState) },
floatingActionButton = {
FloatingActionButton(onClick = { nav.nav(Route.CreateMarmotGroup) }, shape = CircleShape) {
Icon(Icons.Default.Add, contentDescription = "Create Group")
@@ -168,12 +128,7 @@ fun MarmotGroupListScreen(
style = MaterialTheme.typography.titleMedium,
)
Text(
text =
if (hasPublishedKeyPackage) {
"Your KeyPackage is published. Waiting for group invitations."
} else {
"Tap the key icon above to publish your KeyPackage and receive invitations."
},
text = "Create a group or wait for an invitation.",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp),