List locally stored artifacts on the ChatRoomDetailScreen

The Library section hardcoded "No artifacts exists". Load and render the
chat room's stored artifacts instead.

- MantraArtifactDao.getArtifactsByChatRoomId + MantraRepository.getArtifacts
  expose a chat room's artifacts (newest first).
- ChatRoomDetailUIState.Loaded carries artifacts; ChatRoomDetailViewModel
  gains mantraRepository and loads them in initiateChatRoomDetail.
- ChatRoomDetailScreen renders the empty-state text only when there are no
  artifacts, otherwise a Card/ListItem per artifact (name, url, chevron to a
  pending detail route). The screen takes mantraRepository, threaded through
  the factory, the nav host (databaseMantraRepository), and the preview.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kgothatso Ngako
2026-07-26 00:57:53 +02:00
parent 24a00f793a
commit 308a28c073
7 changed files with 69 additions and 7 deletions

View File

@@ -1,6 +1,7 @@
package press.mantra.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
import press.mantra.compose.database.model.MantraArtifact
@@ -8,4 +9,7 @@ import press.mantra.compose.database.model.MantraArtifact
interface MantraArtifactDao {
@Upsert
suspend fun upsert(mantraArtifact: MantraArtifact)
@Query("SELECT * FROM MantraArtifact WHERE chatRoomId = :chatRoomId ORDER BY createdAt DESC")
suspend fun getArtifactsByChatRoomId(chatRoomId: String): List<MantraArtifact>
}

View File

@@ -21,6 +21,9 @@ class DatabaseMantraRepository(
private val logger = Logger.withTag(TAG)
override suspend fun getArtifacts(chatRoomId: String): List<MantraArtifact> =
database.mantraArtifactDao().getArtifactsByChatRoomId(chatRoomId)
override suspend fun getDialects(chatRoomId: String): List<MantraDialect> =
database.mantraDialectDao().getDialectsByChatRoomId(chatRoomId)

View File

@@ -1,10 +1,13 @@
package press.mantra.compose.repository
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import press.mantra.compose.database.model.MantraArtifact
import press.mantra.compose.database.model.MantraDialect
import press.mantra.compose.database.model.MarmotInnerEvent
interface MantraRepository {
suspend fun getArtifacts(chatRoomId: String): List<MantraArtifact>
suspend fun getDialects(chatRoomId: String): List<MantraDialect>
suspend fun addDialect(
@@ -38,6 +41,8 @@ interface MantraRepository {
const val DEFAULT_LICENSE = "cc"
val NO_OP_MANTRA_REPOSITORY = object: MantraRepository{
override suspend fun getArtifacts(chatRoomId: String): List<MantraArtifact> = emptyList()
override suspend fun getDialects(chatRoomId: String): List<MantraDialect> = emptyList()
override suspend fun addDialect(

View File

@@ -46,6 +46,7 @@ import press.mantra.compose.database.model.Profile
import press.mantra.compose.database.model.intermdiate.LocalChatRoom
import press.mantra.compose.database.model.intermdiate.LocalParticipant
import press.mantra.compose.repository.ChatRepository
import press.mantra.compose.repository.MantraRepository
import press.mantra.compose.repository.NostrRepository
import press.mantra.compose.ui.composable.navigation.routes.ImplementationPendingRoute
import press.mantra.compose.ui.composable.navigation.routes.Route
@@ -67,6 +68,7 @@ fun ChatRoomDetailScreen(
initialChatRoomDetailUIState: ChatRoomDetailUIState = ChatRoomDetailUIState.Loading,
nostrRepository: NostrRepository,
chatRepository: ChatRepository,
mantraRepository: MantraRepository,
onNavigateToRoute: (Route) -> Unit,
onPopBackToRoute: (Route) -> Unit
) {
@@ -77,6 +79,7 @@ fun ChatRoomDetailScreen(
initialChatRoomDetailUIState = initialChatRoomDetailUIState,
nostrRepository = nostrRepository,
chatRepository = chatRepository,
mantraRepository = mantraRepository,
activeUserPublicKey = activeUserPublicKey
)
)
@@ -135,8 +138,46 @@ fun ChatRoomDetailScreen(
)
}
item {
Text("No artifacts exists in this groups library.")
if (chatRoomDetailUIState.artifacts.isEmpty()) {
item {
Text("No artifacts exists in this groups library.")
}
} else {
items(
items = chatRoomDetailUIState.artifacts,
key = { artifact -> artifact.id }
) { artifact ->
Card(
onClick = {
onNavigateToRoute.invoke(
ImplementationPendingRoute(
"Artifact detail"
)
)
}
) {
ListItem(
leadingContent = {
Icon(
Icons.Default.LibraryBooks,
contentDescription = "Artifact"
)
},
trailingContent = {
Icon(
Icons.Default.ChevronRight,
contentDescription = "View artifact"
)
},
headlineContent = {
Text(text = artifact.name)
},
supportingContent = {
Text(text = artifact.url)
}
)
}
}
}
item {
@@ -435,6 +476,7 @@ private fun ChatRoomMessagingScreenPreview() {
),
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY,
chatRepository = ChatRepository.NO_OP_CHAT_REPOSITORY,
mantraRepository = MantraRepository.NO_OP_MANTRA_REPOSITORY,
onNavigateToRoute = {},
onPopBackToRoute = {}
)

View File

@@ -597,6 +597,7 @@ fun MantraNavHost(
relayHint = route.relayHint,
nostrRepository = databaseNostrRepository,
chatRepository = databaseChatRepository,
mantraRepository = databaseMantraRepository,
onNavigateToRoute = { actionRoute ->
navController.navigate(
route = actionRoute

View File

@@ -11,6 +11,7 @@ import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import press.mantra.compose.database.model.intermdiate.LocalChatRoom
import press.mantra.compose.repository.ChatRepository
import press.mantra.compose.repository.MantraRepository
import press.mantra.compose.repository.NostrRepository
import press.mantra.compose.ui.composable.navigation.routes.HomeRoute
import press.mantra.compose.ui.composable.navigation.routes.Route
@@ -27,7 +28,8 @@ class ChatRoomDetailViewModel(
val relayHint: String?,
initialChatRoomDetailUIState: ChatRoomDetailUIState,
val nostrRepository: NostrRepository,
val chatRepository: ChatRepository
val chatRepository: ChatRepository,
val mantraRepository: MantraRepository,
): ViewModel() {
var chatRoomDetailUIState: ChatRoomDetailUIState by mutableStateOf(initialChatRoomDetailUIState)
@@ -51,7 +53,8 @@ class ChatRoomDetailViewModel(
ChatRoomDetailUIState.Error("Missing chat room")
} else {
ChatRoomDetailUIState.Loaded(
localChatRoom = localChatRoom
localChatRoom = localChatRoom,
artifacts = mantraRepository.getArtifacts(chatRoomId),
)
}
}
@@ -107,7 +110,8 @@ class ChatRoomDetailViewModel(
relayHint: String?,
initialChatRoomDetailUIState: ChatRoomDetailUIState = ChatRoomDetailUIState.Loading,
nostrRepository: NostrRepository,
chatRepository: ChatRepository
chatRepository: ChatRepository,
mantraRepository: MantraRepository,
): ViewModelProvider.Factory = viewModelFactory {
initializer {
ChatRoomDetailViewModel(
@@ -116,7 +120,8 @@ class ChatRoomDetailViewModel(
relayHint = relayHint,
initialChatRoomDetailUIState = initialChatRoomDetailUIState,
nostrRepository = nostrRepository,
chatRepository = chatRepository
chatRepository = chatRepository,
mantraRepository = mantraRepository,
)
}
}

View File

@@ -1,10 +1,12 @@
package press.mantra.compose.ui.view.state
import press.mantra.compose.database.model.MantraArtifact
import press.mantra.compose.database.model.intermdiate.LocalChatRoom
sealed interface ChatRoomDetailUIState {
data class Loaded(
val localChatRoom: LocalChatRoom
val localChatRoom: LocalChatRoom,
val artifacts: List<MantraArtifact> = emptyList(),
): ChatRoomDetailUIState
data class Error(