Handling reposts
This commit is contained in:
1 parent
3cefeae5c1
commit
c1d7572d44
14 files changed
+383
-161
No files matched your search
@@ -2,7 +2,7 @@
|
|||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"identityHash": "0e3d4940d3b045a7e68c2f0b98642ab0",
|
"identityHash": "6c8c4b9a4b44bed3c7fde29fcf54d7df",
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
"tableName": "BroadcastNostrEventReceipt",
|
"tableName": "BroadcastNostrEventReceipt",
|
||||||
@@ -735,7 +735,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tableName": "Repost",
|
"tableName": "Repost",
|
||||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `repostedPostId` TEXT NOT NULL, `content` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `profilePublicKey` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`profilePublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`repostedPostId`) REFERENCES `Post`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `repostedPostId` TEXT NOT NULL, `repostedPostTags` TEXT NOT NULL, `repostedPostCreatedAt` INTEGER NOT NULL, `repostedPostContent` TEXT NOT NULL, `repostedPostAuthorPublicKey` TEXT NOT NULL, `content` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `profilePublicKey` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`profilePublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`repostedPostAuthorPublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`repostedPostId`) REFERENCES `Post`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "id",
|
"fieldPath": "id",
|
||||||
@@ -749,6 +749,30 @@
|
|||||||
"affinity": "TEXT",
|
"affinity": "TEXT",
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "repostedPostTags",
|
||||||
|
"columnName": "repostedPostTags",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "repostedPostCreatedAt",
|
||||||
|
"columnName": "repostedPostCreatedAt",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "repostedPostContent",
|
||||||
|
"columnName": "repostedPostContent",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "repostedPostAuthorPublicKey",
|
||||||
|
"columnName": "repostedPostAuthorPublicKey",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldPath": "content",
|
"fieldPath": "content",
|
||||||
"columnName": "content",
|
"columnName": "content",
|
||||||
@@ -848,6 +872,17 @@
|
|||||||
"publicKey"
|
"publicKey"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"table": "Profile",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"repostedPostAuthorPublicKey"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"publicKey"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"table": "NostrEvent",
|
"table": "NostrEvent",
|
||||||
"onDelete": "CASCADE",
|
"onDelete": "CASCADE",
|
||||||
@@ -1307,7 +1342,7 @@
|
|||||||
],
|
],
|
||||||
"setupQueries": [
|
"setupQueries": [
|
||||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0e3d4940d3b045a7e68c2f0b98642ab0')"
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6c8c4b9a4b44bed3c7fde29fcf54d7df')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +44,7 @@ abstract class NostrDao(
|
|||||||
)
|
)
|
||||||
val placeHolderProfileNostrEvent = NostrEvent(
|
val placeHolderProfileNostrEvent = NostrEvent(
|
||||||
id = placeHolderProfile.publicKey,
|
id = placeHolderProfile.publicKey,
|
||||||
kind = 30024,
|
kind = DraftWrapEvent.KIND,
|
||||||
pubKey = placeHolderProfile.publicKey,
|
pubKey = placeHolderProfile.publicKey,
|
||||||
content = placeHolderProfile.publicKey,
|
content = placeHolderProfile.publicKey,
|
||||||
createdAt = Clock.System.now(),
|
createdAt = Clock.System.now(),
|
||||||
@@ -81,44 +81,6 @@ abstract class NostrDao(
|
|||||||
synchronizationRelayURLs: List<String>
|
synchronizationRelayURLs: List<String>
|
||||||
) {
|
) {
|
||||||
logger.i("Store Nostr Event: $nostrEvent")
|
logger.i("Store Nostr Event: $nostrEvent")
|
||||||
if (nostrEvent.unsignedNostrEventId == null) {
|
|
||||||
logger.i("Store Nostr Event: $nostrEvent")
|
|
||||||
// Find or create profile with the pubKey... if not found submit a sync request...
|
|
||||||
val profile = database.profileDao().getProfileByPublicKey(nostrEvent.pubKey)
|
|
||||||
|
|
||||||
if (profile == null) {
|
|
||||||
val placeHolderProfile = Profile(
|
|
||||||
publicKey = nostrEvent.pubKey,
|
|
||||||
nostrEventId = nostrEvent.pubKey // This is illegal... because the nostrEventId should be the one with all the profile information
|
|
||||||
)
|
|
||||||
val placeHolderProfileNostrEvent = NostrEvent(
|
|
||||||
id = placeHolderProfile.publicKey,
|
|
||||||
kind = DraftWrapEvent.KIND,
|
|
||||||
pubKey = placeHolderProfile.publicKey,
|
|
||||||
content = placeHolderProfile.publicKey,
|
|
||||||
createdAt = Clock.System.now(),
|
|
||||||
sig = placeHolderProfile.publicKey,
|
|
||||||
tags = emptyArray(),
|
|
||||||
)
|
|
||||||
database.nostrEventDao().upsert(placeHolderProfileNostrEvent)
|
|
||||||
database.profileDao().upsert(placeHolderProfile)
|
|
||||||
|
|
||||||
synchronizationRelayURLs.forEach { synchronizationRelayURL ->
|
|
||||||
logger.i("Sync Profile with PubKey: ${nostrEvent.pubKey}")
|
|
||||||
database.synchronizeNostrEventRequestDao().upsert(
|
|
||||||
SynchronizeNostrEventRequest(
|
|
||||||
synchronizationFilters = arrayOf(
|
|
||||||
SynchronizationFilter(
|
|
||||||
authors = arrayOf(nostrEvent.pubKey),
|
|
||||||
kinds = arrayOf(MetadataEvent.KIND)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
relayURL = synchronizationRelayURL
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
database.nostrEventDao().upsert(nostrEvent)
|
database.nostrEventDao().upsert(nostrEvent)
|
||||||
|
|
||||||
// Index nostrEvent
|
// Index nostrEvent
|
||||||
@@ -132,6 +94,34 @@ abstract class NostrDao(
|
|||||||
nostrEvent: NostrEvent,
|
nostrEvent: NostrEvent,
|
||||||
synchronizationRelayURLs: List<String>
|
synchronizationRelayURLs: List<String>
|
||||||
) {
|
) {
|
||||||
|
if (nostrEvent.unsignedNostrEventId == null) {
|
||||||
|
logger.i("Store Nostr Event: $nostrEvent")
|
||||||
|
// Find or create profile with the pubKey... if not found submit a sync request...
|
||||||
|
val profile = database.profileDao().getProfileByPublicKey(nostrEvent.pubKey)
|
||||||
|
|
||||||
|
if (profile == null) {
|
||||||
|
val placeHolderProfile = Profile(
|
||||||
|
publicKey = nostrEvent.pubKey,
|
||||||
|
nostrEventId = nostrEvent.id, // Will get overwriting by sync,
|
||||||
|
)
|
||||||
|
database.profileDao().insertPlaceholderProfile(placeHolderProfile)
|
||||||
|
|
||||||
|
database.synchronizeNostrEventRequestDao().insert(
|
||||||
|
synchronizationRelayURLs.map { synchronizationRelayURL ->
|
||||||
|
SynchronizeNostrEventRequest(
|
||||||
|
synchronizationFilters = arrayOf(
|
||||||
|
SynchronizationFilter(
|
||||||
|
authors = arrayOf(nostrEvent.pubKey),
|
||||||
|
kinds = arrayOf(MetadataEvent.KIND)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
relayURL = synchronizationRelayURL
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nostrEvent.toPost()?.let { post ->
|
nostrEvent.toPost()?.let { post ->
|
||||||
if (post.replyToId != null) {
|
if (post.replyToId != null) {
|
||||||
// Find or create placeHolder note that is replyTo
|
// Find or create placeHolder note that is replyTo
|
||||||
@@ -149,8 +139,8 @@ abstract class NostrDao(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Request a sync for the post being replied to
|
// Request a sync for the post being replied to
|
||||||
synchronizationRelayURLs.forEach { synchronizationRelayURL ->
|
database.synchronizeNostrEventRequestDao().insert(
|
||||||
database.synchronizeNostrEventRequestDao().upsert(
|
synchronizationRelayURLs.map { synchronizationRelayURL ->
|
||||||
SynchronizeNostrEventRequest(
|
SynchronizeNostrEventRequest(
|
||||||
synchronizationFilters = arrayOf(
|
synchronizationFilters = arrayOf(
|
||||||
SynchronizationFilter(
|
SynchronizationFilter(
|
||||||
@@ -159,8 +149,8 @@ abstract class NostrDao(
|
|||||||
),
|
),
|
||||||
relayURL = synchronizationRelayURL
|
relayURL = synchronizationRelayURL
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,8 +168,8 @@ abstract class NostrDao(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
// Request a sync for the post being reposted
|
// Request a sync for the post being reposted
|
||||||
synchronizationRelayURLs.forEach { synchronizationRelayURL ->
|
database.synchronizeNostrEventRequestDao().insert(
|
||||||
database.synchronizeNostrEventRequestDao().upsert(
|
synchronizationRelayURLs.map { synchronizationRelayURL ->
|
||||||
SynchronizeNostrEventRequest(
|
SynchronizeNostrEventRequest(
|
||||||
synchronizationFilters = arrayOf(
|
synchronizationFilters = arrayOf(
|
||||||
SynchronizationFilter(
|
SynchronizationFilter(
|
||||||
@@ -188,11 +178,11 @@ abstract class NostrDao(
|
|||||||
),
|
),
|
||||||
relayURL = synchronizationRelayURL
|
relayURL = synchronizationRelayURL
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
database.postDao().insert(post)
|
database.postDao().upsert(post)
|
||||||
}
|
}
|
||||||
|
|
||||||
nostrEvent.toProfile()?.let { profile ->
|
nostrEvent.toProfile()?.let { profile ->
|
||||||
@@ -219,8 +209,8 @@ abstract class NostrDao(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Request a sync for the post being replied to
|
// Request a sync for the post being replied to
|
||||||
synchronizationRelayURLs.forEach { synchronizationRelayURL ->
|
database.synchronizeNostrEventRequestDao().insert(
|
||||||
database.synchronizeNostrEventRequestDao().upsert(
|
synchronizationRelayURLs.map { synchronizationRelayURL ->
|
||||||
SynchronizeNostrEventRequest(
|
SynchronizeNostrEventRequest(
|
||||||
synchronizationFilters = arrayOf(
|
synchronizationFilters = arrayOf(
|
||||||
SynchronizationFilter(
|
SynchronizationFilter(
|
||||||
@@ -229,8 +219,35 @@ abstract class NostrDao(
|
|||||||
),
|
),
|
||||||
relayURL = synchronizationRelayURL
|
relayURL = synchronizationRelayURL
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val repostedPostProfile = database.profileDao().getProfileByPublicKey(repost.repostedPostAuthorPublicKey)
|
||||||
|
|
||||||
|
if (repostedPostProfile == null) {
|
||||||
|
// Persist PlaceHolder and sync
|
||||||
|
database.profileDao().insertPlaceholderProfile(
|
||||||
|
Profile(
|
||||||
|
publicKey = nostrEvent.pubKey,
|
||||||
|
nostrEventId = nostrEvent.id, // Will get overwriting by sync,
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
|
||||||
|
// Request a sync for the post being replied to
|
||||||
|
database.synchronizeNostrEventRequestDao().insert(
|
||||||
|
synchronizationRelayURLs.map { synchronizationRelayURL ->
|
||||||
|
SynchronizeNostrEventRequest(
|
||||||
|
synchronizationFilters = arrayOf(
|
||||||
|
SynchronizationFilter(
|
||||||
|
authors = arrayOf(nostrEvent.pubKey),
|
||||||
|
kinds = arrayOf(MetadataEvent.KIND)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
relayURL = synchronizationRelayURL
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,8 +267,8 @@ abstract class NostrDao(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Request a sync for the post being replied to
|
// Request a sync for the post being replied to
|
||||||
synchronizationRelayURLs.forEach { synchronizationRelayURL ->
|
database.synchronizeNostrEventRequestDao().insert(
|
||||||
database.synchronizeNostrEventRequestDao().upsert(
|
synchronizationRelayURLs.map { synchronizationRelayURL ->
|
||||||
SynchronizeNostrEventRequest(
|
SynchronizeNostrEventRequest(
|
||||||
synchronizationFilters = arrayOf(
|
synchronizationFilters = arrayOf(
|
||||||
SynchronizationFilter(
|
SynchronizationFilter(
|
||||||
@@ -260,8 +277,8 @@ abstract class NostrDao(
|
|||||||
),
|
),
|
||||||
relayURL = synchronizationRelayURL
|
relayURL = synchronizationRelayURL
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Placeholder for all the other things...
|
// TODO: Placeholder for all the other things...
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
package ac.aux.compose.database.dao
|
package ac.aux.compose.database.dao
|
||||||
|
|
||||||
import ac.aux.compose.database.model.NostrEvent
|
import ac.aux.compose.database.model.NostrEvent
|
||||||
|
import ac.aux.compose.database.model.intermdiate.LocalNostrEvent
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy.Companion.IGNORE
|
import androidx.room.OnConflictStrategy.Companion.IGNORE
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.Upsert
|
import androidx.room.Upsert
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Kind
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface NostrEventDao {
|
interface NostrEventDao {
|
||||||
@Query("SELECT * FROM NostrEvent ORDER BY createdAt DESC")
|
@Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) ORDER BY savedAt DESC")
|
||||||
fun getAllNostrEvents(): List<NostrEvent>
|
fun observeNostrEvents(kinds: Array<Kind>): Flow<List<LocalNostrEvent>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) ORDER BY savedAt DESC")
|
||||||
|
fun getAllNostrEvents(kinds: Array<Kind>): List<NostrEvent>
|
||||||
|
|
||||||
@Query("SELECT * FROM NostrEvent WHERE id = :id")
|
@Query("SELECT * FROM NostrEvent WHERE id = :id")
|
||||||
fun getNostrEventById(id: String): Flow<NostrEvent?>
|
fun getNostrEventById(id: String): Flow<NostrEvent?>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import androidx.room.Embedded
|
|||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.Relation
|
import androidx.room.Relation
|
||||||
|
import androidx.room.Upsert
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
@@ -20,4 +21,7 @@ interface PostDao {
|
|||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insert(post: Post)
|
suspend fun insert(post: Post)
|
||||||
|
|
||||||
|
@Upsert
|
||||||
|
suspend fun upsert(post: Post)
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,6 @@ import ac.aux.compose.database.model.traits.SoftDeletableEntity
|
|||||||
import ac.aux.compose.database.model.traits.TimestampedEntity
|
import ac.aux.compose.database.model.traits.TimestampedEntity
|
||||||
import ac.aux.compose.database.model.traits.UnsignedNostrEventEntity
|
import ac.aux.compose.database.model.traits.UnsignedNostrEventEntity
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.ForeignKey
|
|
||||||
import androidx.room.Ignore
|
import androidx.room.Ignore
|
||||||
import androidx.room.Index
|
import androidx.room.Index
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
@@ -197,18 +196,23 @@ data class NostrEvent(
|
|||||||
sig = sig
|
sig = sig
|
||||||
)
|
)
|
||||||
|
|
||||||
return repostEvent.boostedEventId()?.let { repostedPostId ->
|
return repostEvent.containedPost()?.let { containedPost ->
|
||||||
Repost(
|
Repost(
|
||||||
id = repostEvent.id,
|
id = repostEvent.id,
|
||||||
repostedPostId = repostedPostId,
|
repostedPostId = containedPost.id,
|
||||||
|
repostedPostTags = containedPost.tags,
|
||||||
|
repostedPostContent = containedPost.content,
|
||||||
|
repostedPostCreatedAt = Instant.fromEpochSeconds(containedPost.createdAt),
|
||||||
content = repostEvent.content,
|
content = repostEvent.content,
|
||||||
|
|
||||||
nostrEventId = id,
|
nostrEventId = id,
|
||||||
profilePublicKey = repostEvent.pubKey,
|
profilePublicKey = repostEvent.pubKey,
|
||||||
|
repostedPostAuthorPublicKey = containedPost.pubKey,
|
||||||
|
|
||||||
createdAt = Instant.fromEpochSeconds(repostEvent.createdAt),
|
createdAt = Instant.fromEpochSeconds(repostEvent.createdAt),
|
||||||
updatedAt = Instant.fromEpochSeconds(repostEvent.createdAt),
|
updatedAt = Instant.fromEpochSeconds(repostEvent.createdAt),
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import androidx.room.ForeignKey
|
|||||||
import androidx.room.Index
|
import androidx.room.Index
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||||
import kotlin.time.Clock
|
import kotlin.time.Clock
|
||||||
import kotlin.time.Instant
|
import kotlin.time.Instant
|
||||||
|
|
||||||
@@ -23,6 +24,12 @@ import kotlin.time.Instant
|
|||||||
childColumns = ["profilePublicKey"],
|
childColumns = ["profilePublicKey"],
|
||||||
onDelete = ForeignKey.CASCADE,
|
onDelete = ForeignKey.CASCADE,
|
||||||
),
|
),
|
||||||
|
ForeignKey(
|
||||||
|
entity = Profile::class,
|
||||||
|
parentColumns = ["publicKey"],
|
||||||
|
childColumns = ["repostedPostAuthorPublicKey"],
|
||||||
|
onDelete = ForeignKey.CASCADE,
|
||||||
|
),
|
||||||
// This might cause problems... but we gonna make the feed so cool if it works...
|
// This might cause problems... but we gonna make the feed so cool if it works...
|
||||||
ForeignKey(
|
ForeignKey(
|
||||||
entity = NostrEvent::class,
|
entity = NostrEvent::class,
|
||||||
@@ -47,7 +54,13 @@ import kotlin.time.Instant
|
|||||||
data class Repost(
|
data class Repost(
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
val id: HexKey,
|
val id: HexKey,
|
||||||
|
|
||||||
val repostedPostId: HexKey,
|
val repostedPostId: HexKey,
|
||||||
|
val repostedPostTags: TagArray,
|
||||||
|
val repostedPostCreatedAt: Instant,
|
||||||
|
val repostedPostContent: String,
|
||||||
|
val repostedPostAuthorPublicKey: HexKey,
|
||||||
|
|
||||||
val content: String,
|
val content: String,
|
||||||
|
|
||||||
override val nostrEventId: HexKey, // Might be a replaceable nostr event?
|
override val nostrEventId: HexKey, // Might be a replaceable nostr event?
|
||||||
@@ -59,4 +72,44 @@ data class Repost(
|
|||||||
override val viewedAt: Instant? = null,
|
override val viewedAt: Instant? = null,
|
||||||
override val deletedAt: Instant? = null,
|
override val deletedAt: Instant? = null,
|
||||||
override val broadcastedAt: Instant? = null,
|
override val broadcastedAt: Instant? = null,
|
||||||
): NostrEventEntity, ProfilePublicKeyEntity, TimestampedEntity, LocalStoreEntity, UserViewableEntity, BroadcastableEntity, SoftDeletableEntity
|
): NostrEventEntity, ProfilePublicKeyEntity, TimestampedEntity, LocalStoreEntity, UserViewableEntity, BroadcastableEntity, SoftDeletableEntity {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other == null || this::class != other::class) return false
|
||||||
|
|
||||||
|
other as Repost
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
|
if (repostedPostId != other.repostedPostId) return false
|
||||||
|
if (repostedPostAuthorPublicKey != other.repostedPostAuthorPublicKey) return false
|
||||||
|
if (!repostedPostTags.contentDeepEquals(other.repostedPostTags)) return false
|
||||||
|
if (content != other.content) return false
|
||||||
|
if (nostrEventId != other.nostrEventId) return false
|
||||||
|
if (profilePublicKey != other.profilePublicKey) return false
|
||||||
|
if (createdAt != other.createdAt) return false
|
||||||
|
if (updatedAt != other.updatedAt) return false
|
||||||
|
if (savedAt != other.savedAt) return false
|
||||||
|
if (viewedAt != other.viewedAt) return false
|
||||||
|
if (deletedAt != other.deletedAt) return false
|
||||||
|
if (broadcastedAt != other.broadcastedAt) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = id.hashCode()
|
||||||
|
result = 31 * result + repostedPostId.hashCode()
|
||||||
|
result = 31 * result + repostedPostAuthorPublicKey.hashCode()
|
||||||
|
result = 31 * result + repostedPostTags.contentDeepHashCode()
|
||||||
|
result = 31 * result + content.hashCode()
|
||||||
|
result = 31 * result + nostrEventId.hashCode()
|
||||||
|
result = 31 * result + profilePublicKey.hashCode()
|
||||||
|
result = 31 * result + createdAt.hashCode()
|
||||||
|
result = 31 * result + updatedAt.hashCode()
|
||||||
|
result = 31 * result + savedAt.hashCode()
|
||||||
|
result = 31 * result + (viewedAt?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (deletedAt?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (broadcastedAt?.hashCode() ?: 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package ac.aux.compose.database.model.intermdiate
|
||||||
|
|
||||||
|
import ac.aux.compose.database.model.NostrEvent
|
||||||
|
import ac.aux.compose.database.model.Post
|
||||||
|
import ac.aux.compose.database.model.Profile
|
||||||
|
import androidx.room.Embedded
|
||||||
|
import androidx.room.Relation
|
||||||
|
|
||||||
|
data class LocalNostrEvent(
|
||||||
|
@Embedded val nostrEvent: NostrEvent,
|
||||||
|
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "pubKey",
|
||||||
|
entityColumn = "publicKey"
|
||||||
|
)
|
||||||
|
val profile: Profile
|
||||||
|
)
|
||||||
+11
-4
@@ -8,25 +8,26 @@ import ac.aux.compose.database.model.Profile
|
|||||||
import ac.aux.compose.database.model.SynchronizeNostrEventRequest
|
import ac.aux.compose.database.model.SynchronizeNostrEventRequest
|
||||||
import ac.aux.compose.database.model.UnsignedNostrEvent
|
import ac.aux.compose.database.model.UnsignedNostrEvent
|
||||||
import ac.aux.compose.database.model.intermdiate.LocalBroadcastNostrEventRequest
|
import ac.aux.compose.database.model.intermdiate.LocalBroadcastNostrEventRequest
|
||||||
|
import ac.aux.compose.database.model.intermdiate.LocalNostrEvent
|
||||||
import ac.aux.compose.database.model.intermdiate.LocalProfile
|
import ac.aux.compose.database.model.intermdiate.LocalProfile
|
||||||
import ac.aux.compose.nostr.Relays
|
import ac.aux.compose.nostr.Relays
|
||||||
import ac.aux.compose.repository.NostrRepository
|
import ac.aux.compose.repository.NostrRepository
|
||||||
import co.touchlab.kermit.Logger
|
import co.touchlab.kermit.Logger
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent.Companion.ALT
|
|
||||||
import com.vitorpamplona.quartz.nip02FollowList.RelaySet
|
import com.vitorpamplona.quartz.nip02FollowList.RelaySet
|
||||||
import com.vitorpamplona.quartz.nip02FollowList.tags.ContactTag
|
import com.vitorpamplona.quartz.nip02FollowList.tags.ContactTag
|
||||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||||
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
|
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
|
||||||
|
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListEvent
|
||||||
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
import com.vitorpamplona.quartz.nip31Alts.AltTag
|
||||||
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
|
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent
|
import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
|
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.tags.RelayTag
|
import com.vitorpamplona.quartz.nip51Lists.tags.RelayTag
|
||||||
|
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
|
||||||
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@@ -296,8 +297,14 @@ class DatabaseNostrRepository(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun loadNostrFeed(): List<NostrEvent> {
|
override suspend fun observeNostrFeed(): Flow<List<LocalNostrEvent>> {
|
||||||
return database.nostrEventDao().getAllNostrEvents()
|
return database.nostrEventDao().observeNostrEvents(
|
||||||
|
kinds = arrayOf(
|
||||||
|
TextNoteEvent.KIND,
|
||||||
|
RepostEvent.KIND,
|
||||||
|
LnZapEvent.KIND
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@ import ac.aux.compose.database.model.Profile
|
|||||||
import ac.aux.compose.database.model.SynchronizeNostrEventRequest
|
import ac.aux.compose.database.model.SynchronizeNostrEventRequest
|
||||||
import ac.aux.compose.database.model.UnsignedNostrEvent
|
import ac.aux.compose.database.model.UnsignedNostrEvent
|
||||||
import ac.aux.compose.database.model.intermdiate.LocalBroadcastNostrEventRequest
|
import ac.aux.compose.database.model.intermdiate.LocalBroadcastNostrEventRequest
|
||||||
|
import ac.aux.compose.database.model.intermdiate.LocalNostrEvent
|
||||||
import ac.aux.compose.database.model.intermdiate.LocalProfile
|
import ac.aux.compose.database.model.intermdiate.LocalProfile
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@@ -64,7 +65,7 @@ interface NostrRepository {
|
|||||||
synchronizeNostrEventRequests: List<SynchronizeNostrEventRequest>,
|
synchronizeNostrEventRequests: List<SynchronizeNostrEventRequest>,
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun loadNostrFeed(): List<NostrEvent>
|
suspend fun observeNostrFeed(): Flow<List<LocalNostrEvent>>
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val NO_OP_NOSTR_REPOSITORY = object : NostrRepository {
|
val NO_OP_NOSTR_REPOSITORY = object : NostrRepository {
|
||||||
@@ -142,7 +143,7 @@ interface NostrRepository {
|
|||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun loadNostrFeed(): List<NostrEvent> {
|
override suspend fun observeNostrFeed(): Flow<List<LocalNostrEvent>> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import androidx.compose.material3.MediumTopAppBar
|
|||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -42,7 +41,6 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -187,14 +185,12 @@ fun FeedScreen(
|
|||||||
|
|
||||||
}
|
}
|
||||||
items(
|
items(
|
||||||
items = feedListUIState.nostrEvents,
|
items = feedListUIState.localNostrEvents,
|
||||||
key = { e -> e.id }
|
key = { localNostrEvent -> localNostrEvent.nostrEvent.id }
|
||||||
) { nostrEvent ->
|
) { localNostrEvent ->
|
||||||
FeedListItem(
|
FeedListItem(
|
||||||
nostrEvent = nostrEvent,
|
localNostrEvent = localNostrEvent,
|
||||||
onNavigateToEvent = {
|
onNavigateToEvent = onNavigateToEvent
|
||||||
onNavigateToEvent.invoke(nostrEvent)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+129
-64
@@ -1,6 +1,8 @@
|
|||||||
package ac.aux.compose.ui.composable.widgets.feed
|
package ac.aux.compose.ui.composable.widgets.feed
|
||||||
|
|
||||||
import ac.aux.compose.database.model.NostrEvent
|
import ac.aux.compose.database.model.NostrEvent
|
||||||
|
import ac.aux.compose.database.model.Profile
|
||||||
|
import ac.aux.compose.database.model.intermdiate.LocalNostrEvent
|
||||||
import ac.aux.compose.extensions.toFormattedTimeAndDateString
|
import ac.aux.compose.extensions.toFormattedTimeAndDateString
|
||||||
import ac.aux.compose.ui.theme.AuxTheme
|
import ac.aux.compose.ui.theme.AuxTheme
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -13,7 +15,9 @@ import androidx.compose.foundation.lazy.items
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Person
|
import androidx.compose.material.icons.filled.Person
|
||||||
import androidx.compose.material3.AssistChip
|
import androidx.compose.material3.AssistChip
|
||||||
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.SuggestionChip
|
import androidx.compose.material3.SuggestionChip
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -24,78 +28,130 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
|
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NostrEvent.ListView() {
|
fun LocalNostrEvent.ListView(
|
||||||
when (kind) {
|
onNavigateToEvent: (NostrEvent) -> Unit
|
||||||
// TODO: Support other kinds...
|
) {
|
||||||
else -> {
|
when (nostrEvent.kind) {
|
||||||
|
RepostEvent.KIND -> {
|
||||||
|
val repost = nostrEvent.toRepost()
|
||||||
|
// TODO: Make use of the indexed Repost field to render...
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(10.dp).fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth().padding(10.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth()
|
||||||
horizontalArrangement = Arrangement.spacedBy(10.dp, Alignment.Start),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
) {
|
||||||
Icon(
|
Text(
|
||||||
Icons.Default.Person,
|
modifier = Modifier.weight(1f),
|
||||||
contentDescription = "Profile picture"
|
text = profile.displayName ?: profile.userName ?: profile.publicKey,
|
||||||
|
style = MaterialTheme.typography.labelSmall
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = pubKey
|
text = "reposted",
|
||||||
)
|
style = MaterialTheme.typography.labelSmall)
|
||||||
}
|
|
||||||
Text(
|
|
||||||
text = content,
|
|
||||||
)
|
|
||||||
|
|
||||||
LazyRow(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(
|
|
||||||
10.dp,
|
|
||||||
alignment = Alignment.Start
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
item {
|
|
||||||
AssistChip(
|
|
||||||
label = {
|
|
||||||
Text(
|
|
||||||
text = kind.toString() // TODO: Kind by name...
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onClick = {
|
|
||||||
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
items(
|
|
||||||
items = tags,
|
|
||||||
) { tag ->
|
|
||||||
SuggestionChip(
|
|
||||||
label = {
|
|
||||||
Text(
|
|
||||||
text = tag.joinToString(":")
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onClick = {
|
|
||||||
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
TextNoteFeedItem(
|
||||||
text = createdAt.toFormattedTimeAndDateString()
|
this@ListView,
|
||||||
|
onNavigateToEvent = onNavigateToEvent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO: Support other kinds...
|
||||||
|
else -> {
|
||||||
|
TextNoteFeedItem(
|
||||||
|
this,
|
||||||
|
onNavigateToEvent = onNavigateToEvent
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TextNoteFeedItem(
|
||||||
|
localNostrEvent: LocalNostrEvent,
|
||||||
|
onNavigateToEvent: (NostrEvent) -> Unit
|
||||||
|
) {
|
||||||
|
ElevatedCard(
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
horizontal = 10.dp
|
||||||
|
),
|
||||||
|
onClick = {
|
||||||
|
onNavigateToEvent.invoke(localNostrEvent.nostrEvent)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(10.dp).fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(10.dp, Alignment.Start),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Person,
|
||||||
|
contentDescription = "Profile picture",
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
text = localNostrEvent.profile.displayName ?: localNostrEvent.profile.userName ?: localNostrEvent.profile.publicKey,
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = localNostrEvent.nostrEvent.content,
|
||||||
|
)
|
||||||
|
|
||||||
|
LazyRow(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(
|
||||||
|
10.dp,
|
||||||
|
alignment = Alignment.Start
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
AssistChip(
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
text = localNostrEvent.nostrEvent.kind.toString() // TODO: Kind by name...
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
items(
|
||||||
|
items = localNostrEvent.nostrEvent.tags,
|
||||||
|
) { tag ->
|
||||||
|
SuggestionChip(
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
text = tag.joinToString(":")
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = localNostrEvent.nostrEvent.createdAt.toFormattedTimeAndDateString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun EventListViewPreview() {
|
private fun EventListViewPreview() {
|
||||||
@@ -103,14 +159,23 @@ private fun EventListViewPreview() {
|
|||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.padding(20.dp)
|
modifier = Modifier.padding(20.dp)
|
||||||
) {
|
) {
|
||||||
NostrEvent(
|
LocalNostrEvent(
|
||||||
id = "hex",
|
nostrEvent = NostrEvent(
|
||||||
content = "Something something seomthing",
|
id = "hex",
|
||||||
pubKey = "hexpubkeypubkeypubkeypubkey",
|
content = "Something something seomthing",
|
||||||
kind = 0,
|
pubKey = "hexpubkeypubkeypubkeypubkey",
|
||||||
sig = "hexsig",
|
kind = 0,
|
||||||
tags = emptyArray()
|
sig = "hexsig",
|
||||||
).ListView()
|
tags = emptyArray()
|
||||||
|
),
|
||||||
|
profile = Profile(
|
||||||
|
publicKey = "",
|
||||||
|
nostrEventId = "referenced",
|
||||||
|
displayName = "Frank Talk"
|
||||||
|
)
|
||||||
|
).ListView(
|
||||||
|
onNavigateToEvent = {}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+5
-12
@@ -1,26 +1,19 @@
|
|||||||
package ac.aux.compose.ui.composable.widgets.feed
|
package ac.aux.compose.ui.composable.widgets.feed
|
||||||
|
|
||||||
import ac.aux.compose.database.model.NostrEvent
|
import ac.aux.compose.database.model.NostrEvent
|
||||||
|
import ac.aux.compose.database.model.intermdiate.LocalNostrEvent
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FeedListItem(
|
fun FeedListItem(
|
||||||
nostrEvent: NostrEvent,
|
localNostrEvent: LocalNostrEvent,
|
||||||
onNavigateToEvent: (NostrEvent) -> Unit
|
onNavigateToEvent: (NostrEvent) -> Unit
|
||||||
) {
|
) {
|
||||||
ElevatedCard(
|
localNostrEvent.ListView(
|
||||||
modifier = Modifier.padding(
|
onNavigateToEvent = onNavigateToEvent
|
||||||
horizontal = 10.dp
|
)
|
||||||
),
|
|
||||||
onClick = {
|
|
||||||
onNavigateToEvent.invoke(nostrEvent)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
nostrEvent.ListView()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -38,6 +38,30 @@ class FeedListViewModel(
|
|||||||
|
|
||||||
fun loadNostrFeed() {
|
fun loadNostrFeed() {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
// Sync Feed
|
||||||
|
// nostrRepository.queueSynchronizeNostrEvent(
|
||||||
|
// Relays.eventPublishRelaySet.map { normalizedRelay ->
|
||||||
|
// SynchronizeNostrEventRequest(
|
||||||
|
// synchronizationFilters = arrayOf(
|
||||||
|
// SynchronizationFilter(
|
||||||
|
// kinds = arrayOf(
|
||||||
|
// TextNoteEvent.KIND,
|
||||||
|
// RepostEvent.KIND,
|
||||||
|
// ReactionEvent.KIND,
|
||||||
|
// LnZapEvent.KIND,
|
||||||
|
// ),
|
||||||
|
// tags = mapOf(
|
||||||
|
// Pair("p", listOf(SeedManager.activePublicKey().toHexKey()))
|
||||||
|
// ),
|
||||||
|
// limit = 50
|
||||||
|
// )
|
||||||
|
// ),
|
||||||
|
// relayURL = normalizedRelay.url
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
|
||||||
|
// Sync Notifications
|
||||||
nostrRepository.queueSynchronizeNostrEvent(
|
nostrRepository.queueSynchronizeNostrEvent(
|
||||||
Relays.eventPublishRelaySet.map { normalizedRelay ->
|
Relays.eventPublishRelaySet.map { normalizedRelay ->
|
||||||
SynchronizeNostrEventRequest(
|
SynchronizeNostrEventRequest(
|
||||||
@@ -67,11 +91,11 @@ class FeedListViewModel(
|
|||||||
if (localProfile?.profile == null) {
|
if (localProfile?.profile == null) {
|
||||||
feedListUIState = FeedListUIState.Error
|
feedListUIState = FeedListUIState.Error
|
||||||
} else {
|
} else {
|
||||||
val nostrFeed = nostrRepository.loadNostrFeed()
|
nostrRepository.observeNostrFeed().collect { localNostrEvents ->
|
||||||
|
feedListUIState = FeedListUIState.Loaded(
|
||||||
feedListUIState = FeedListUIState.Loaded(
|
localNostrEvents = localNostrEvents
|
||||||
nostrEvents = nostrFeed
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package ac.aux.compose.ui.view.state
|
package ac.aux.compose.ui.view.state
|
||||||
|
|
||||||
import ac.aux.compose.database.model.NostrEvent
|
import ac.aux.compose.database.model.NostrEvent
|
||||||
|
import ac.aux.compose.database.model.intermdiate.LocalNostrEvent
|
||||||
|
|
||||||
sealed interface FeedListUIState {
|
sealed interface FeedListUIState {
|
||||||
data class Loaded(
|
data class Loaded(
|
||||||
val nostrEvents: List<NostrEvent>
|
val localNostrEvents: List<LocalNostrEvent>
|
||||||
): FeedListUIState
|
): FeedListUIState
|
||||||
|
|
||||||
data object Error: FeedListUIState
|
data object Error: FeedListUIState
|
||||||
|
|||||||
Reference in new issue
Block a user