diff --git a/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/1.json b/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/1.json index 9e52d1fb..e8c377f8 100644 --- a/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/1.json +++ b/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "98aa54dd7fe776faca364638070a3871", + "identityHash": "296c3251f1f835c144c94c7ff1598939", "entities": [ { "tableName": "BroadcastNostrEventReceipt", @@ -263,62 +263,35 @@ ] }, { - "tableName": "InReplyToPost", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `inReplyToPostId` TEXT NOT NULL, `inReplyToPostTags` TEXT NOT NULL, `inReplyToPostCreatedAt` INTEGER NOT NULL, `inReplyToPostContent` TEXT NOT NULL, `inReplyToPostAuthorPublicKey` 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(`inReplyToPostAuthorPublicKey`) 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(`inReplyToPostId`) REFERENCES `Post`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "tableName": "InReplyToRelation", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`replyingNostrEventId` TEXT NOT NULL, `inReplyToNostrEventId` TEXT NOT NULL, `inReplyToProfilePublicKey` TEXT, `inReplyToRootNostrEventId` TEXT, `inReplyToRootProfilePublicKey` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`replyingNostrEventId`, `inReplyToNostrEventId`), FOREIGN KEY(`inReplyToProfilePublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`inReplyToRootProfilePublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`replyingNostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`inReplyToNostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`inReplyToRootNostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { - "fieldPath": "id", - "columnName": "id", + "fieldPath": "replyingNostrEventId", + "columnName": "replyingNostrEventId", "affinity": "TEXT", "notNull": true }, { - "fieldPath": "inReplyToPostId", - "columnName": "inReplyToPostId", + "fieldPath": "inReplyToNostrEventId", + "columnName": "inReplyToNostrEventId", "affinity": "TEXT", "notNull": true }, { - "fieldPath": "inReplyToPostTags", - "columnName": "inReplyToPostTags", - "affinity": "TEXT", - "notNull": true + "fieldPath": "inReplyToProfilePublicKey", + "columnName": "inReplyToProfilePublicKey", + "affinity": "TEXT" }, { - "fieldPath": "inReplyToPostCreatedAt", - "columnName": "inReplyToPostCreatedAt", - "affinity": "INTEGER", - "notNull": true + "fieldPath": "inReplyToRootNostrEventId", + "columnName": "inReplyToRootNostrEventId", + "affinity": "TEXT" }, { - "fieldPath": "inReplyToPostContent", - "columnName": "inReplyToPostContent", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "inReplyToPostAuthorPublicKey", - "columnName": "inReplyToPostAuthorPublicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "profilePublicKey", - "columnName": "profilePublicKey", - "affinity": "TEXT", - "notNull": true + "fieldPath": "inReplyToRootProfilePublicKey", + "columnName": "inReplyToRootProfilePublicKey", + "affinity": "TEXT" }, { "fieldPath": "createdAt", @@ -347,55 +320,22 @@ "fieldPath": "deletedAt", "columnName": "deletedAt", "affinity": "INTEGER" - }, - { - "fieldPath": "broadcastedAt", - "columnName": "broadcastedAt", - "affinity": "INTEGER" } ], "primaryKey": { "autoGenerate": false, "columnNames": [ - "id" + "replyingNostrEventId", + "inReplyToNostrEventId" ] }, - "indices": [ - { - "name": "index_InReplyToPost_profilePublicKey", - "unique": false, - "columnNames": [ - "profilePublicKey" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_InReplyToPost_profilePublicKey` ON `${TABLE_NAME}` (`profilePublicKey`)" - }, - { - "name": "index_InReplyToPost_nostrEventId", - "unique": false, - "columnNames": [ - "nostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_InReplyToPost_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)" - }, - { - "name": "index_InReplyToPost_inReplyToPostId", - "unique": false, - "columnNames": [ - "inReplyToPostId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_InReplyToPost_inReplyToPostId` ON `${TABLE_NAME}` (`inReplyToPostId`)" - } - ], "foreignKeys": [ { "table": "Profile", "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "profilePublicKey" + "inReplyToProfilePublicKey" ], "referencedColumns": [ "publicKey" @@ -406,7 +346,7 @@ "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "inReplyToPostAuthorPublicKey" + "inReplyToRootProfilePublicKey" ], "referencedColumns": [ "publicKey" @@ -417,18 +357,29 @@ "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "nostrEventId" + "replyingNostrEventId" ], "referencedColumns": [ "id" ] }, { - "table": "Post", + "table": "NostrEvent", "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "inReplyToPostId" + "inReplyToNostrEventId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "NostrEvent", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "inReplyToRootNostrEventId" ], "referencedColumns": [ "id" @@ -438,11 +389,11 @@ }, { "tableName": "Mention", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`publicKey` TEXT NOT NULL, `relayUrl` TEXT, `nostrEventId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`nostrEventId`, `publicKey`), FOREIGN KEY(`publicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mentionedPublicKey` TEXT NOT NULL, `relayUrl` TEXT, `nostrEventId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`nostrEventId`, `mentionedPublicKey`), FOREIGN KEY(`mentionedPublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { - "fieldPath": "publicKey", - "columnName": "publicKey", + "fieldPath": "mentionedPublicKey", + "columnName": "mentionedPublicKey", "affinity": "TEXT", "notNull": true }, @@ -490,7 +441,7 @@ "autoGenerate": false, "columnNames": [ "nostrEventId", - "publicKey" + "mentionedPublicKey" ] }, "foreignKeys": [ @@ -499,7 +450,7 @@ "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "publicKey" + "mentionedPublicKey" ], "referencedColumns": [ "publicKey" @@ -520,7 +471,7 @@ }, { "tableName": "NostrEvent", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `pubKey` TEXT NOT NULL, `kind` INTEGER NOT NULL, `tags` TEXT NOT NULL, `content` TEXT NOT NULL, `sig` TEXT NOT NULL, `quotedNostrEventId` TEXT, `rootNostrEventId` TEXT, `replyNostrEventId` TEXT, `taggedNostrEventId` TEXT, `taggedNostrEventRelayUrl` TEXT, `unsignedNostrEventId` INTEGER, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`id`))", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `pubKey` TEXT NOT NULL, `kind` INTEGER NOT NULL, `tags` TEXT NOT NULL, `content` TEXT NOT NULL, `sig` TEXT NOT NULL, `quotedNostrEventId` TEXT, `quotedAuthorPublicKey` TEXT, `inReplyToNostrEventId` TEXT, `inReplyToAuthorPublicKey` TEXT, `inReplyToRootNostrEventId` TEXT, `inReplyToRootAuthorPublicKey` TEXT, `repostedNostrEventId` TEXT, `repostedAuthorPublicKey` TEXT, `unsignedNostrEventId` INTEGER, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`id`))", "fields": [ { "fieldPath": "id", @@ -564,23 +515,38 @@ "affinity": "TEXT" }, { - "fieldPath": "rootNostrEventId", - "columnName": "rootNostrEventId", + "fieldPath": "quotedAuthorPublicKey", + "columnName": "quotedAuthorPublicKey", "affinity": "TEXT" }, { - "fieldPath": "replyNostrEventId", - "columnName": "replyNostrEventId", + "fieldPath": "inReplyToNostrEventId", + "columnName": "inReplyToNostrEventId", "affinity": "TEXT" }, { - "fieldPath": "taggedNostrEventId", - "columnName": "taggedNostrEventId", + "fieldPath": "inReplyToAuthorPublicKey", + "columnName": "inReplyToAuthorPublicKey", "affinity": "TEXT" }, { - "fieldPath": "taggedNostrEventRelayUrl", - "columnName": "taggedNostrEventRelayUrl", + "fieldPath": "inReplyToRootNostrEventId", + "columnName": "inReplyToRootNostrEventId", + "affinity": "TEXT" + }, + { + "fieldPath": "inReplyToRootAuthorPublicKey", + "columnName": "inReplyToRootAuthorPublicKey", + "affinity": "TEXT" + }, + { + "fieldPath": "repostedNostrEventId", + "columnName": "repostedNostrEventId", + "affinity": "TEXT" + }, + { + "fieldPath": "repostedAuthorPublicKey", + "columnName": "repostedAuthorPublicKey", "affinity": "TEXT" }, { @@ -652,22 +618,22 @@ "createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEvent_quotedNostrEventId` ON `${TABLE_NAME}` (`quotedNostrEventId`)" }, { - "name": "index_NostrEvent_rootNostrEventId", + "name": "index_NostrEvent_inReplyToNostrEventId", "unique": false, "columnNames": [ - "rootNostrEventId" + "inReplyToNostrEventId" ], "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEvent_rootNostrEventId` ON `${TABLE_NAME}` (`rootNostrEventId`)" + "createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEvent_inReplyToNostrEventId` ON `${TABLE_NAME}` (`inReplyToNostrEventId`)" }, { - "name": "index_NostrEvent_replyNostrEventId", + "name": "index_NostrEvent_inReplyToRootNostrEventId", "unique": false, "columnNames": [ - "replyNostrEventId" + "inReplyToRootNostrEventId" ], "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEvent_replyNostrEventId` ON `${TABLE_NAME}` (`replyNostrEventId`)" + "createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEvent_inReplyToRootNostrEventId` ON `${TABLE_NAME}` (`inReplyToRootNostrEventId`)" } ] }, @@ -986,62 +952,25 @@ ] }, { - "tableName": "QuotedPost", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `quotedPostId` TEXT NOT NULL, `quotedPostTags` TEXT NOT NULL, `quotedPostCreatedAt` INTEGER NOT NULL, `quotedPostContent` TEXT NOT NULL, `quotedPostAuthorPublicKey` 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(`quotedPostAuthorPublicKey`) 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(`quotedPostId`) REFERENCES `Post`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "tableName": "QuotedRelation", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`quotingNostrEventId` TEXT NOT NULL, `quotedNostrEventId` TEXT NOT NULL, `quotedProfilePublicKey` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`quotingNostrEventId`, `quotedNostrEventId`), FOREIGN KEY(`quotedProfilePublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`quotingNostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`quotedNostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { - "fieldPath": "id", - "columnName": "id", + "fieldPath": "quotingNostrEventId", + "columnName": "quotingNostrEventId", "affinity": "TEXT", "notNull": true }, { - "fieldPath": "quotedPostId", - "columnName": "quotedPostId", + "fieldPath": "quotedNostrEventId", + "columnName": "quotedNostrEventId", "affinity": "TEXT", "notNull": true }, { - "fieldPath": "quotedPostTags", - "columnName": "quotedPostTags", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "quotedPostCreatedAt", - "columnName": "quotedPostCreatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "quotedPostContent", - "columnName": "quotedPostContent", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "quotedPostAuthorPublicKey", - "columnName": "quotedPostAuthorPublicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "profilePublicKey", - "columnName": "profilePublicKey", - "affinity": "TEXT", - "notNull": true + "fieldPath": "quotedProfilePublicKey", + "columnName": "quotedProfilePublicKey", + "affinity": "TEXT" }, { "fieldPath": "createdAt", @@ -1080,56 +1009,17 @@ "primaryKey": { "autoGenerate": false, "columnNames": [ - "id" + "quotingNostrEventId", + "quotedNostrEventId" ] }, - "indices": [ - { - "name": "index_QuotedPost_profilePublicKey", - "unique": false, - "columnNames": [ - "profilePublicKey" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_QuotedPost_profilePublicKey` ON `${TABLE_NAME}` (`profilePublicKey`)" - }, - { - "name": "index_QuotedPost_nostrEventId", - "unique": false, - "columnNames": [ - "nostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_QuotedPost_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)" - }, - { - "name": "index_QuotedPost_quotedPostId", - "unique": false, - "columnNames": [ - "quotedPostId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_QuotedPost_quotedPostId` ON `${TABLE_NAME}` (`quotedPostId`)" - } - ], "foreignKeys": [ { "table": "Profile", "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "profilePublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - }, - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "quotedPostAuthorPublicKey" + "quotedProfilePublicKey" ], "referencedColumns": [ "publicKey" @@ -1140,18 +1030,18 @@ "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "nostrEventId" + "quotingNostrEventId" ], "referencedColumns": [ "id" ] }, { - "table": "Post", + "table": "NostrEvent", "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "quotedPostId" + "quotedNostrEventId" ], "referencedColumns": [ "id" @@ -1378,62 +1268,25 @@ } }, { - "tableName": "Repost", - "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 )", + "tableName": "RepostedRelation", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`repostingNostrEventId` TEXT NOT NULL, `repostedNostrEventId` TEXT NOT NULL, `repostedProfilePublicKey` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`repostingNostrEventId`, `repostedNostrEventId`), FOREIGN KEY(`repostedProfilePublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`repostedNostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`repostingNostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { - "fieldPath": "id", - "columnName": "id", + "fieldPath": "repostingNostrEventId", + "columnName": "repostingNostrEventId", "affinity": "TEXT", "notNull": true }, { - "fieldPath": "repostedPostId", - "columnName": "repostedPostId", + "fieldPath": "repostedNostrEventId", + "columnName": "repostedNostrEventId", "affinity": "TEXT", "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", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "profilePublicKey", - "columnName": "profilePublicKey", - "affinity": "TEXT", - "notNull": true + "fieldPath": "repostedProfilePublicKey", + "columnName": "repostedProfilePublicKey", + "affinity": "TEXT" }, { "fieldPath": "createdAt", @@ -1472,56 +1325,17 @@ "primaryKey": { "autoGenerate": false, "columnNames": [ - "id" + "repostingNostrEventId", + "repostedNostrEventId" ] }, - "indices": [ - { - "name": "index_Repost_profilePublicKey", - "unique": false, - "columnNames": [ - "profilePublicKey" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Repost_profilePublicKey` ON `${TABLE_NAME}` (`profilePublicKey`)" - }, - { - "name": "index_Repost_nostrEventId", - "unique": false, - "columnNames": [ - "nostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Repost_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)" - }, - { - "name": "index_Repost_repostedPostId", - "unique": false, - "columnNames": [ - "repostedPostId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Repost_repostedPostId` ON `${TABLE_NAME}` (`repostedPostId`)" - } - ], "foreignKeys": [ { "table": "Profile", "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "profilePublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - }, - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "repostedPostAuthorPublicKey" + "repostedProfilePublicKey" ], "referencedColumns": [ "publicKey" @@ -1532,18 +1346,18 @@ "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "nostrEventId" + "repostedNostrEventId" ], "referencedColumns": [ "id" ] }, { - "table": "Post", + "table": "NostrEvent", "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "repostedPostId" + "repostingNostrEventId" ], "referencedColumns": [ "id" @@ -2004,7 +1818,7 @@ ], "setupQueries": [ "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, '98aa54dd7fe776faca364638070a3871')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '296c3251f1f835c144c94c7ff1598939')" ] } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/AuxDatabase.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/AuxDatabase.kt index f0912b26..18f043c5 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/AuxDatabase.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/AuxDatabase.kt @@ -4,17 +4,17 @@ import ac.cord.auxiliary.compose.database.converters.AuxConverters import ac.cord.auxiliary.compose.database.dao.BroadcastNostrEventReceiptDao import ac.cord.auxiliary.compose.database.dao.BroadcastNostrEventRequestDao import ac.cord.auxiliary.compose.database.dao.ConnectionDao -import ac.cord.auxiliary.compose.database.dao.InReplyToPostDao +import ac.cord.auxiliary.compose.database.dao.InReplyToRelationDao import ac.cord.auxiliary.compose.database.dao.MentionDao import ac.cord.auxiliary.compose.database.dao.NostrDao import ac.cord.auxiliary.compose.database.dao.NostrEventDao import ac.cord.auxiliary.compose.database.dao.PostDao import ac.cord.auxiliary.compose.database.dao.ProfileDao -import ac.cord.auxiliary.compose.database.dao.QuotedPostDao +import ac.cord.auxiliary.compose.database.dao.QuotedRelationDao import ac.cord.auxiliary.compose.database.dao.ReactionDao import ac.cord.auxiliary.compose.database.dao.RecentSearchDao import ac.cord.auxiliary.compose.database.dao.RelayDao -import ac.cord.auxiliary.compose.database.dao.RepostDao +import ac.cord.auxiliary.compose.database.dao.RepostedRelationDao import ac.cord.auxiliary.compose.database.dao.SynchronizeNostrEventRequestDao import ac.cord.auxiliary.compose.database.dao.SynchronizeNostrEventResultDao import ac.cord.auxiliary.compose.database.dao.UnsignedNostrEventDao @@ -22,16 +22,16 @@ import ac.cord.auxiliary.compose.database.dao.ZapDao import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventReceipt import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventRequest import ac.cord.auxiliary.compose.database.model.Connection -import ac.cord.auxiliary.compose.database.model.InReplyToPost +import ac.cord.auxiliary.compose.database.model.InReplyToRelation import ac.cord.auxiliary.compose.database.model.Mention import ac.cord.auxiliary.compose.database.model.NostrEvent import ac.cord.auxiliary.compose.database.model.Post import ac.cord.auxiliary.compose.database.model.Profile -import ac.cord.auxiliary.compose.database.model.QuotedPost +import ac.cord.auxiliary.compose.database.model.QuotedRelation import ac.cord.auxiliary.compose.database.model.Reaction import ac.cord.auxiliary.compose.database.model.RecentSearch import ac.cord.auxiliary.compose.database.model.Relay -import ac.cord.auxiliary.compose.database.model.Repost +import ac.cord.auxiliary.compose.database.model.RepostedRelation import ac.cord.auxiliary.compose.database.model.SynchronizeNostrEventRequest import ac.cord.auxiliary.compose.database.model.SynchronizeNostrEventResult import ac.cord.auxiliary.compose.database.model.UnsignedNostrEvent @@ -50,16 +50,16 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L) BroadcastNostrEventReceipt::class, BroadcastNostrEventRequest::class, Connection::class, - InReplyToPost::class, + InReplyToRelation::class, Mention::class, NostrEvent::class, Post::class, Profile::class, - QuotedPost::class, + QuotedRelation::class, Reaction::class, RecentSearch::class, Relay::class, - Repost::class, + RepostedRelation::class, SynchronizeNostrEventRequest::class, SynchronizeNostrEventResult::class, UnsignedNostrEvent::class, @@ -73,7 +73,7 @@ abstract class AuxDatabase: RoomDatabase() { abstract fun broadcastNostrEventRequestDao(): BroadcastNostrEventRequestDao abstract fun connectionDao(): ConnectionDao - abstract fun inReplyToPostDao(): InReplyToPostDao + abstract fun inReplyToRelationDao(): InReplyToRelationDao abstract fun mentionDao(): MentionDao @@ -83,7 +83,7 @@ abstract class AuxDatabase: RoomDatabase() { abstract fun postDao(): PostDao abstract fun profileDao(): ProfileDao - abstract fun quotedPostDao(): QuotedPostDao + abstract fun quotedRelationDao(): QuotedRelationDao abstract fun reactionDao(): ReactionDao @@ -91,7 +91,7 @@ abstract class AuxDatabase: RoomDatabase() { abstract fun relayDao(): RelayDao - abstract fun repostDao(): RepostDao + abstract fun repostedRelationDao(): RepostedRelationDao abstract fun synchronizeNostrEventRequestDao(): SynchronizeNostrEventRequestDao diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/InReplyToPostDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/InReplyToPostDao.kt deleted file mode 100644 index b3357cbc..00000000 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/InReplyToPostDao.kt +++ /dev/null @@ -1,18 +0,0 @@ -package ac.cord.auxiliary.compose.database.dao - -import ac.cord.auxiliary.compose.database.model.InReplyToPost -import ac.cord.auxiliary.compose.database.model.Post -import ac.cord.auxiliary.compose.database.model.intermdiate.LocalPost -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.Query -import androidx.room.Upsert - -@Dao -interface InReplyToPostDao { - @Insert - suspend fun insert(inReplyToPost: InReplyToPost) - - @Upsert - suspend fun upsert(inReplyToPost: InReplyToPost) -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/InReplyToRelationDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/InReplyToRelationDao.kt new file mode 100644 index 00000000..d95909b3 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/InReplyToRelationDao.kt @@ -0,0 +1,22 @@ +package ac.cord.auxiliary.compose.database.dao + +import ac.cord.auxiliary.compose.database.model.InReplyToRelation +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Upsert + +@Dao +interface InReplyToRelationDao { + @Query("SELECT * FROM InReplyToRelation WHERE replyingNostrEventId = :replyingNostrEventId") + fun getRepostByReplyingNostrEventId(replyingNostrEventId: String): InReplyToRelation? + + @Query("SELECT * FROM InReplyToRelation WHERE inReplyToNostrEventId = :inReplyToNostrEventId") + fun getRepostByInReplyToNostrEventId(inReplyToNostrEventId: String): InReplyToRelation? + + @Insert + suspend fun insert(inReplyToRelation: InReplyToRelation) + + @Upsert + suspend fun upsert(inReplyToRelation: InReplyToRelation) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrDao.kt index 439f3980..0b2c52ae 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrDao.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrDao.kt @@ -18,12 +18,9 @@ import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent -import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent -import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListEvent -import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent -import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent -import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent -import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent +import com.vitorpamplona.quartz.nip18Reposts.quotes.QAddressableTag +import com.vitorpamplona.quartz.nip18Reposts.quotes.QEventTag +import com.vitorpamplona.quartz.nip18Reposts.quotes.firstTaggedQuote import kotlin.time.Clock @Dao @@ -187,41 +184,141 @@ abstract class NostrDao( } // Sync tagged events and authors... +// nostrEvent.tags.firstTaggedQuote()?.let { taggedQuote -> +// when (taggedQuote) { +// is QEventTag -> { +// Triple( +// taggedQuote.eventId, +// taggedQuote.author, +// taggedQuote.relay, +// ) +// } +// is QAddressableTag -> { +// Triple( +// taggedQuote.address.dTag, +// taggedQuote.address.pubKeyHex, +// taggedQuote.relay, +// ) +// } +// else -> null +// }?.let { quotedEventData -> +// val quotedNostrEvent = database.nostrEventDao().getNostrEventById(quotedEventData.first) +// +// if (quotedNostrEvent == null) { +//// val quotedPost = database.postDao().getPostById(quotedEventData.first) +// +//// if (quotedPost == null) { +//// // Persist PlaceHolder and sync +//// database.postDao().insert( +//// Post( +//// id = quotedEventData.first, +//// createdAt = GENESIS_AT, +//// nostrEventId = nostrEvent.id, // Will get overwriting by sync handled by tagged events... +//// content = "sync required", +//// profilePublicKey = nostrEvent.pubKey // Will get overwriting by sync handled by tagged events... +//// ) +//// ) +//// } +//// +//// quotedEventData.second?.let { authorPubkey -> +//// val repostedPostProfile = database.profileDao().getProfileByPublicKey(authorPubkey) +//// +//// if (repostedPostProfile == null) { +//// // Persist PlaceHolder and sync +//// database.profileDao().insertPlaceholderProfile( +//// Profile( +//// publicKey = authorPubkey, +//// createdAt = GENESIS_AT, +//// nostrEventId = nostrEvent.id, // Will get overwriting by sync handled by tagged events... +//// ) +//// ) +//// profilePublicKeysToSync.add( +//// authorPubkey +//// ) +//// } else if (repostedPostProfile.createdAt == GENESIS_AT && level == 0) { +//// logger.i("${level} This is a placeholder profile... that might need to get synced...: $repostedPostProfile") +//// profilePublicKeysToSync.add(authorPubkey) +//// } +//// } +//// +//// database.quotedPostDao() +// +// val recommendRelayUrl = quotedEventData.third?.url +// +// val synchronizeNostrEventRequests = if (recommendRelayUrl != null) { +// eventIdsToSync.add(quotedEventData.first) +// +// listOf( +// SynchronizeNostrEventRequest( +// purpose = "synchronization", +// synchronizationFilters = arrayOf( +// SynchronizationFilter( +// ids = arrayOf(quotedEventData.first) +// ) +// ), +// relayURL = recommendRelayUrl, +// isRecommendedRelay = true, +// level = level + 1 +// ) +// ) +// } else { +// eventIdsToSync.add(quotedEventData.first) +// synchronizationRelayURLs.map { synchronizationRelayURL -> +// SynchronizeNostrEventRequest( +// purpose = "synchronization", +// synchronizationFilters = arrayOf( +// SynchronizationFilter( +// ids = arrayOf(quotedEventData.first) +// ) +// ), +// relayURL = synchronizationRelayURL, +// level = level + 1 +// ) +// } +// } +// +// database.synchronizeNostrEventRequestDao().insert( +// synchronizeNostrEventRequests +// ) +// } +// } +// } + nostrEvent.tags.taggedEvents().take(4).forEach { taggedEvent -> // We only look at the first 3 tagged events... val taggedNostrEvent = database.nostrEventDao().getNostrEventById(taggedEvent.eventId) if (taggedNostrEvent == null) { val recommendRelayUrl = taggedEvent.relay?.url val synchronizeNostrEventRequests = if (recommendRelayUrl != null) { - eventIdsToSync.add(taggedEvent.eventId) - listOf( - SynchronizeNostrEventRequest( - purpose = "synchronization", - synchronizationFilters = arrayOf( - SynchronizationFilter( - ids = arrayOf(taggedEvent.eventId) - ) - ), - relayURL = recommendRelayUrl, - isRecommendedRelay = true, - level = level + 1 - ) + eventIdsToSync.add(taggedEvent.eventId) + listOf( + SynchronizeNostrEventRequest( + purpose = "synchronization", + synchronizationFilters = arrayOf( + SynchronizationFilter( + ids = arrayOf(taggedEvent.eventId) + ) + ), + relayURL = recommendRelayUrl, + isRecommendedRelay = true, + level = level + 1 + ) + ) + } else { + eventIdsToSync.add(taggedEvent.eventId) + synchronizationRelayURLs.map { synchronizationRelayURL -> + SynchronizeNostrEventRequest( + purpose = "synchronization", + synchronizationFilters = arrayOf( + SynchronizationFilter( + ids = arrayOf(taggedEvent.eventId) + ) + ), + relayURL = synchronizationRelayURL, + level = level + 1 ) - } else { - eventIdsToSync.add(taggedEvent.eventId) - synchronizationRelayURLs.map { synchronizationRelayURL -> - SynchronizeNostrEventRequest( - purpose = "synchronization", - synchronizationFilters = arrayOf( - SynchronizationFilter( - ids = arrayOf(taggedEvent.eventId) - ) - ), - relayURL = synchronizationRelayURL, - level = level + 1 - ) - } } + } database.synchronizeNostrEventRequestDao().insert( synchronizeNostrEventRequests @@ -232,6 +329,19 @@ abstract class NostrDao( nostrEvent.tags.taggedUsers().take(5).forEach { taggedUser -> // 5 tagged users... val recommendedRelay = taggedUser.relayHint?.url + val taggedProfile = database.profileDao().getProfileByPublicKey(taggedUser.pubKey) + + if (taggedProfile == null) { + // Save a placeholder + database.profileDao().insertPlaceholderProfile( + Profile( + publicKey = taggedUser.pubKey, + createdAt = GENESIS_AT, + nostrEventId = nostrEvent.id, // Will get overwriting by sync, + ) + ) + } + val synchronizeNostrEventRequests = if (recommendedRelay != null) { // listOf( // SynchronizeNostrEventRequest( @@ -327,60 +437,144 @@ abstract class NostrDao( database.reactionDao().upsert(reaction) } - nostrEvent.toRepost()?.let { repost -> - // Find or create placeHolder note that is being reposted - val repostedPost = database.postDao().getPostById(repost.repostedPostId) + nostrEvent.toQuotedRelation()?.let { quotedRelation -> + val quotedNostrEvent = database.nostrEventDao().getNostrEventById(quotedRelation.quotedNostrEventId) - if (repostedPost == null) { - // Persist PlaceHolder and sync - database.postDao().insert( - Post( - id = repost.repostedPostId, + val quotedProfilePublicKey = if (quotedNostrEvent == null) { + eventIdsToSync.add(quotedRelation.quotedNostrEventId) + + database.nostrEventDao().insert( + NostrEvent( + id = quotedRelation.quotedNostrEventId, + content = "Pending Sync", createdAt = GENESIS_AT, - nostrEventId = nostrEvent.id, // Will get overwriting by sync handled by tagged events... - content = repost.repostedPostId, - profilePublicKey = nostrEvent.pubKey // Will get overwriting by sync handled by tagged events... + pubKey = nostrEvent.pubKey, + sig = "", + kind = nostrEvent.kind, + tags = emptyArray() ) ) + + quotedRelation.quotedProfilePublicKey + } else { + quotedNostrEvent.pubKey } - val repostedPostProfile = database.profileDao().getProfileByPublicKey(repost.repostedPostAuthorPublicKey) + val quotedProfile = quotedProfilePublicKey?.let { database.profileDao().getProfileByPublicKey(it) } - if (repostedPostProfile == null) { - // Persist PlaceHolder and sync + if (quotedProfilePublicKey != null && quotedProfile == null) { database.profileDao().insertPlaceholderProfile( Profile( - publicKey = repost.repostedPostAuthorPublicKey, + publicKey = quotedProfilePublicKey, createdAt = GENESIS_AT, - nostrEventId = nostrEvent.id, // Will get overwriting by sync handled by tagged events... + nostrEventId = nostrEvent.id, // Will get overwriting by sync, + ) + ) + } + + // TODO: Might need to store placeholders + database.quotedRelationDao().upsert( + quotedRelation.copy( + quotedProfilePublicKey = quotedProfilePublicKey + ) + ) + } + nostrEvent.toRepostedRelation()?.let { repostedRelation -> + // Find or create placeHolder note that is being reposted + val repostedNostrEvent = database.nostrEventDao().getNostrEventById(repostedRelation.repostedNostrEventId) + + val repostedProfilePublicKey = if (repostedNostrEvent == null) { + // Sync event... + eventIdsToSync.add(repostedRelation.repostedNostrEventId) + + database.nostrEventDao().insert( + NostrEvent( + id = repostedRelation.repostedNostrEventId, + content = "Pending Sync", + createdAt = GENESIS_AT, + pubKey = nostrEvent.pubKey, + sig = "", + kind = nostrEvent.kind, + tags = emptyArray() ) ) - // Request a sync for the post being replied to -// database.synchronizeNostrEventRequestDao().insert( -// synchronizationRelayURLs.map { synchronizationRelayURL -> -// SynchronizeNostrEventRequest( -// purpose = "synchronization", -// synchronizationFilters = arrayOf( -// -// ), -// relayURL = synchronizationRelayURL -// ) -// } -// ) -// synchronizationFilters.add( -// SynchronizationFilter( -// authors = arrayOf(repost.repostedPostAuthorPublicKey), -// kinds = profileEventKinds -// ) -// ) - profilePublicKeysToSync.add( - repost.repostedPostAuthorPublicKey - ) - } else if (repostedPostProfile.createdAt == GENESIS_AT && level == 0) { - logger.i("${level} This is a placeholder profile... that might need to get synced...: $repostedPostProfile") - profilePublicKeysToSync.add(repost.repostedPostAuthorPublicKey) + repostedRelation.repostedProfilePublicKey + } else { + repostedNostrEvent.pubKey } + + val repostedProfile = repostedProfilePublicKey?.let { database.profileDao().getProfileByPublicKey(it) } + + if (repostedProfilePublicKey != null && repostedProfile == null) { + database.profileDao().insertPlaceholderProfile( + Profile( + publicKey = repostedProfilePublicKey, + createdAt = GENESIS_AT, + nostrEventId = nostrEvent.id, // Will get overwriting by sync, + ) + ) + } + database.repostedRelationDao().upsert( + repostedRelation.copy( + repostedProfilePublicKey = repostedProfilePublicKey + ) + ) + } + + nostrEvent.toInReplyToRelation()?.let { inReplyToRelation -> + + val inReplyToNostrEvent = database.nostrEventDao().getNostrEventById(inReplyToRelation.inReplyToNostrEventId) + + val inReplyToProfilePublicKey = if (inReplyToNostrEvent == null) { + eventIdsToSync.add(inReplyToRelation.inReplyToNostrEventId) + + database.nostrEventDao().insert( + NostrEvent( + id = inReplyToRelation.inReplyToNostrEventId, + content = "Pending Sync", + createdAt = GENESIS_AT, + pubKey = nostrEvent.pubKey, + sig = "", + kind = nostrEvent.kind, + tags = emptyArray() + ) + ) + + inReplyToRelation.inReplyToProfilePublicKey + } else { + inReplyToNostrEvent.pubKey + } + + val inReplyToRootNostrEvent = inReplyToRelation.inReplyToRootNostrEventId?.let { database.nostrEventDao().getNostrEventById(it) } + + val inReplyToRootProfilePublicKey = if (inReplyToRootNostrEvent == null) { + eventIdsToSync.add(inReplyToRelation.inReplyToNostrEventId) + + inReplyToRelation.inReplyToRootProfilePublicKey + // Might need to save place holder... + } else { + inReplyToRootNostrEvent.pubKey + } + + val inReplyToProfile = inReplyToRootProfilePublicKey?.let { database.profileDao().getProfileByPublicKey(it) } + + if (inReplyToRootProfilePublicKey != null && inReplyToProfile == null) { + database.profileDao().insertPlaceholderProfile( + Profile( + publicKey = inReplyToRootProfilePublicKey, + createdAt = GENESIS_AT, + nostrEventId = nostrEvent.id, // Will get overwriting by sync, + ) + ) + } + + database.inReplyToRelationDao().upsert( + inReplyToRelation.copy( + inReplyToRootProfilePublicKey = inReplyToRootProfilePublicKey, + inReplyToProfilePublicKey = inReplyToProfilePublicKey + ) + ) } nostrEvent.toZap()?.let { zap -> diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/QuotedPostDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/QuotedPostDao.kt deleted file mode 100644 index 73bcff52..00000000 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/QuotedPostDao.kt +++ /dev/null @@ -1,18 +0,0 @@ -package ac.cord.auxiliary.compose.database.dao - -import ac.cord.auxiliary.compose.database.model.Post -import ac.cord.auxiliary.compose.database.model.QuotedPost -import ac.cord.auxiliary.compose.database.model.intermdiate.LocalPost -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.Query -import androidx.room.Upsert - -@Dao -interface QuotedPostDao { - @Insert - suspend fun insert(quotedPost: QuotedPost) - - @Upsert - suspend fun upsert(quotedPost: QuotedPost) -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/QuotedRelationDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/QuotedRelationDao.kt new file mode 100644 index 00000000..4c28d9cc --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/QuotedRelationDao.kt @@ -0,0 +1,22 @@ +package ac.cord.auxiliary.compose.database.dao + +import ac.cord.auxiliary.compose.database.model.QuotedRelation +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Upsert + +@Dao +interface QuotedRelationDao { + @Query("SELECT * FROM QuotedRelation WHERE quotingNostrEventId = :quotingNostrEventId") + fun getRepostByQuotingNostrEventId(quotingNostrEventId: String): QuotedRelation? + + @Query("SELECT * FROM QuotedRelation WHERE quotedNostrEventId = :quotedNostrEventId") + fun getRepostByQuotedNostrEventId(quotedNostrEventId: String): QuotedRelation? + + @Insert + suspend fun insert(quotedRelation: QuotedRelation) + + @Upsert + suspend fun upsert(quotedRelation: QuotedRelation) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/RepostDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/RepostDao.kt deleted file mode 100644 index 9651e693..00000000 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/RepostDao.kt +++ /dev/null @@ -1,19 +0,0 @@ -package ac.cord.auxiliary.compose.database.dao - -import ac.cord.auxiliary.compose.database.model.Repost -import androidx.room.Dao -import androidx.room.Query -import androidx.room.Upsert -import kotlinx.coroutines.flow.Flow - -@Dao -interface RepostDao { - @Query("SELECT * FROM Repost") - fun getAllReposts(): List - - @Query("SELECT * FROM Repost WHERE id = :id") - fun getRepostById(id: String): Flow - - @Upsert - suspend fun upsert(repost: Repost) -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/RepostedRelationDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/RepostedRelationDao.kt new file mode 100644 index 00000000..b040f58d --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/RepostedRelationDao.kt @@ -0,0 +1,21 @@ +package ac.cord.auxiliary.compose.database.dao + +import ac.cord.auxiliary.compose.database.model.RepostedRelation +import androidx.room.Dao +import androidx.room.Query +import androidx.room.Upsert + +@Dao +interface RepostedRelationDao { + @Query("SELECT * FROM RepostedRelation") + fun getAllReposts(): List + + @Query("SELECT * FROM RepostedRelation WHERE repostingNostrEventId = :repostingNostrEventId") + fun getRepostByRepostingId(repostingNostrEventId: String): RepostedRelation? + + @Query("SELECT * FROM RepostedRelation WHERE repostedNostrEventId = :repostedNostrEventId") + fun getRepostByRepostedId(repostedNostrEventId: String): RepostedRelation? + + @Upsert + suspend fun upsert(repostedRelation: RepostedRelation) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/InReplyToPost.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/InReplyToPost.kt deleted file mode 100644 index 9936cb2d..00000000 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/InReplyToPost.kt +++ /dev/null @@ -1,120 +0,0 @@ -package ac.cord.auxiliary.compose.database.model - -import ac.cord.auxiliary.compose.database.model.traits.BroadcastableEntity -import ac.cord.auxiliary.compose.database.model.traits.LocalStoreEntity -import ac.cord.auxiliary.compose.database.model.traits.NostrEventEntity -import ac.cord.auxiliary.compose.database.model.traits.ProfilePublicKeyEntity -import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity -import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity -import ac.cord.auxiliary.compose.database.model.traits.UserViewableEntity -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.TagArray -import kotlin.time.Clock -import kotlin.time.Instant - -@Entity( - foreignKeys = [ - ForeignKey( - entity = Profile::class, - parentColumns = ["publicKey"], - childColumns = ["profilePublicKey"], - onDelete = ForeignKey.CASCADE, - ), - ForeignKey( - entity = Profile::class, - parentColumns = ["publicKey"], - childColumns = ["inReplyToPostAuthorPublicKey"], - 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 = ["inReplyToPostId"], - onDelete = ForeignKey.CASCADE, - ), - ], - indices = [ - Index("profilePublicKey"), - Index("nostrEventId"), - Index("inReplyToPostId") - ], -) -data class InReplyToPost( - @PrimaryKey - val id: HexKey, - - val inReplyToPostId: HexKey, - val inReplyToPostTags: TagArray, - val inReplyToPostCreatedAt: Instant, - val inReplyToPostContent: String, - val inReplyToPostAuthorPublicKey: HexKey, - - val content: String, - - override val nostrEventId: HexKey, // Might be a replaceable nostr event? - override val profilePublicKey: HexKey, - - override val createdAt: Instant = Clock.System.now(), - override val updatedAt: Instant = Clock.System.now(), - override val savedAt: Instant = Clock.System.now(), - override val viewedAt: Instant? = null, - override val deletedAt: Instant? = null, - override val broadcastedAt: Instant? = null, -): 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 InReplyToPost - - if (id != other.id) return false - if (inReplyToPostId != other.inReplyToPostId) return false - if (!inReplyToPostTags.contentDeepEquals(other.inReplyToPostTags)) return false - if (inReplyToPostCreatedAt != other.inReplyToPostCreatedAt) return false - if (inReplyToPostContent != other.inReplyToPostContent) return false - if (inReplyToPostAuthorPublicKey != other.inReplyToPostAuthorPublicKey) 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 + inReplyToPostId.hashCode() - result = 31 * result + inReplyToPostTags.contentDeepHashCode() - result = 31 * result + inReplyToPostCreatedAt.hashCode() - result = 31 * result + inReplyToPostContent.hashCode() - result = 31 * result + inReplyToPostAuthorPublicKey.hashCode() - 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 - } - -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/InReplyToRelation.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/InReplyToRelation.kt new file mode 100644 index 00000000..56335213 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/InReplyToRelation.kt @@ -0,0 +1,68 @@ +package ac.cord.auxiliary.compose.database.model + +import ac.cord.auxiliary.compose.database.model.traits.BroadcastableEntity +import ac.cord.auxiliary.compose.database.model.traits.LocalStoreEntity +import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity +import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity +import ac.cord.auxiliary.compose.database.model.traits.UserViewableEntity +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.Clock +import kotlin.time.Instant + +@Entity( + foreignKeys = [ + ForeignKey( + entity = Profile::class, + parentColumns = ["publicKey"], + childColumns = ["inReplyToProfilePublicKey"], + onDelete = ForeignKey.CASCADE, + ), + ForeignKey( + entity = Profile::class, + parentColumns = ["publicKey"], + childColumns = ["inReplyToRootProfilePublicKey"], + 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 = ["replyingNostrEventId"], + onDelete = ForeignKey.CASCADE, + ), + ForeignKey( + entity = NostrEvent::class, + parentColumns = ["id"], + childColumns = ["inReplyToNostrEventId"], + onDelete = ForeignKey.CASCADE, + ), + ForeignKey( + entity = NostrEvent::class, + parentColumns = ["id"], + childColumns = ["inReplyToRootNostrEventId"], + onDelete = ForeignKey.CASCADE, + ), + ], + primaryKeys = ["replyingNostrEventId", "inReplyToNostrEventId"] +) +data class InReplyToRelation( + val replyingNostrEventId: HexKey, // Might be a replaceable nostr event? + + val inReplyToNostrEventId: HexKey, + val inReplyToProfilePublicKey: HexKey?, + + val inReplyToRootNostrEventId: HexKey?, + val inReplyToRootProfilePublicKey: HexKey?, + + override val createdAt: Instant = Clock.System.now(), + override val updatedAt: Instant = Clock.System.now(), + override val savedAt: Instant = Clock.System.now(), + override val viewedAt: Instant? = null, + override val deletedAt: Instant? = null, +): TimestampedEntity, LocalStoreEntity, UserViewableEntity, SoftDeletableEntity { + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/Mention.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/Mention.kt index 161fbb4b..c7598140 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/Mention.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/Mention.kt @@ -16,7 +16,7 @@ import kotlin.time.Instant ForeignKey( entity = Profile::class, parentColumns = ["publicKey"], - childColumns = ["publicKey"], + childColumns = ["mentionedPublicKey"], onDelete = ForeignKey.CASCADE, ), ForeignKey( @@ -26,10 +26,10 @@ import kotlin.time.Instant onDelete = ForeignKey.CASCADE, ), ], - primaryKeys = ["nostrEventId", "publicKey"] + primaryKeys = ["nostrEventId", "mentionedPublicKey"] ) data class Mention( - val publicKey: HexKey, + val mentionedPublicKey: HexKey, val relayUrl: String? = null, diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/NostrEvent.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/NostrEvent.kt index 7183c3f1..ae45f077 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/NostrEvent.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/NostrEvent.kt @@ -5,6 +5,7 @@ import ac.cord.auxiliary.compose.database.model.traits.LocalStoreEntity import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity import ac.cord.auxiliary.compose.database.model.traits.UnsignedNostrEventEntity +import ac.cord.auxiliary.compose.database.model.types.SynchronizationFilter import androidx.room.Entity import androidx.room.Ignore import androidx.room.Index @@ -16,9 +17,12 @@ import com.vitorpamplona.quartz.nip01Core.core.Kind import com.vitorpamplona.quartz.nip01Core.core.TagArray import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent import com.vitorpamplona.quartz.nip01Core.tags.events.firstTaggedEvent -import com.vitorpamplona.quartz.nip01Core.tags.people.firstTaggedUser +import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip18Reposts.RepostEvent +import com.vitorpamplona.quartz.nip18Reposts.quotes.QAddressableTag +import com.vitorpamplona.quartz.nip18Reposts.quotes.QEventTag +import com.vitorpamplona.quartz.nip18Reposts.quotes.firstTaggedQuote import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.utils.EventFactory @@ -37,8 +41,8 @@ import kotlin.time.Instant Index("kind"), Index("pubKey"), Index("quotedNostrEventId"), - Index("rootNostrEventId"), - Index("replyNostrEventId"), + Index("inReplyToNostrEventId"), + Index("inReplyToRootNostrEventId"), ] ) data class NostrEvent( @@ -51,12 +55,16 @@ data class NostrEvent( val sig: HexKey, val quotedNostrEventId: HexKey? = null, + val quotedAuthorPublicKey: HexKey? = null, - val rootNostrEventId: HexKey? = null, - val replyNostrEventId: HexKey? = null, + val inReplyToNostrEventId: HexKey? = null, + val inReplyToAuthorPublicKey: HexKey? = null, - val taggedNostrEventId: HexKey? = null, - val taggedNostrEventRelayUrl: String? = null, + val inReplyToRootNostrEventId: HexKey? = null, + val inReplyToRootAuthorPublicKey: HexKey? = null, + + val repostedNostrEventId: HexKey? = null, + val repostedAuthorPublicKey: HexKey? = null, override val unsignedNostrEventId: Long? = null, @@ -202,7 +210,7 @@ data class NostrEvent( return null } - fun toRepost(): Repost? = try { + fun toRepostedRelation(): RepostedRelation? = try { if (kind == RepostEvent.KIND) { val repostEvent = EventFactory.create( id = id, @@ -215,17 +223,11 @@ data class NostrEvent( ) return repostEvent.containedPost()?.let { containedPost -> - Repost( - id = repostEvent.id, - repostedPostId = containedPost.id, - repostedPostTags = containedPost.tags, - repostedPostContent = containedPost.content, - repostedPostCreatedAt = Instant.fromEpochSeconds(containedPost.createdAt), - content = containedPost.content, + RepostedRelation( + repostedNostrEventId = containedPost.id, + repostedProfilePublicKey = containedPost.pubKey, - nostrEventId = id, - profilePublicKey = repostEvent.pubKey, - repostedPostAuthorPublicKey = containedPost.pubKey, + repostingNostrEventId = repostEvent.id, createdAt = Instant.fromEpochSeconds(repostEvent.createdAt), updatedAt = Instant.fromEpochSeconds(repostEvent.createdAt), @@ -240,6 +242,80 @@ data class NostrEvent( return null } + fun toQuotedRelation(): QuotedRelation? = try { + if (kind == TextNoteEvent.KIND) { + tags.firstTaggedQuote()?.let { taggedQuote -> + when (taggedQuote) { + is QEventTag -> { + Triple( + taggedQuote.eventId, + taggedQuote.author, + taggedQuote.relay, + ) + } + is QAddressableTag -> { + Triple( + taggedQuote.address.dTag, + taggedQuote.address.pubKeyHex, + taggedQuote.relay, + ) + } + else -> null + }?.let { quotedEventData -> + + return QuotedRelation( + quotedNostrEventId = quotedEventData.first, + quotedProfilePublicKey = quotedEventData.second, + + quotingNostrEventId = id, + + createdAt = createdAt + ) + } + } + } + + // TODO: Get quoted event from content... + + return null + } catch (e: Throwable) { + logger.e("Failed to return Repost: ", e) + return null + } + + fun toInReplyToRelation(): InReplyToRelation? = try { + if (kind == RepostEvent.KIND) { + val textNoteEvent = EventFactory.create( + id = id, + pubKey = pubKey, + createdAt = createdAt.epochSeconds, + kind = kind, + tags = tags, + content = content, + sig = sig + ) + + + val rootReply = textNoteEvent.markedRoot() + textNoteEvent.markedReply()?.let { replyTag -> + + return InReplyToRelation( + inReplyToNostrEventId = replyTag.eventId, + inReplyToProfilePublicKey = replyTag.author, + + inReplyToRootNostrEventId = rootReply?.eventId, + inReplyToRootProfilePublicKey = rootReply?.author, + + replyingNostrEventId = id + ) + } + } + + return null + } catch (e: Throwable) { + logger.e("Failed to return Repost: ", e) + return null + } fun toZap(): Zap? = try { if (kind == LnZapEvent.KIND) { val zapEvent = EventFactory.create( @@ -299,6 +375,58 @@ data class NostrEvent( // TODO: Check for unsupported kind val taggedEvent = event.firstTaggedEvent() + val quotedEvent = when (val taggedQuote = event.firstTaggedQuote()) { + is QEventTag -> { + Triple( + taggedQuote.eventId, + taggedQuote.author, + taggedQuote.relay, + ) + } + is QAddressableTag -> { + Triple( + taggedQuote.address.dTag, + taggedQuote.address.pubKeyHex, + taggedQuote.relay, + ) + } + else -> null + } + + val repostedEvent = if (event.kind == RepostEvent.KIND) { + val repostedEvent = event.taggedEvents().firstOrNull() + when { + (repostedEvent != null) -> { + repostedEvent + } + else -> null + } + } else { + null + } + + val (replyToRoot, replyTo) = if (event.kind == TextNoteEvent.KIND) { + val textNoteEvent = TextNoteEvent( + id = event.id, + content = event.content, + tags = event.tags, + createdAt = event.createdAt, + sig = event.sig, + pubKey = event.pubKey + ) + + Pair( + textNoteEvent.markedRoot(), + textNoteEvent.markedReply() + ) + } else { + Pair( + null, + null + ) + } + + return NostrEvent( id = event.id, pubKey = event.pubKey, @@ -309,8 +437,17 @@ data class NostrEvent( unsignedNostrEventId = synchronizeNostrEventRequest?.unsignedNostrEventId, sig = event.sig, - taggedNostrEventId = taggedEvent?.eventId, - taggedNostrEventRelayUrl = taggedEvent?.relay?.url, + quotedNostrEventId = quotedEvent?.first, + quotedAuthorPublicKey = quotedEvent?.second, + + repostedNostrEventId = repostedEvent?.eventId, + repostedAuthorPublicKey = repostedEvent?.author, + + inReplyToRootNostrEventId = replyToRoot?.eventId, + inReplyToRootAuthorPublicKey = replyToRoot?.author, + + inReplyToNostrEventId = replyTo?.eventId, + inReplyToAuthorPublicKey = replyTo?.author, ) } catch (e: Throwable) { return null diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/QuotedPost.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/QuotedPost.kt deleted file mode 100644 index 8f6221cc..00000000 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/QuotedPost.kt +++ /dev/null @@ -1,188 +0,0 @@ -package ac.cord.auxiliary.compose.database.model - -import ac.cord.auxiliary.compose.database.model.traits.BroadcastableEntity -import ac.cord.auxiliary.compose.database.model.traits.LocalStoreEntity -import ac.cord.auxiliary.compose.database.model.traits.NostrEventEntity -import ac.cord.auxiliary.compose.database.model.traits.ProfilePublicKeyEntity -import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity -import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity -import ac.cord.auxiliary.compose.database.model.traits.UserViewableEntity -import ac.cord.auxiliary.compose.extensions.toFormattedTimeAndDateString -import ac.cord.auxiliary.compose.ui.composable.widgets.profile.ProfileAvatar -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.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -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.TagArray -import kotlin.time.Clock -import kotlin.time.Instant - -@Entity( - foreignKeys = [ - ForeignKey( - entity = Profile::class, - parentColumns = ["publicKey"], - childColumns = ["profilePublicKey"], - onDelete = ForeignKey.CASCADE, - ), - ForeignKey( - entity = Profile::class, - parentColumns = ["publicKey"], - childColumns = ["quotedPostAuthorPublicKey"], - 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 = ["quotedPostId"], - onDelete = ForeignKey.CASCADE, - ), - ], - indices = [ - Index("profilePublicKey"), - Index("nostrEventId"), - Index("quotedPostId") - ], -) -data class QuotedPost( - @PrimaryKey - val id: HexKey, - - val quotedPostId: HexKey, - val quotedPostTags: TagArray, - val quotedPostCreatedAt: Instant, - val quotedPostContent: String, - val quotedPostAuthorPublicKey: HexKey, - - val content: String, - - override val nostrEventId: HexKey, // Might be a replaceable nostr event? - override val profilePublicKey: HexKey, - - override val createdAt: Instant = Clock.System.now(), - override val updatedAt: Instant = Clock.System.now(), - override val savedAt: Instant = Clock.System.now(), - override val viewedAt: Instant? = null, - override val deletedAt: Instant? = null, - override val broadcastedAt: Instant? = null, -): 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 QuotedPost - - if (id != other.id) return false - if (quotedPostId != other.quotedPostId) return false - if (!quotedPostTags.contentDeepEquals(other.quotedPostTags)) return false - if (quotedPostCreatedAt != other.quotedPostCreatedAt) return false - if (quotedPostContent != other.quotedPostContent) return false - if (quotedPostAuthorPublicKey != other.quotedPostAuthorPublicKey) 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 + quotedPostId.hashCode() - result = 31 * result + quotedPostTags.contentDeepHashCode() - result = 31 * result + quotedPostCreatedAt.hashCode() - result = 31 * result + quotedPostContent.hashCode() - result = 31 * result + quotedPostAuthorPublicKey.hashCode() - 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 - } - - @Composable - fun RenderNotePreview() { - Column { - Row( - modifier = Modifier.fillMaxWidth().padding(12.dp), - horizontalArrangement = Arrangement.spacedBy(4.dp), - verticalAlignment = Alignment.CenterVertically - ) { - -// TODO ProfileAvatar( -// profile = profile, -// publicKey = pubKey -// ) - -// Column( -// modifier = Modifier.weight(1f) -// ) { -// Text( -// text = profile?.displayName ?: profile?.userName ?: nostrEvent.pubKey, -// style = MaterialTheme.typography.labelLarge -// ) -// -// profile?.nip05?.let { -// Text( -// text = profile.nip05 ?: "", -// style = MaterialTheme.typography.labelMedium -// ) -// } -// } - Text( - text = "Loading Profile information" - ) - } - Text( - modifier = Modifier.fillMaxWidth().padding( - horizontal = 20.dp - ), - style = MaterialTheme.typography.labelSmall, - textAlign = TextAlign.Start, - overflow = TextOverflow.Ellipsis, - text = content - ) - Row( - modifier = Modifier.fillMaxWidth().padding(20.dp), - horizontalArrangement = Arrangement.Start - ) { - Text( - text = createdAt.toFormattedTimeAndDateString(), - style = MaterialTheme.typography.labelSmall - ) - } - } - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/QuotedRelation.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/QuotedRelation.kt new file mode 100644 index 00000000..3212451c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/QuotedRelation.kt @@ -0,0 +1,57 @@ +package ac.cord.auxiliary.compose.database.model + +import ac.cord.auxiliary.compose.database.model.traits.BroadcastableEntity +import ac.cord.auxiliary.compose.database.model.traits.LocalStoreEntity +import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity +import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity +import ac.cord.auxiliary.compose.database.model.traits.UserViewableEntity +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.Clock +import kotlin.time.Instant + +@Entity( + foreignKeys = [ + ForeignKey( + entity = Profile::class, + parentColumns = ["publicKey"], + childColumns = ["quotedProfilePublicKey"], + 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 = ["quotingNostrEventId"], + onDelete = ForeignKey.CASCADE, + ), + // Support post replies + ForeignKey( + entity = NostrEvent::class, + parentColumns = ["id"], + childColumns = ["quotedNostrEventId"], + onDelete = ForeignKey.CASCADE, + ), + ], + primaryKeys = [ + "quotingNostrEventId", "quotedNostrEventId" + ] +) +data class QuotedRelation( + val quotingNostrEventId: HexKey, + + val quotedNostrEventId: HexKey, + val quotedProfilePublicKey: HexKey?, + + override val createdAt: Instant = Clock.System.now(), + override val updatedAt: Instant = createdAt, + override val savedAt: Instant = Clock.System.now(), + override val viewedAt: Instant? = null, + override val deletedAt: Instant? = null, + override val broadcastedAt: Instant? = null, +): TimestampedEntity, LocalStoreEntity, UserViewableEntity, BroadcastableEntity, SoftDeletableEntity { + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/Repost.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/Repost.kt deleted file mode 100644 index e3ed7229..00000000 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/Repost.kt +++ /dev/null @@ -1,115 +0,0 @@ -package ac.cord.auxiliary.compose.database.model - -import ac.cord.auxiliary.compose.database.model.traits.BroadcastableEntity -import ac.cord.auxiliary.compose.database.model.traits.LocalStoreEntity -import ac.cord.auxiliary.compose.database.model.traits.NostrEventEntity -import ac.cord.auxiliary.compose.database.model.traits.ProfilePublicKeyEntity -import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity -import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity -import ac.cord.auxiliary.compose.database.model.traits.UserViewableEntity -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.TagArray -import kotlin.time.Clock -import kotlin.time.Instant - -@Entity( - foreignKeys = [ - ForeignKey( - entity = Profile::class, - parentColumns = ["publicKey"], - childColumns = ["profilePublicKey"], - 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... - ForeignKey( - entity = NostrEvent::class, - parentColumns = ["id"], - childColumns = ["nostrEventId"], - onDelete = ForeignKey.CASCADE, - ), - // Support post replies - ForeignKey( - entity = Post::class, - parentColumns = ["id"], - childColumns = ["repostedPostId"], - onDelete = ForeignKey.CASCADE, - ), - ], - indices = [ - Index("profilePublicKey"), - Index("nostrEventId"), - Index("repostedPostId") - ], -) -data class Repost( - @PrimaryKey - val id: HexKey, - - val repostedPostId: HexKey, - val repostedPostTags: TagArray, - val repostedPostCreatedAt: Instant, - val repostedPostContent: String, - val repostedPostAuthorPublicKey: HexKey, - - val content: String, - - override val nostrEventId: HexKey, // Might be a replaceable nostr event? - override val profilePublicKey: HexKey, - - override val createdAt: Instant = Clock.System.now(), - override val updatedAt: Instant = Clock.System.now(), - override val savedAt: Instant = Clock.System.now(), - override val viewedAt: Instant? = null, - override val deletedAt: Instant? = null, - override val broadcastedAt: Instant? = null, -): 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 - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/RepostedRelation.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/RepostedRelation.kt new file mode 100644 index 00000000..6cd402df --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/RepostedRelation.kt @@ -0,0 +1,56 @@ +package ac.cord.auxiliary.compose.database.model + +import ac.cord.auxiliary.compose.database.model.traits.BroadcastableEntity +import ac.cord.auxiliary.compose.database.model.traits.LocalStoreEntity +import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity +import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity +import ac.cord.auxiliary.compose.database.model.traits.UserViewableEntity +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.Clock +import kotlin.time.Instant + +@Entity( + foreignKeys = [ + ForeignKey( + entity = Profile::class, + parentColumns = ["publicKey"], + childColumns = ["repostedProfilePublicKey"], + onDelete = ForeignKey.CASCADE, + ), + ForeignKey( + entity = NostrEvent::class, + parentColumns = ["id"], + childColumns = ["repostedNostrEventId"], + 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 = ["repostingNostrEventId"], + onDelete = ForeignKey.CASCADE, + ), + ], + primaryKeys = [ + "repostingNostrEventId", "repostedNostrEventId" + ] +) +data class RepostedRelation( + val repostingNostrEventId: HexKey, + + val repostedNostrEventId: HexKey, + val repostedProfilePublicKey: HexKey?, + + override val createdAt: Instant = Clock.System.now(), + override val updatedAt: Instant = Clock.System.now(), + override val savedAt: Instant = Clock.System.now(), + override val viewedAt: Instant? = null, + override val deletedAt: Instant? = null, + override val broadcastedAt: Instant? = null, +): TimestampedEntity, LocalStoreEntity, UserViewableEntity, BroadcastableEntity, SoftDeletableEntity { + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalNostrEvent.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalNostrEvent.kt index bab8768e..fae9e72d 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalNostrEvent.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalNostrEvent.kt @@ -1,11 +1,11 @@ package ac.cord.auxiliary.compose.database.model.intermdiate -import ac.cord.auxiliary.compose.database.model.InReplyToPost +import ac.cord.auxiliary.compose.database.model.InReplyToRelation import ac.cord.auxiliary.compose.database.model.Mention import ac.cord.auxiliary.compose.database.model.NostrEvent import ac.cord.auxiliary.compose.database.model.Profile -import ac.cord.auxiliary.compose.database.model.QuotedPost -import ac.cord.auxiliary.compose.database.model.Repost +import ac.cord.auxiliary.compose.database.model.QuotedRelation +import ac.cord.auxiliary.compose.database.model.RepostedRelation import ac.cord.auxiliary.compose.extensions.toFormattedTimeAndDateString import ac.cord.auxiliary.compose.ui.composable.widgets.profile.ProfileAvatar import androidx.compose.foundation.layout.Arrangement @@ -22,6 +22,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.room.Embedded +import androidx.room.Ignore import androidx.room.Junction import androidx.room.Relation @@ -36,32 +37,59 @@ data class LocalNostrEvent( @Relation( parentColumn = "id", - entityColumn = "nostrEventId" + entityColumn = "repostingNostrEventId" ) - val repost: Repost? = null, + val repostedRelation: RepostedRelation? = null, @Relation( parentColumn = "id", - entityColumn = "nostrEventId" + entityColumn = "quotingNostrEventId" ) - val quotedPost: QuotedPost? = null, + val quotedRelation: QuotedRelation? = null, @Relation( parentColumn = "id", - entityColumn = "nostrEventId" + entityColumn = "id", + associateBy = Junction( + value = QuotedRelation::class, + parentColumn = "quotingNostrEventId", + entityColumn = "quotedNostrEventId" + ) ) - val inReplyToNostrEvent: InReplyToPost? = null, + val quotedNostrEvent: NostrEvent? = null, + @Relation( + parentColumn = "id", + entityColumn = "publicKey", + associateBy = Junction( + value = QuotedRelation::class, + parentColumn = "quotingNostrEventId", + entityColumn = "quotedProfilePublicKey" + ) + ) + val quotedAuthorProfile: Profile? = null, + + @Relation( + parentColumn = "id", + entityColumn = "replyingNostrEventId" + ) + val inReplyToNostrEvent: InReplyToRelation? = null, + +// @Relation( +// parentColumn = "id", +// entityColumn = "nostrEventId", +// associateBy = Junction( +// value = Mention::class, +// parentColumn = "mentionedPublicKey", +// entityColumn = "mentionedPublicKey" +// ) +// ) +// val mentionedProfiles: List = emptyList() @Relation( parentColumn = "id", entityColumn = "nostrEventId", - associateBy = Junction( - value = Mention::class, - parentColumn = "publicKey", - entityColumn = "publicKey" - ) ) - val mentionedProfiles: List = emptyList() + val mentions: List = emptyList() ) { @Composable fun RenderNotePreview() { diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalQuotedNostrEvent.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalQuotedNostrEvent.kt new file mode 100644 index 00000000..490771c0 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalQuotedNostrEvent.kt @@ -0,0 +1,99 @@ +package ac.cord.auxiliary.compose.database.model.intermdiate + +import ac.cord.auxiliary.compose.database.model.InReplyToRelation +import ac.cord.auxiliary.compose.database.model.Mention +import ac.cord.auxiliary.compose.database.model.NostrEvent +import ac.cord.auxiliary.compose.database.model.Profile +import ac.cord.auxiliary.compose.database.model.QuotedRelation +import ac.cord.auxiliary.compose.database.model.RepostedRelation +import ac.cord.auxiliary.compose.extensions.toFormattedTimeAndDateString +import ac.cord.auxiliary.compose.ui.composable.widgets.profile.ProfileAvatar +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.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.room.Embedded +import androidx.room.Junction +import androidx.room.Relation + +data class LocalQuotedNostrEvent( + @Embedded val nostrEvent: NostrEvent, + + @Relation( + parentColumn = "pubKey", + entityColumn = "publicKey" + ) + val profile: Profile?, + + @Relation( + parentColumn = "id", + entityColumn = "nostrEventId", + associateBy = Junction( + value = Mention::class, + parentColumn = "publicKey", + entityColumn = "publicKey" + ) + ) + val mentionedProfiles: List = emptyList() +) { + @Composable + fun RenderNotePreview() { + Column { + Row( + modifier = Modifier.fillMaxWidth().padding(12.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + val profile = profile + + ProfileAvatar( + profile = profile, + publicKey = nostrEvent.pubKey + ) + + Column( + modifier = Modifier.weight(1f) + ) { + Text( + text = profile?.displayName ?: profile?.userName ?: nostrEvent.pubKey, + style = MaterialTheme.typography.labelLarge + ) + + profile?.nip05?.let { + Text( + text = profile.nip05 ?: "", + style = MaterialTheme.typography.labelMedium + ) + } + } + } + Text( + modifier = Modifier.fillMaxWidth().padding( + horizontal = 20.dp + ), + style = MaterialTheme.typography.labelSmall, + textAlign = TextAlign.Start, + overflow = TextOverflow.Ellipsis, + text = nostrEvent.content + ) + Row( + modifier = Modifier.fillMaxWidth().padding(20.dp), + horizontalArrangement = Arrangement.Start + ) { + Text( + text = nostrEvent.createdAt.toFormattedTimeAndDateString(), + style = MaterialTheme.typography.labelSmall + ) + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/traits/ProfilePublicKeyEntity.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/traits/ProfilePublicKeyEntity.kt index 6d2ccb4a..98dc4d06 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/traits/ProfilePublicKeyEntity.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/traits/ProfilePublicKeyEntity.kt @@ -3,5 +3,5 @@ package ac.cord.auxiliary.compose.database.model.traits import com.vitorpamplona.quartz.nip01Core.core.HexKey interface ProfilePublicKeyEntity { - val profilePublicKey: HexKey + val profilePublicKey: HexKey? } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/QuotedAddressableNote.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/QuotedAddressableNote.kt index 9fb0b7a2..6b624ff0 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/QuotedAddressableNote.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/QuotedAddressableNote.kt @@ -14,7 +14,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.TextStyle @@ -41,9 +40,9 @@ internal fun QuotedAddressableNote( // } // } - if (localNostrEvent?.quotedPost != null) { + if (localNostrEvent?.quotedRelation != null) { QuotedNote( - quotedPost = localNostrEvent.quotedPost, + quotedNostrEvent = localNostrEvent.quotedRelation, onNoteClick = onNoteClick, noteActions = noteActions ) diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/QuotedNote.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/QuotedNote.kt index f7eb7ab7..767aee31 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/QuotedNote.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/QuotedNote.kt @@ -1,12 +1,10 @@ package ac.cord.auxiliary.compose.ui.composable.widgets.content -import ac.cord.auxiliary.compose.database.model.QuotedPost -import ac.cord.auxiliary.compose.database.model.intermdiate.LocalNostrEvent -import ac.cord.auxiliary.compose.ui.composable.widgets.content.Helpers.isGalleryEvent -import ac.cord.auxiliary.compose.ui.composable.widgets.profile.ProfileAvatar +import ac.cord.auxiliary.compose.database.model.NostrEvent +import ac.cord.auxiliary.compose.database.model.Profile +import ac.cord.auxiliary.compose.database.model.QuotedRelation import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -19,16 +17,14 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @Composable fun QuotedNote( - quotedPost: QuotedPost? = null, + quotedNostrEvent: QuotedRelation? = null, + quotedProfile: Profile? = null, relayHints: List = emptyList(), onNoteClick: ((String) -> Unit)? = null, noteActions: NoteActions? = null @@ -54,7 +50,7 @@ fun QuotedNote( // } // } - if (quotedPost != null && noteActions != null) { + if (quotedNostrEvent != null && noteActions != null) { // Full rendering with all interactive features Surface( @@ -65,9 +61,10 @@ fun QuotedNote( .fillMaxWidth() .padding(vertical = 6.dp) ) { - quotedPost.RenderNotePreview() + Text("Quote render coming soon") +// quotedNostrEvent.RenderNotePreview() } - } else if (quotedPost != null) { + } else if (quotedNostrEvent != null) { // Simple fallback rendering (no noteActions available) Surface( shape = RoundedCornerShape(12.dp), @@ -77,11 +74,12 @@ fun QuotedNote( .fillMaxWidth() .padding(vertical = 6.dp) .then( - if (effectiveNoteClick != null) Modifier.clickable { effectiveNoteClick(quotedPost.nostrEventId) } + if (effectiveNoteClick != null) Modifier.clickable { effectiveNoteClick(quotedNostrEvent.quotedNostrEventId) } else Modifier ) ) { - quotedPost.RenderNotePreview() + Text("Quote render coming soon") +// quotedPost.RenderNotePreview() } } else { // Loading state diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/RichContent.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/RichContent.kt index 0efa8874..f062ac13 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/RichContent.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/RichContent.kt @@ -101,11 +101,11 @@ fun RichContent( val profileNames = remember(profilePubkeys) { // TODO: Get these from taggedUsers... val names = mutableMapOf() - for (pubkey in profilePubkeys) { - val profile = localNostrEvent.mentionedProfiles.find { taggedAuthor -> taggedAuthor.publicKey == pubkey } - names[pubkey] = profile?.humanReadableNameOrPubkey() - ?: "${pubkey.take(8)}...${pubkey.takeLast(4)}" - } +// TODO for (pubkey in profilePubkeys) { +// val profile = localNostrEvent.mentionedProfiles.find { taggedAuthor -> taggedAuthor.publicKey == pubkey } +// names[pubkey] = profile?.humanReadableNameOrPubkey() +// ?: "${pubkey.take(8)}...${pubkey.takeLast(4)}" +// } names } // TODO Queue fetches for any missing profiles @@ -263,9 +263,9 @@ fun RichContent( LinkPreview(url = segment.url) } is ContentSegment.NostrNoteSegment -> { - if (localNostrEvent.quotedPost != null) { + if (localNostrEvent.quotedRelation != null) { QuotedNote( - quotedPost = localNostrEvent.quotedPost, + quotedNostrEvent = localNostrEvent.quotedRelation, relayHints = segment.relayHints, onNoteClick = onNoteClick, noteActions = noteActions diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/feed/EventListView.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/feed/EventListView.kt index 59a020f7..69de015e 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/feed/EventListView.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/feed/EventListView.kt @@ -4,7 +4,6 @@ import ac.cord.auxiliary.compose.database.model.NostrEvent import ac.cord.auxiliary.compose.database.model.Profile import ac.cord.auxiliary.compose.database.model.intermdiate.LocalNostrEvent import ac.cord.auxiliary.compose.extensions.toFormattedTimeAndDateString -import ac.cord.auxiliary.compose.ui.composable.widgets.content.RichContent import ac.cord.auxiliary.compose.ui.composable.widgets.profile.ProfileAvatar import ac.cord.auxiliary.compose.ui.theme.AuxTheme import androidx.compose.foundation.clickable @@ -62,7 +61,7 @@ fun LocalNostrEvent.ListView( style = MaterialTheme.typography.labelSmall) } - if (repost != null) { + if (repostedRelation != null) { TextNoteFeedItem( localNostrEvent = this@ListView, onNavigateToEvent = onNavigateToEvent @@ -110,7 +109,7 @@ private fun TextNoteFeedItem( onNavigateToEvent: (HexKey) -> Unit ) { val nostrEvent = localNostrEvent.nostrEvent - if (localNostrEvent.repost != null) { + if (localNostrEvent.repostedRelation != null) { Row( modifier = Modifier.fillMaxWidth().padding( horizontal = 20.dp @@ -127,11 +126,11 @@ private fun TextNoteFeedItem( Text( modifier = Modifier.clickable( onClick = { - onNavigateToEvent.invoke(localNostrEvent.repost.repostedPostId) + onNavigateToEvent.invoke(localNostrEvent.repostedRelation.repostedNostrEventId) } ), style = MaterialTheme.typography.labelLargeEmphasized, - text = localNostrEvent.repost.repostedPostAuthorPublicKey ?: "loading information..." + text = localNostrEvent.repostedRelation.repostedProfilePublicKey ?: "loading information..." ) } } @@ -178,9 +177,12 @@ private fun TextNoteFeedItem( } // TODO: RichContent here... - RichContent( - modifier = Modifier.fillMaxWidth(), - localNostrEvent = localNostrEvent, +// RichContent( +// modifier = Modifier.fillMaxWidth(), +// localNostrEvent = localNostrEvent, +// ) + Text( + text = localNostrEvent.nostrEvent.content ) LazyRow( @@ -250,10 +252,6 @@ private fun EventListViewPreview() { nostrEventId = "referenced", displayName = "Frank Talk" ), - repost = null, - quotedPost = null, - inReplyToNostrEvent = null, - mentionedProfiles = emptyList() ).ListView( onNavigateToEvent = {} )