Make room for a database.
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
package ac.aux.compose.database
|
||||
|
||||
import ac.aux.compose.database.converters.Converters
|
||||
import ac.aux.compose.database.dao.NostrDao
|
||||
import ac.aux.compose.database.dao.NostrEventDao
|
||||
import ac.aux.compose.database.dao.PostDao
|
||||
import ac.aux.compose.database.dao.ProfileDao
|
||||
import ac.aux.compose.database.dao.ReactionDao
|
||||
import ac.aux.compose.database.dao.RepostDao
|
||||
import ac.aux.compose.database.dao.ZapDao
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import ac.aux.compose.database.model.Reaction
|
||||
import ac.aux.compose.database.model.Repost
|
||||
import ac.aux.compose.database.model.Zap
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
NostrEvent::class,
|
||||
Post::class,
|
||||
Profile::class,
|
||||
Reaction::class,
|
||||
Repost::class,
|
||||
Zap::class
|
||||
],
|
||||
version = 1
|
||||
)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class AuxDatabase: RoomDatabase() {
|
||||
abstract fun nostrDao(): NostrDao
|
||||
|
||||
abstract fun nostrEventDao(): NostrEventDao
|
||||
abstract fun postDao(): PostDao
|
||||
abstract fun profileDao(): ProfileDao
|
||||
abstract fun reactionDao(): ReactionDao
|
||||
abstract fun repostDao(): RepostDao
|
||||
abstract fun zapDao(): ZapDao
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package ac.aux.compose.database.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.time.Instant
|
||||
|
||||
class Converters {
|
||||
@TypeConverter
|
||||
fun fromTimestamp(value: Long?): Instant? {
|
||||
return value?.let { Instant.fromEpochMilliseconds(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun instantToTimestamp(instant: Instant?): Long? {
|
||||
return instant?.toEpochMilliseconds()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromTagArray(value: TagArray?): String? {
|
||||
// Returns null if the value is null, otherwise serializes to JSON string
|
||||
return value?.let { Json.encodeToString(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toTagArray(value: String?): TagArray? {
|
||||
// Decodes the JSON string back into the nested array structure
|
||||
return value?.let { Json.decodeFromString<TagArray>(it) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.AuxDatabase
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Transaction
|
||||
|
||||
@Dao
|
||||
abstract class NostrDao(
|
||||
private val database: AuxDatabase
|
||||
) {
|
||||
@Transaction
|
||||
open suspend fun insertPostAndNostrEvent(post: Post, nostrEvent: NostrEvent) {
|
||||
database.nostrEventDao().insert(nostrEvent)
|
||||
database.postDao().insert(post) // TODO: Make use of the nostrEventId...
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface NostrEventDao {
|
||||
@Query("SELECT * FROM NostrEvent")
|
||||
fun getAllNostrEvents(): List<NostrEvent>
|
||||
|
||||
@Query("SELECT * FROM NostrEvent WHERE id = :id")
|
||||
fun getNostrEventById(id: String): Flow<NostrEvent?>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(nostrEvent: NostrEvent)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.Relation
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface PostDao {
|
||||
@Query("SELECT * FROM Post")
|
||||
fun getAllPosts(): List<PostAndProfile>
|
||||
|
||||
@Query("SELECT * FROM Post WHERE id = :id")
|
||||
fun getPostById(id: String): Flow<Post?>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(post: Post)
|
||||
|
||||
data class PostAndProfile(
|
||||
@Embedded val post: Post,
|
||||
@Relation(
|
||||
parentColumn = "profilePublicKey",
|
||||
entityColumn = "publicKey"
|
||||
)
|
||||
val profile: Profile
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface ProfileDao {
|
||||
@Query("SELECT * FROM Profile")
|
||||
fun getAllPosts(): List<Profile>
|
||||
|
||||
@Query("SELECT * FROM Profile WHERE publicKey = :publicKey")
|
||||
fun getProfileByPublicKey(publicKey: String): Flow<Profile?>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(profile: Profile)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import ac.aux.compose.database.model.Reaction
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface ReactionDao {
|
||||
@Query("SELECT * FROM Reaction")
|
||||
fun getAllReactions(): List<Reaction>
|
||||
|
||||
@Query("SELECT * FROM Reaction WHERE id = :id")
|
||||
fun getReactionById(id: String): Flow<Reaction?>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(reaction: Reaction)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.database.model.Repost
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface RepostDao {
|
||||
@Query("SELECT * FROM Repost")
|
||||
fun getAllReposts(): List<Repost>
|
||||
|
||||
@Query("SELECT * FROM Repost WHERE id = :id")
|
||||
fun getRepostById(id: String): Flow<Repost?>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(repost: Repost)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.database.model.Zap
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface ZapDao {
|
||||
@Query("SELECT * FROM Zap WHERE id = :id")
|
||||
fun getZapById(id: String): Flow<Zap?>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(zap: Zap)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Fts4
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Kind
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Entity
|
||||
data class NostrEvent(
|
||||
@PrimaryKey
|
||||
val id: String,
|
||||
val pubKey: HexKey,
|
||||
val createdAt: Instant,
|
||||
val savedAt: Instant,
|
||||
val broadcastedAt: Instant,
|
||||
val kind: Kind,
|
||||
val tags: TagArray,
|
||||
val content: String,
|
||||
val sig: HexKey,
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || this::class != other::class) return false
|
||||
|
||||
other as NostrEvent
|
||||
|
||||
if (kind != other.kind) return false
|
||||
if (id != other.id) return false
|
||||
if (pubKey != other.pubKey) return false
|
||||
if (createdAt != other.createdAt) return false
|
||||
if (!tags.contentDeepEquals(other.tags)) return false
|
||||
if (content != other.content) return false
|
||||
if (sig != other.sig) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = kind
|
||||
result = 31 * result + id.hashCode()
|
||||
result = 31 * result + pubKey.hashCode()
|
||||
result = 31 * result + createdAt.hashCode()
|
||||
result = 31 * result + tags.contentDeepHashCode()
|
||||
result = 31 * result + content.hashCode()
|
||||
result = 31 * result + sig.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Kind
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = Profile::class,
|
||||
parentColumns = ["publicKey"],
|
||||
childColumns = ["profilePublicKey"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// This might cause problems... but we gonna make the feed so cool if it works...
|
||||
ForeignKey(
|
||||
entity = NostrEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["nostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// Support post replies
|
||||
ForeignKey(
|
||||
entity = Post::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["replyToId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// Support reposts
|
||||
ForeignKey(
|
||||
entity = Post::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["repostId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
indices = [
|
||||
Index("profilePublicKey"),
|
||||
Index("nostrEventId"),
|
||||
Index("replyToId"),
|
||||
Index("repostId")
|
||||
],
|
||||
)
|
||||
data class Post(
|
||||
@PrimaryKey
|
||||
val id: HexKey,
|
||||
val nostrEventId: HexKey, // Might be a replaceable nostr event?
|
||||
val replyToId: HexKey? = null,
|
||||
val repostId: HexKey? = null,
|
||||
val quote: HexKey? = null,
|
||||
val profilePublicKey: HexKey,
|
||||
val createdAt: Instant,
|
||||
val savedAt: Instant,
|
||||
val broadcastedAt: Instant,
|
||||
val content: String,
|
||||
) {
|
||||
companion object {
|
||||
val DUMMY_POSTS = arrayOf(
|
||||
Post(
|
||||
id = "genesisnoteid",
|
||||
nostrEventId = "genesisnoteid", // Need to check how to handle addressableEvents...
|
||||
profilePublicKey = "satoshi",
|
||||
createdAt = Clock.System.now(),
|
||||
savedAt = Clock.System.now(),
|
||||
broadcastedAt = Clock.System.now(),
|
||||
content = "Chancellor the rapper...",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = NostrEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["nostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
)
|
||||
],
|
||||
indices = [
|
||||
Index("nostrEventId"),
|
||||
],
|
||||
)
|
||||
data class Profile(
|
||||
@PrimaryKey
|
||||
val publicKey: String,
|
||||
val nostrEventId: String,
|
||||
val userName: String? = null, // name in the nostr event...
|
||||
val displayName: String? = null,
|
||||
val picture: String? = null,
|
||||
val banner: String? = null,
|
||||
val website: String? = null,
|
||||
val about: String? = null,
|
||||
val bot: Boolean? = null,
|
||||
val pronouns: String? = null,
|
||||
val nip05: String? = null,
|
||||
val domain: String? = null,
|
||||
val lud06: String? = null,
|
||||
val lud16: String? = null,
|
||||
val twitter: String? = null
|
||||
) {
|
||||
companion object {
|
||||
val DUMMAY_PROFILES = arrayOf<Profile>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Kind
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = Profile::class,
|
||||
parentColumns = ["publicKey"],
|
||||
childColumns = ["profilePublicKey"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// This might cause problems... but we gonna make the feed so cool if it works...
|
||||
ForeignKey(
|
||||
entity = NostrEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["nostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// Support post replies
|
||||
ForeignKey(
|
||||
entity = Post::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["replyToId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
indices = [
|
||||
Index("profilePublicKey"),
|
||||
Index("nostrEventId"),
|
||||
Index("replyToId")
|
||||
],
|
||||
)
|
||||
data class Reaction(
|
||||
@PrimaryKey
|
||||
val id: HexKey,
|
||||
val nostrEventId: HexKey, // Might be a replaceable nostr event?
|
||||
val replyToId: HexKey,
|
||||
val profilePublicKey: HexKey,
|
||||
val createdAt: Instant,
|
||||
val content: String,
|
||||
)
|
||||
@@ -0,0 +1,50 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Kind
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = Profile::class,
|
||||
parentColumns = ["publicKey"],
|
||||
childColumns = ["profilePublicKey"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// This might cause problems... but we gonna make the feed so cool if it works...
|
||||
ForeignKey(
|
||||
entity = NostrEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["nostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// Support post replies
|
||||
ForeignKey(
|
||||
entity = Post::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["postId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
indices = [
|
||||
Index("profilePublicKey"),
|
||||
Index("nostrEventId"),
|
||||
Index("postId")
|
||||
],
|
||||
)
|
||||
data class Repost(
|
||||
@PrimaryKey
|
||||
val id: HexKey,
|
||||
val nostrEventId: HexKey, // Might be a replaceable nostr event?
|
||||
val postId: HexKey? = null,
|
||||
val profilePublicKey: HexKey,
|
||||
val createdAt: Instant,
|
||||
val content: String,
|
||||
)
|
||||
@@ -0,0 +1,55 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = Profile::class,
|
||||
parentColumns = ["publicKey"],
|
||||
childColumns = ["senderPublicKey"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
ForeignKey(
|
||||
entity = Profile::class,
|
||||
parentColumns = ["publicKey"],
|
||||
childColumns = ["receiverPublicKey"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// This might cause problems... but we gonna make the feed so cool if it works...
|
||||
ForeignKey(
|
||||
entity = NostrEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["nostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// Support post replies
|
||||
ForeignKey(
|
||||
entity = Post::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["postId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
indices = [
|
||||
Index("senderPublicKey"),
|
||||
Index("nostrEventId"),
|
||||
Index("postId")
|
||||
],
|
||||
)
|
||||
data class Zap(
|
||||
@PrimaryKey
|
||||
val id: HexKey,
|
||||
val nostrEventId: HexKey, // Might be a replaceable nostr event?
|
||||
val postId: HexKey,
|
||||
val senderPublicKey: HexKey,
|
||||
val receiverPublicKey: HexKey,
|
||||
val createdAt: Instant,
|
||||
val message: String?,
|
||||
val invoice: String?,
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
package ac.aux.compose.database.repository
|
||||
|
||||
import ac.aux.compose.database.AuxDatabase
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.repository.PostRepository
|
||||
|
||||
class DatabasePostRepository(
|
||||
private val database: AuxDatabase
|
||||
): PostRepository {
|
||||
override suspend fun publishPost(
|
||||
post: Post,
|
||||
nostrEvent: NostrEvent
|
||||
) {
|
||||
val nostrDao = database.nostrDao()
|
||||
|
||||
nostrDao.insertPostAndNostrEvent(
|
||||
post,
|
||||
nostrEvent
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package ac.aux.compose.repository
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
|
||||
interface PostRepository {
|
||||
suspend fun publishPost(post: Post, nostrEvent: NostrEvent)
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
package ac.aux.compose.ui.composable
|
||||
|
||||
import ac.aux.compose.ui.composable.widgets.LoadingDataIndicator
|
||||
import ac.aux.compose.ui.composable.widgets.feed.FeedListItem
|
||||
import ac.aux.compose.ui.composable.widgets.feed.ListView
|
||||
import ac.aux.compose.ui.composable.widgets.feed.LoadedFeedView
|
||||
import ac.aux.compose.ui.theme.AuxTheme
|
||||
import ac.aux.compose.ui.view.model.FeedListViewModel
|
||||
import ac.aux.compose.ui.view.state.FeedListUIState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -25,6 +26,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@@ -91,10 +93,23 @@ fun FeedScreen(
|
||||
Text("Something went wrong")
|
||||
}
|
||||
is FeedListUIState.Loaded -> {
|
||||
LoadedFeedView(
|
||||
events = feedListUIState.events,
|
||||
onNavigateToEvent = onNavigateToEvent
|
||||
)
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
items(
|
||||
items = feedListUIState.events,
|
||||
key = { e -> e.id }
|
||||
) { event ->
|
||||
FeedListItem(
|
||||
event = event,
|
||||
onNavigateToEvent = {
|
||||
onNavigateToEvent.invoke(event)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ac.aux.compose.ui.composable.widgets.feed
|
||||
import ac.aux.compose.ui.theme.AuxTheme
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
@@ -28,6 +29,9 @@ fun Event.ListView() {
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Row {
|
||||
|
||||
}
|
||||
Text(
|
||||
text = content,
|
||||
)
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package ac.aux.compose.ui.composable.widgets.feed
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
|
||||
@Composable
|
||||
fun LoadedFeedView(
|
||||
events: List<Event>,
|
||||
onNavigateToEvent: (Event) -> Unit
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
items(
|
||||
items = events,
|
||||
key = { e -> e.id }
|
||||
) { event ->
|
||||
FeedListItem(
|
||||
event = event,
|
||||
onNavigateToEvent = {
|
||||
onNavigateToEvent.invoke(event)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user