mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-09 08:04:45 +00:00
refactor(napplets): render browse rows via shared NoteCompose
Replace the bespoke NappletCard with NoteCompose — the same path the main feed uses for kind 15129/35129 events. This reuses the author header, the shared StaticWebsiteCard (title/description/source/servers/capability list + Open button wired to NappletLauncher), and the standard reaction bar (reply/boost/like/zap), instead of a second hand-rolled card that could drift from the feed and was missing NoteCompose's timestamp, hidden-user handling, zap-amount menus, etc. The new top bar + follow-list filter (defaultNappletsFollowList / matchAuthor) are genuinely new and kept as-is. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ncMHuBBVHEf7spAoSssde
This commit is contained in:
+12
-104
@@ -20,56 +20,42 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SuggestionChip
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.napplet.NappletLauncher
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.ReactionsRow
|
||||
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets.datasource.NappletsFilterAssemblerSubscription
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip5dNapplets.NamedNappletEvent
|
||||
import com.vitorpamplona.quartz.nip5dNapplets.NappletManifest
|
||||
import com.vitorpamplona.quartz.nip5dNapplets.RootNappletEvent
|
||||
|
||||
/**
|
||||
* Lists the napplet manifests currently in the local cache (NIP-5D kinds 15129/35129) and opens
|
||||
* the selected one in the sandboxed [NappletLauncher] host. The top bar carries a follow-list filter
|
||||
* (like the Pictures/Articles feeds) and each row is a rich card with the author, declared
|
||||
* capabilities, and the usual reaction bar (reply/boost/like/zap).
|
||||
* Lists the napplet manifests currently in the local cache (NIP-5D kinds 15129/35129). The top bar
|
||||
* carries a follow-list filter (like the Pictures/Articles feeds) and each row is rendered through the
|
||||
* shared [NoteCompose] — the same path the main feed uses for these events — so it gets the author
|
||||
* header, the [com.vitorpamplona.amethyst.commons.ui.note.StaticWebsiteCard] (title, description,
|
||||
* capability chips, and an Open button wired to the sandboxed [NappletLauncher]), and the standard
|
||||
* reaction bar (reply/boost/like/zap) for free, without a second card implementation that could drift.
|
||||
*/
|
||||
@Composable
|
||||
fun NappletsScreen(
|
||||
@@ -104,23 +90,15 @@ fun NappletsScreen(
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val context = LocalContext.current
|
||||
LazyColumn(Modifier.fillMaxSize().padding(padding)) {
|
||||
items(visible, key = { it.id }) { event ->
|
||||
val manifest = event as? NappletManifest ?: return@items
|
||||
NappletCard(
|
||||
event = event,
|
||||
manifest = manifest,
|
||||
val note = remember(event.id) { Amethyst.instance.cache.getOrCreateNote(event) }
|
||||
NoteCompose(
|
||||
baseNote = note,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
quotesLeft = 3,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
onClick = {
|
||||
NappletLauncher.launch(
|
||||
context = context,
|
||||
manifest = manifest,
|
||||
authorPubKey = event.pubKey,
|
||||
identifier = (event as? NamedNappletEvent)?.identifier() ?: "",
|
||||
)
|
||||
},
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
@@ -128,73 +106,3 @@ fun NappletsScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun NappletCard(
|
||||
event: Event,
|
||||
manifest: NappletManifest,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val note = remember(event.id) { Amethyst.instance.cache.getOrCreateNote(event) }
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
UserPicture(userHex = event.pubKey, size = 48.dp, accountViewModel = accountViewModel, nav = nav)
|
||||
Spacer(Modifier.width(12.dp))
|
||||
Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
Text(
|
||||
text = manifest.title()?.ifBlank { null } ?: stringResource(R.string.napplet_untitled),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
LoadUser(baseUserHex = event.pubKey, accountViewModel) { user ->
|
||||
if (user != null) {
|
||||
UsernameDisplay(user, accountViewModel = accountViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
manifest.description()?.takeIf { it.isNotBlank() }?.let {
|
||||
Text(
|
||||
text = it,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
val requires = manifest.requires()
|
||||
if (requires.isNotEmpty()) {
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
requires.forEach { capability ->
|
||||
SuggestionChip(
|
||||
onClick = onClick,
|
||||
label = { Text(capability.replaceFirstChar { it.uppercase() }) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReactionsRow(
|
||||
baseNote = note,
|
||||
showReactionDetail = true,
|
||||
addPadding = false,
|
||||
editState = null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user