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 03251bd9..9e3ddea4 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": "49c580a762a1c128bf5b4fc6f93eeea4", + "identityHash": "6eec904dd8f91ab435301e23425eb536", "entities": [ { "tableName": "BroadcastNostrEventReceipt", @@ -274,6 +274,197 @@ } ] }, + { + "tableName": "ChatMessage", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `senderPublicKey` TEXT NOT NULL, `isUserMessage` INTEGER NOT NULL, `chatRoomId` TEXT NOT NULL, `replyToEventId` TEXT, `quotedEventId` TEXT, `content` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`chatRoomId`) REFERENCES `ChatRoom`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`id`) REFERENCES `GiftWrapPayload`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderPublicKey", + "columnName": "senderPublicKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isUserMessage", + "columnName": "isUserMessage", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "chatRoomId", + "columnName": "chatRoomId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "replyToEventId", + "columnName": "replyToEventId", + "affinity": "TEXT" + }, + { + "fieldPath": "quotedEventId", + "columnName": "quotedEventId", + "affinity": "TEXT" + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "savedAt", + "columnName": "savedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "viewedAt", + "columnName": "viewedAt", + "affinity": "INTEGER" + }, + { + "fieldPath": "deletedAt", + "columnName": "deletedAt", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "foreignKeys": [ + { + "table": "ChatRoom", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "chatRoomId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "GiftWrapPayload", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "ChatRoom", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `userPublicKey` TEXT NOT NULL, `subject` TEXT, `initialGiftWrapUnsignedId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`userPublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`initialGiftWrapUnsignedId`) REFERENCES `GiftWrapPayload`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userPublicKey", + "columnName": "userPublicKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT" + }, + { + "fieldPath": "initialGiftWrapUnsignedId", + "columnName": "initialGiftWrapUnsignedId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "savedAt", + "columnName": "savedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "viewedAt", + "columnName": "viewedAt", + "affinity": "INTEGER" + }, + { + "fieldPath": "deletedAt", + "columnName": "deletedAt", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "foreignKeys": [ + { + "table": "Profile", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "userPublicKey" + ], + "referencedColumns": [ + "publicKey" + ] + }, + { + "table": "GiftWrapPayload", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "initialGiftWrapUnsignedId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, { "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 )", @@ -399,6 +590,293 @@ } ] }, + { + "tableName": "GiftWrapMessage", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `publicKey` TEXT NOT NULL, `kind` INTEGER NOT NULL, `receiverPublicKey` TEXT NOT NULL, `receiverRelayHit` TEXT, `content` TEXT NOT NULL, `signature` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "publicKey", + "columnName": "publicKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "kind", + "columnName": "kind", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "receiverPublicKey", + "columnName": "receiverPublicKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "receiverRelayHit", + "columnName": "receiverRelayHit", + "affinity": "TEXT" + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "signature", + "columnName": "signature", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "nostrEventId", + "columnName": "nostrEventId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "savedAt", + "columnName": "savedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "viewedAt", + "columnName": "viewedAt", + "affinity": "INTEGER" + }, + { + "fieldPath": "deletedAt", + "columnName": "deletedAt", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "foreignKeys": [ + { + "table": "NostrEvent", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "nostrEventId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "GiftWrapSeal", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `publicKey` TEXT NOT NULL, `kind` INTEGER NOT NULL, `tags` TEXT NOT NULL, `content` TEXT NOT NULL, `signature` TEXT NOT NULL, `giftWrapMessageId` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`giftWrapMessageId`) REFERENCES `GiftWrapMessage`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "publicKey", + "columnName": "publicKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "kind", + "columnName": "kind", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "signature", + "columnName": "signature", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "giftWrapMessageId", + "columnName": "giftWrapMessageId", + "affinity": "TEXT" + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "savedAt", + "columnName": "savedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "viewedAt", + "columnName": "viewedAt", + "affinity": "INTEGER" + }, + { + "fieldPath": "deletedAt", + "columnName": "deletedAt", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "foreignKeys": [ + { + "table": "GiftWrapMessage", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "giftWrapMessageId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "GiftWrapPayload", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `publicKey` TEXT NOT NULL, `kind` INTEGER NOT NULL, `tags` TEXT NOT NULL, `content` TEXT NOT NULL, `quotedEventId` TEXT, `giftWrapSealId` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`giftWrapSealId`) REFERENCES `GiftWrapSeal`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "publicKey", + "columnName": "publicKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "kind", + "columnName": "kind", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "quotedEventId", + "columnName": "quotedEventId", + "affinity": "TEXT" + }, + { + "fieldPath": "giftWrapSealId", + "columnName": "giftWrapSealId", + "affinity": "TEXT" + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "savedAt", + "columnName": "savedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "viewedAt", + "columnName": "viewedAt", + "affinity": "INTEGER" + }, + { + "fieldPath": "deletedAt", + "columnName": "deletedAt", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "foreignKeys": [ + { + "table": "GiftWrapSeal", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "giftWrapSealId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, { "tableName": "Mention", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `mentionedPublicKey` TEXT NOT NULL, `mentioningNostrEventId` TEXT, `relayUrl` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, FOREIGN KEY(`mentionedPublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`mentioningNostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", @@ -2066,7 +2544,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, '49c580a762a1c128bf5b4fc6f93eeea4')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6eec904dd8f91ab435301e23425eb536')" ] } } \ No newline at end of file diff --git a/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/2.json b/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/2.json deleted file mode 100644 index e59cb3f7..00000000 --- a/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/2.json +++ /dev/null @@ -1,2556 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 2, - "identityHash": "db402ac92a6774f569ca2c6e19a4b9a5", - "entities": [ - { - "tableName": "BroadcastNostrEventReceipt", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nostrEventId` TEXT NOT NULL, `unsignedNostrEventId` INTEGER, `isAccepted` INTEGER NOT NULL, `relayURL` TEXT NOT NULL, `messages` TEXT, FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`unsignedNostrEventId`) REFERENCES `UnsignedNostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "unsignedNostrEventId", - "columnName": "unsignedNostrEventId", - "affinity": "INTEGER" - }, - { - "fieldPath": "isAccepted", - "columnName": "isAccepted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "relayURL", - "columnName": "relayURL", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messages", - "columnName": "messages", - "affinity": "TEXT" - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_BroadcastNostrEventReceipt_nostrEventId", - "unique": false, - "columnNames": [ - "nostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_BroadcastNostrEventReceipt_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)" - }, - { - "name": "index_BroadcastNostrEventReceipt_isAccepted", - "unique": false, - "columnNames": [ - "isAccepted" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_BroadcastNostrEventReceipt_isAccepted` ON `${TABLE_NAME}` (`isAccepted`)" - } - ], - "foreignKeys": [ - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "nostrEventId" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "UnsignedNostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "unsignedNostrEventId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "BroadcastNostrEventRequest", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nostrEventId` TEXT NOT NULL, `unsignedNostrEventId` INTEGER, `status` TEXT NOT NULL, `relayURL` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`unsignedNostrEventId`) REFERENCES `UnsignedNostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "unsignedNostrEventId", - "columnName": "unsignedNostrEventId", - "affinity": "INTEGER" - }, - { - "fieldPath": "status", - "columnName": "status", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "relayURL", - "columnName": "relayURL", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_BroadcastNostrEventRequest_nostrEventId", - "unique": false, - "columnNames": [ - "nostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_BroadcastNostrEventRequest_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)" - }, - { - "name": "index_BroadcastNostrEventRequest_status", - "unique": false, - "columnNames": [ - "status" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_BroadcastNostrEventRequest_status` ON `${TABLE_NAME}` (`status`)" - } - ], - "foreignKeys": [ - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "nostrEventId" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "UnsignedNostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "unsignedNostrEventId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "Connection", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourcePublicKey` TEXT NOT NULL, `destinationPublicKey` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`sourcePublicKey`, `destinationPublicKey`), FOREIGN KEY(`sourcePublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`destinationPublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "sourcePublicKey", - "columnName": "sourcePublicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destinationPublicKey", - "columnName": "destinationPublicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "broadcastedAt", - "columnName": "broadcastedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "sourcePublicKey", - "destinationPublicKey" - ] - }, - "foreignKeys": [ - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "sourcePublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - }, - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "destinationPublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - } - ] - }, - { - "tableName": "ChatMessage", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `senderPublicKey` TEXT NOT NULL, `isUserMessage` INTEGER NOT NULL, `chatRoomId` TEXT NOT NULL, `giftWrapUnsignedId` TEXT NOT NULL, `replyToEventId` TEXT, `quotedEventId` TEXT, `content` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`chatRoomId`) REFERENCES `ChatRoom`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`giftWrapUnsignedId`) REFERENCES `GiftWrapUnsigned`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderPublicKey", - "columnName": "senderPublicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isUserMessage", - "columnName": "isUserMessage", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "chatRoomId", - "columnName": "chatRoomId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "giftWrapUnsignedId", - "columnName": "giftWrapUnsignedId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "replyToEventId", - "columnName": "replyToEventId", - "affinity": "TEXT" - }, - { - "fieldPath": "quotedEventId", - "columnName": "quotedEventId", - "affinity": "TEXT" - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "viewedAt", - "columnName": "viewedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "foreignKeys": [ - { - "table": "ChatRoom", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "chatRoomId" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "GiftWrapUnsigned", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "giftWrapUnsignedId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "ChatRoom", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `userPublicKey` TEXT NOT NULL, `subject` TEXT, `name` TEXT, `initialGiftWrapUnsignedId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`userPublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`initialGiftWrapUnsignedId`) REFERENCES `GiftWrapUnsigned`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userPublicKey", - "columnName": "userPublicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT" - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT" - }, - { - "fieldPath": "initialGiftWrapUnsignedId", - "columnName": "initialGiftWrapUnsignedId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "viewedAt", - "columnName": "viewedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "foreignKeys": [ - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "userPublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - }, - { - "table": "GiftWrapUnsigned", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "initialGiftWrapUnsignedId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "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": "replyingNostrEventId", - "columnName": "replyingNostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "inReplyToNostrEventId", - "columnName": "inReplyToNostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "inReplyToProfilePublicKey", - "columnName": "inReplyToProfilePublicKey", - "affinity": "TEXT" - }, - { - "fieldPath": "inReplyToRootNostrEventId", - "columnName": "inReplyToRootNostrEventId", - "affinity": "TEXT" - }, - { - "fieldPath": "inReplyToRootProfilePublicKey", - "columnName": "inReplyToRootProfilePublicKey", - "affinity": "TEXT" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "viewedAt", - "columnName": "viewedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "replyingNostrEventId", - "inReplyToNostrEventId" - ] - }, - "foreignKeys": [ - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "inReplyToProfilePublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - }, - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "inReplyToRootProfilePublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - }, - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "replyingNostrEventId" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "inReplyToNostrEventId" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "inReplyToRootNostrEventId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "GiftWrapMessage", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `publicKey` TEXT NOT NULL, `kind` INTEGER NOT NULL, `tags` TEXT NOT NULL, `content` TEXT NOT NULL, `signature` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "publicKey", - "columnName": "publicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "kind", - "columnName": "kind", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "tags", - "columnName": "tags", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "signature", - "columnName": "signature", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "viewedAt", - "columnName": "viewedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "foreignKeys": [ - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "nostrEventId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "GiftWrapSeal", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `publicKey` TEXT NOT NULL, `kind` INTEGER NOT NULL, `tags` TEXT NOT NULL, `content` TEXT NOT NULL, `signature` TEXT NOT NULL, `giftWrapMessageId` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`giftWrapMessageId`) REFERENCES `GiftWrapMessage`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "publicKey", - "columnName": "publicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "kind", - "columnName": "kind", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "tags", - "columnName": "tags", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "signature", - "columnName": "signature", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "giftWrapMessageId", - "columnName": "giftWrapMessageId", - "affinity": "TEXT" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "viewedAt", - "columnName": "viewedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "foreignKeys": [ - { - "table": "GiftWrapMessage", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "giftWrapMessageId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "GiftWrapUnsigned", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `publicKey` TEXT NOT NULL, `kind` INTEGER NOT NULL, `tags` TEXT NOT NULL, `content` TEXT NOT NULL, `quotedEventId` TEXT, `giftWrapSealId` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`giftWrapSealId`) REFERENCES `GiftWrapSeal`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "publicKey", - "columnName": "publicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "kind", - "columnName": "kind", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "tags", - "columnName": "tags", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "quotedEventId", - "columnName": "quotedEventId", - "affinity": "TEXT" - }, - { - "fieldPath": "giftWrapSealId", - "columnName": "giftWrapSealId", - "affinity": "TEXT" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "viewedAt", - "columnName": "viewedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "foreignKeys": [ - { - "table": "GiftWrapSeal", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "giftWrapSealId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "Mention", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `mentionedPublicKey` TEXT NOT NULL, `mentioningNostrEventId` TEXT, `relayUrl` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, FOREIGN KEY(`mentionedPublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`mentioningNostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "mentionedPublicKey", - "columnName": "mentionedPublicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mentioningNostrEventId", - "columnName": "mentioningNostrEventId", - "affinity": "TEXT" - }, - { - "fieldPath": "relayUrl", - "columnName": "relayUrl", - "affinity": "TEXT" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "broadcastedAt", - "columnName": "broadcastedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "foreignKeys": [ - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "mentionedPublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - }, - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "mentioningNostrEventId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "NegentropySynchronizeRequest", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `uuid` TEXT NOT NULL, `purpose` TEXT NOT NULL, `status` TEXT NOT NULL, `relayURL` TEXT NOT NULL, `isRecommendedRelay` INTEGER NOT NULL, `level` INTEGER NOT NULL, `synchronizationFilter` TEXT NOT NULL, `nostrEventId` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "uuid", - "columnName": "uuid", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "purpose", - "columnName": "purpose", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "status", - "columnName": "status", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "relayURL", - "columnName": "relayURL", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isRecommendedRelay", - "columnName": "isRecommendedRelay", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "level", - "columnName": "level", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "synchronizationFilter", - "columnName": "synchronizationFilter", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_NegentropySynchronizeRequest_nostrEventId", - "unique": false, - "columnNames": [ - "nostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_NegentropySynchronizeRequest_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)" - }, - { - "name": "index_NegentropySynchronizeRequest_status", - "unique": false, - "columnNames": [ - "status" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_NegentropySynchronizeRequest_status` ON `${TABLE_NAME}` (`status`)" - } - ], - "foreignKeys": [ - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "nostrEventId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "NegentropySynchronizeResult", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `relayURL` TEXT NOT NULL, `status` TEXT NOT NULL, `negentropySynchronizeRequestId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`negentropySynchronizeRequestId`) REFERENCES `SynchronizeNostrEventResult`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "relayURL", - "columnName": "relayURL", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "status", - "columnName": "status", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "negentropySynchronizeRequestId", - "columnName": "negentropySynchronizeRequestId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "foreignKeys": [ - { - "table": "SynchronizeNostrEventResult", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "negentropySynchronizeRequestId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "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, `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", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pubKey", - "columnName": "pubKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "kind", - "columnName": "kind", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "tags", - "columnName": "tags", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sig", - "columnName": "sig", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "quotedNostrEventId", - "columnName": "quotedNostrEventId", - "affinity": "TEXT" - }, - { - "fieldPath": "quotedAuthorPublicKey", - "columnName": "quotedAuthorPublicKey", - "affinity": "TEXT" - }, - { - "fieldPath": "inReplyToNostrEventId", - "columnName": "inReplyToNostrEventId", - "affinity": "TEXT" - }, - { - "fieldPath": "inReplyToAuthorPublicKey", - "columnName": "inReplyToAuthorPublicKey", - "affinity": "TEXT" - }, - { - "fieldPath": "inReplyToRootNostrEventId", - "columnName": "inReplyToRootNostrEventId", - "affinity": "TEXT" - }, - { - "fieldPath": "inReplyToRootAuthorPublicKey", - "columnName": "inReplyToRootAuthorPublicKey", - "affinity": "TEXT" - }, - { - "fieldPath": "repostedNostrEventId", - "columnName": "repostedNostrEventId", - "affinity": "TEXT" - }, - { - "fieldPath": "repostedAuthorPublicKey", - "columnName": "repostedAuthorPublicKey", - "affinity": "TEXT" - }, - { - "fieldPath": "unsignedNostrEventId", - "columnName": "unsignedNostrEventId", - "affinity": "INTEGER" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "broadcastedAt", - "columnName": "broadcastedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_NostrEvent_kind", - "unique": false, - "columnNames": [ - "kind" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEvent_kind` ON `${TABLE_NAME}` (`kind`)" - }, - { - "name": "index_NostrEvent_pubKey", - "unique": false, - "columnNames": [ - "pubKey" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEvent_pubKey` ON `${TABLE_NAME}` (`pubKey`)" - }, - { - "name": "index_NostrEvent_quotedNostrEventId", - "unique": false, - "columnNames": [ - "quotedNostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEvent_quotedNostrEventId` ON `${TABLE_NAME}` (`quotedNostrEventId`)" - }, - { - "name": "index_NostrEvent_inReplyToNostrEventId", - "unique": false, - "columnNames": [ - "inReplyToNostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEvent_inReplyToNostrEventId` ON `${TABLE_NAME}` (`inReplyToNostrEventId`)" - }, - { - "name": "index_NostrEvent_inReplyToRootNostrEventId", - "unique": false, - "columnNames": [ - "inReplyToRootNostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEvent_inReplyToRootNostrEventId` ON `${TABLE_NAME}` (`inReplyToRootNostrEventId`)" - } - ] - }, - { - "tableName": "NostrEventRelay", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`relayURL` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, PRIMARY KEY(`nostrEventId`, `relayURL`), FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "relayURL", - "columnName": "relayURL", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "nostrEventId", - "relayURL" - ] - }, - "indices": [ - { - "name": "index_NostrEventRelay_relayURL", - "unique": false, - "columnNames": [ - "relayURL" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEventRelay_relayURL` ON `${TABLE_NAME}` (`relayURL`)" - } - ], - "foreignKeys": [ - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "nostrEventId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "Post", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `repostId` TEXT, `quote` TEXT, `content` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `replyToId` TEXT, `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(`replyToId`) REFERENCES `Post`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`repostId`) REFERENCES `Post`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "repostId", - "columnName": "repostId", - "affinity": "TEXT" - }, - { - "fieldPath": "quote", - "columnName": "quote", - "affinity": "TEXT" - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "replyToId", - "columnName": "replyToId", - "affinity": "TEXT" - }, - { - "fieldPath": "profilePublicKey", - "columnName": "profilePublicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "viewedAt", - "columnName": "viewedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "broadcastedAt", - "columnName": "broadcastedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_Post_profilePublicKey", - "unique": false, - "columnNames": [ - "profilePublicKey" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Post_profilePublicKey` ON `${TABLE_NAME}` (`profilePublicKey`)" - }, - { - "name": "index_Post_nostrEventId", - "unique": false, - "columnNames": [ - "nostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Post_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)" - }, - { - "name": "index_Post_replyToId", - "unique": false, - "columnNames": [ - "replyToId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Post_replyToId` ON `${TABLE_NAME}` (`replyToId`)" - }, - { - "name": "index_Post_repostId", - "unique": false, - "columnNames": [ - "repostId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Post_repostId` ON `${TABLE_NAME}` (`repostId`)" - } - ], - "foreignKeys": [ - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "profilePublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - }, - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "nostrEventId" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "Post", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "replyToId" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "Post", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "repostId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "Profile", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`publicKey` TEXT NOT NULL, `userName` TEXT, `displayName` TEXT, `picture` TEXT, `banner` TEXT, `website` TEXT, `about` TEXT, `bot` INTEGER, `pronouns` TEXT, `nip05` TEXT, `domain` TEXT, `lud06` TEXT, `lud16` TEXT, `twitter` TEXT, `nostrEventId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`publicKey`), FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "publicKey", - "columnName": "publicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT" - }, - { - "fieldPath": "displayName", - "columnName": "displayName", - "affinity": "TEXT" - }, - { - "fieldPath": "picture", - "columnName": "picture", - "affinity": "TEXT" - }, - { - "fieldPath": "banner", - "columnName": "banner", - "affinity": "TEXT" - }, - { - "fieldPath": "website", - "columnName": "website", - "affinity": "TEXT" - }, - { - "fieldPath": "about", - "columnName": "about", - "affinity": "TEXT" - }, - { - "fieldPath": "bot", - "columnName": "bot", - "affinity": "INTEGER" - }, - { - "fieldPath": "pronouns", - "columnName": "pronouns", - "affinity": "TEXT" - }, - { - "fieldPath": "nip05", - "columnName": "nip05", - "affinity": "TEXT" - }, - { - "fieldPath": "domain", - "columnName": "domain", - "affinity": "TEXT" - }, - { - "fieldPath": "lud06", - "columnName": "lud06", - "affinity": "TEXT" - }, - { - "fieldPath": "lud16", - "columnName": "lud16", - "affinity": "TEXT" - }, - { - "fieldPath": "twitter", - "columnName": "twitter", - "affinity": "TEXT" - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "viewedAt", - "columnName": "viewedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "broadcastedAt", - "columnName": "broadcastedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "publicKey" - ] - }, - "indices": [ - { - "name": "index_Profile_nostrEventId", - "unique": false, - "columnNames": [ - "nostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Profile_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)" - } - ], - "foreignKeys": [ - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "nostrEventId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "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": "quotingNostrEventId", - "columnName": "quotingNostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "quotedNostrEventId", - "columnName": "quotedNostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "quotedProfilePublicKey", - "columnName": "quotedProfilePublicKey", - "affinity": "TEXT" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "viewedAt", - "columnName": "viewedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "broadcastedAt", - "columnName": "broadcastedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "quotingNostrEventId", - "quotedNostrEventId" - ] - }, - "foreignKeys": [ - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "quotedProfilePublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - }, - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "quotingNostrEventId" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "quotedNostrEventId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "Reaction", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `content` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `replyToId` 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(`replyToId`) REFERENCES `Post`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "replyToId", - "columnName": "replyToId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "profilePublicKey", - "columnName": "profilePublicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "viewedAt", - "columnName": "viewedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "broadcastedAt", - "columnName": "broadcastedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_Reaction_profilePublicKey", - "unique": false, - "columnNames": [ - "profilePublicKey" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Reaction_profilePublicKey` ON `${TABLE_NAME}` (`profilePublicKey`)" - }, - { - "name": "index_Reaction_nostrEventId", - "unique": false, - "columnNames": [ - "nostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Reaction_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)" - }, - { - "name": "index_Reaction_replyToId", - "unique": false, - "columnNames": [ - "replyToId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Reaction_replyToId` ON `${TABLE_NAME}` (`replyToId`)" - } - ], - "foreignKeys": [ - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "profilePublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - }, - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "nostrEventId" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "Post", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "replyToId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "RecentSearch", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`query` TEXT NOT NULL, `synchronizationFilters` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, PRIMARY KEY(`query`))", - "fields": [ - { - "fieldPath": "query", - "columnName": "query", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "synchronizationFilters", - "columnName": "synchronizationFilters", - "affinity": "TEXT" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "query" - ] - } - }, - { - "tableName": "Relay", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`publicKey` TEXT NOT NULL, `type` TEXT NOT NULL, `url` TEXT NOT NULL, `read` INTEGER NOT NULL, `write` INTEGER NOT NULL, PRIMARY KEY(`publicKey`, `type`, `url`))", - "fields": [ - { - "fieldPath": "publicKey", - "columnName": "publicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "read", - "columnName": "read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "write", - "columnName": "write", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "publicKey", - "type", - "url" - ] - } - }, - { - "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": "repostingNostrEventId", - "columnName": "repostingNostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "repostedNostrEventId", - "columnName": "repostedNostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "repostedProfilePublicKey", - "columnName": "repostedProfilePublicKey", - "affinity": "TEXT" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "viewedAt", - "columnName": "viewedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "broadcastedAt", - "columnName": "broadcastedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "repostingNostrEventId", - "repostedNostrEventId" - ] - }, - "foreignKeys": [ - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "repostedProfilePublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - }, - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "repostedNostrEventId" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "repostingNostrEventId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "SynchronizeNostrEventRequest", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `purpose` TEXT NOT NULL, `status` TEXT NOT NULL, `relayURL` TEXT NOT NULL, `isRecommendedRelay` INTEGER NOT NULL, `level` INTEGER NOT NULL, `synchronizationFilters` TEXT NOT NULL, `nostrEventId` TEXT, `unsignedNostrEventId` INTEGER, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`unsignedNostrEventId`) REFERENCES `UnsignedNostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "purpose", - "columnName": "purpose", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "status", - "columnName": "status", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "relayURL", - "columnName": "relayURL", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isRecommendedRelay", - "columnName": "isRecommendedRelay", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "level", - "columnName": "level", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "synchronizationFilters", - "columnName": "synchronizationFilters", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT" - }, - { - "fieldPath": "unsignedNostrEventId", - "columnName": "unsignedNostrEventId", - "affinity": "INTEGER" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_SynchronizeNostrEventRequest_nostrEventId", - "unique": false, - "columnNames": [ - "nostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_SynchronizeNostrEventRequest_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)" - }, - { - "name": "index_SynchronizeNostrEventRequest_status", - "unique": false, - "columnNames": [ - "status" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_SynchronizeNostrEventRequest_status` ON `${TABLE_NAME}` (`status`)" - } - ], - "foreignKeys": [ - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "nostrEventId" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "UnsignedNostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "unsignedNostrEventId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "SynchronizeNostrEventResult", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `relayURL` TEXT NOT NULL, `nostrEventId` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "relayURL", - "columnName": "relayURL", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_SynchronizeNostrEventResult_nostrEventId", - "unique": false, - "columnNames": [ - "nostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_SynchronizeNostrEventResult_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)" - } - ], - "foreignKeys": [ - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "nostrEventId" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "UnsignedNostrEvent", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `pubKey` TEXT NOT NULL, `kind` INTEGER NOT NULL, `tags` TEXT NOT NULL, `privateTags` TEXT, `content` TEXT NOT NULL, `signedAt` INTEGER, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "pubKey", - "columnName": "pubKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "kind", - "columnName": "kind", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "tags", - "columnName": "tags", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateTags", - "columnName": "privateTags", - "affinity": "TEXT" - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "signedAt", - "columnName": "signedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "broadcastedAt", - "columnName": "broadcastedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_UnsignedNostrEvent_pubKey", - "unique": false, - "columnNames": [ - "pubKey" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_UnsignedNostrEvent_pubKey` ON `${TABLE_NAME}` (`pubKey`)" - }, - { - "name": "index_UnsignedNostrEvent_kind", - "unique": false, - "columnNames": [ - "kind" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_UnsignedNostrEvent_kind` ON `${TABLE_NAME}` (`kind`)" - } - ] - }, - { - "tableName": "Zap", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `postId` TEXT NOT NULL, `senderPublicKey` TEXT NOT NULL, `receiverPublicKey` TEXT NOT NULL, `message` TEXT, `invoice` TEXT, `amountInMillisatoshis` INTEGER, `nostrEventId` 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(`senderPublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`receiverPublicKey`) 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(`postId`) REFERENCES `Post`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "postId", - "columnName": "postId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderPublicKey", - "columnName": "senderPublicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "receiverPublicKey", - "columnName": "receiverPublicKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "message", - "columnName": "message", - "affinity": "TEXT" - }, - { - "fieldPath": "invoice", - "columnName": "invoice", - "affinity": "TEXT" - }, - { - "fieldPath": "amountInMillisatoshis", - "columnName": "amountInMillisatoshis", - "affinity": "INTEGER" - }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "savedAt", - "columnName": "savedAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "viewedAt", - "columnName": "viewedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "broadcastedAt", - "columnName": "broadcastedAt", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_Zap_senderPublicKey", - "unique": false, - "columnNames": [ - "senderPublicKey" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Zap_senderPublicKey` ON `${TABLE_NAME}` (`senderPublicKey`)" - }, - { - "name": "index_Zap_nostrEventId", - "unique": false, - "columnNames": [ - "nostrEventId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Zap_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)" - }, - { - "name": "index_Zap_postId", - "unique": false, - "columnNames": [ - "postId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Zap_postId` ON `${TABLE_NAME}` (`postId`)" - } - ], - "foreignKeys": [ - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "senderPublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - }, - { - "table": "Profile", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "receiverPublicKey" - ], - "referencedColumns": [ - "publicKey" - ] - }, - { - "table": "NostrEvent", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "nostrEventId" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "Post", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "postId" - ], - "referencedColumns": [ - "id" - ] - } - ] - } - ], - "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, 'db402ac92a6774f569ca2c6e19a4b9a5')" - ] - } -} \ 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 ddcc03dd..25a67d0b 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 @@ -8,7 +8,7 @@ import ac.cord.auxiliary.compose.database.dao.ChatRoomDao import ac.cord.auxiliary.compose.database.dao.ConnectionDao import ac.cord.auxiliary.compose.database.dao.GiftWrapMessageDao import ac.cord.auxiliary.compose.database.dao.GiftWrapSealDao -import ac.cord.auxiliary.compose.database.dao.GiftWrapUnsignedDao +import ac.cord.auxiliary.compose.database.dao.GiftWrapPayloadDao import ac.cord.auxiliary.compose.database.dao.InReplyToRelationDao import ac.cord.auxiliary.compose.database.dao.MentionDao import ac.cord.auxiliary.compose.database.dao.NegentropySynchronizeRequestDao @@ -34,7 +34,7 @@ import ac.cord.auxiliary.compose.database.model.ChatRoom import ac.cord.auxiliary.compose.database.model.Connection import ac.cord.auxiliary.compose.database.model.GiftWrapMessage import ac.cord.auxiliary.compose.database.model.GiftWrapSeal -import ac.cord.auxiliary.compose.database.model.GiftWrapUnsigned +import ac.cord.auxiliary.compose.database.model.GiftWrapPayload import ac.cord.auxiliary.compose.database.model.InReplyToRelation import ac.cord.auxiliary.compose.database.model.Mention import ac.cord.auxiliary.compose.database.model.NegentropySynchronizeRequest @@ -72,7 +72,7 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L) InReplyToRelation::class, GiftWrapMessage::class, GiftWrapSeal::class, - GiftWrapUnsigned::class, + GiftWrapPayload::class, Mention::class, NegentropySynchronizeRequest::class, NegentropySynchronizeResult::class, @@ -90,13 +90,7 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L) UnsignedNostrEvent::class, Zap::class ], - version = 2, - autoMigrations = [ - AutoMigration( - from = 1, - to = 2 - ) - ] + version = 1 ) @TypeConverters(AuxConverters::class) abstract class AuxDatabase: RoomDatabase() { @@ -113,7 +107,7 @@ abstract class AuxDatabase: RoomDatabase() { abstract fun giftWrapSealDao(): GiftWrapSealDao - abstract fun giftWrapUnsignedDao(): GiftWrapUnsignedDao + abstract fun giftWrapUnsignedDao(): GiftWrapPayloadDao abstract fun inReplyToRelationDao(): InReplyToRelationDao diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/ChatRoomDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/ChatRoomDao.kt index 35ee47a6..5fb5a2b7 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/ChatRoomDao.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/ChatRoomDao.kt @@ -2,6 +2,7 @@ package ac.cord.auxiliary.compose.database.dao import ac.cord.auxiliary.compose.database.model.ChatRoom import ac.cord.auxiliary.compose.database.model.Relay +import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom import androidx.room3.Dao import androidx.room3.Query import androidx.room3.Upsert @@ -10,9 +11,11 @@ import kotlinx.coroutines.flow.Flow @Dao interface ChatRoomDao { + @Query("SELECT * FROM ChatRoom WHERE id = :id") + fun findChatRoomById(id: String): ChatRoom? @Query("SELECT * FROM ChatRoom WHERE userPublicKey = :userPublicKey") - fun observeChatRoomByUserPublicKey(userPublicKey: String): Flow> + fun observeChatRoomByUserPublicKey(userPublicKey: String): Flow> @Upsert suspend fun upsert(chatRoom: ChatRoom) diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapPayloadDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapPayloadDao.kt new file mode 100644 index 00000000..649e2ef6 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapPayloadDao.kt @@ -0,0 +1,16 @@ +package ac.cord.auxiliary.compose.database.dao + +import ac.cord.auxiliary.compose.database.model.GiftWrapPayload +import androidx.room3.Dao +import androidx.room3.Query +import androidx.room3.Upsert + +@Dao +interface GiftWrapPayloadDao { + @Query("SELECT * FROM GiftWrapPayload") + fun getAllGiftWrapUnsigned(): List + + @Upsert + suspend fun upsert(giftWrapPayload: GiftWrapPayload) + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapUnsignedDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapUnsignedDao.kt deleted file mode 100644 index 7e49e52f..00000000 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapUnsignedDao.kt +++ /dev/null @@ -1,19 +0,0 @@ -package ac.cord.auxiliary.compose.database.dao - -import ac.cord.auxiliary.compose.database.model.GiftWrapUnsigned -import ac.cord.auxiliary.compose.database.model.Relay -import androidx.room3.Dao -import androidx.room3.Query -import androidx.room3.Upsert -import kotlinx.coroutines.flow.Flow - -@Dao -interface GiftWrapUnsignedDao { - @Query("SELECT * FROM GiftWrapUnsigned") - fun getAllGiftWrapUnsigned(): List - - - @Upsert - suspend fun upsert(giftWrapUnsigned: GiftWrapUnsigned) - -} \ 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 88aa7329..ace670a9 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 @@ -3,6 +3,8 @@ package ac.cord.auxiliary.compose.database.dao import ac.cord.auxiliary.compose.database.AuxDatabase import ac.cord.auxiliary.compose.database.GENESIS_AT import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventRequest +import ac.cord.auxiliary.compose.database.model.ChatMessage +import ac.cord.auxiliary.compose.database.model.ChatRoom import ac.cord.auxiliary.compose.database.model.Connection import ac.cord.auxiliary.compose.database.model.Mention import ac.cord.auxiliary.compose.database.model.NegentropySynchronizeRequest @@ -10,24 +12,21 @@ import ac.cord.auxiliary.compose.database.model.NostrEvent import ac.cord.auxiliary.compose.database.model.NostrEventRelay import ac.cord.auxiliary.compose.database.model.Post import ac.cord.auxiliary.compose.database.model.Profile -import ac.cord.auxiliary.compose.database.model.SynchronizeNostrEventRequest import ac.cord.auxiliary.compose.database.model.UnsignedNostrEvent import ac.cord.auxiliary.compose.database.model.types.SynchronizationFilter -import ac.cord.auxiliary.compose.network.serialization.encodeToJsonString -import ac.cord.auxiliary.compose.nostr.Relays +import ac.cord.auxiliary.compose.managers.SeedManager +import ac.cord.auxiliary.compose.managers.toHex import androidx.room3.Dao import androidx.room3.Transaction -import androidx.room3.useWriterConnection import co.touchlab.kermit.Logger 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.nip18Reposts.quotes.QAddressableTag -import com.vitorpamplona.quartz.nip18Reposts.quotes.QEventTag -import com.vitorpamplona.quartz.nip18Reposts.quotes.firstTaggedQuote -import kotlin.math.ceil +import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent +import fr.acinq.bitcoin.crypto.musig2.Musig2 import kotlin.time.Clock +import kotlin.time.Instant @Dao abstract class NostrDao( @@ -124,7 +123,7 @@ abstract class NostrDao( } else { // Nothing else needs to be done val profile = database.profileDao().getProfileByPublicKey(nostrEvent.pubKey) - if (profile?.createdAt == GENESIS_AT) { + if (profile?.createdAt == GENESIS_AT && nostrEvent.kind == MetadataEvent.KIND) { logger.i("This is a placeholder profile might need to get synced...: ${profile.publicKey}") // Setup the sync here... } else { @@ -158,13 +157,13 @@ abstract class NostrDao( val profilePublicKeysToSync = mutableMapOf>() val eventIdsToSync = mutableMapOf>() - if (nostrEvent.unsignedNostrEventId == null) { + if (nostrEvent.unsignedNostrEventId == null && nostrEvent.kind != GiftWrapEvent.KIND) { // 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( - displayName = "LOADING PROFILE...", + displayName = "LOADING...", publicKey = nostrEvent.pubKey, createdAt = GENESIS_AT, nostrEventId = nostrEvent.id, // Will get overwriting by sync, @@ -185,6 +184,67 @@ abstract class NostrDao( logger.w("We somehow have an unsignedNostrEvent: $nostrEvent") } + // Index reposts first because they have the contained posts... + nostrEvent.toRepostedRelationWithContainedPost()?.let { (repostedRelation, containedPost) -> + // Find or create placeHolder note that is being reposted + val repostedNostrEvent = database.nostrEventDao().getNostrEventById(repostedRelation.repostedNostrEventId) + + val repostedProfilePublicKey = if (repostedNostrEvent == null) { + // Sync event... + if (eventIdsToSync[relayURL] == null) { + eventIdsToSync[relayURL] = mutableSetOf() + } + eventIdsToSync[relayURL]?.add(repostedRelation.repostedNostrEventId) + + database.nostrEventDao().insert( + if (containedPost != null) { + NostrEvent( + id = containedPost.id, + content = containedPost.content, + createdAt = Instant.fromEpochSeconds(containedPost.createdAt), + pubKey = containedPost.pubKey, + sig = containedPost.sig, + kind = containedPost.kind, + tags = containedPost.tags + ) + } else { + NostrEvent( + id = repostedRelation.repostedNostrEventId, + content = "Pending Sync", + createdAt = GENESIS_AT, + pubKey = nostrEvent.pubKey, + sig = "", + kind = nostrEvent.kind, + tags = emptyArray() + ) + } + ) + + repostedRelation.repostedProfilePublicKey + } else { + repostedRelation.repostedProfilePublicKey ?: repostedNostrEvent.pubKey + } + + val repostedProfile = repostedProfilePublicKey?.let { database.profileDao().getProfileByPublicKey(it) } + + if (repostedProfilePublicKey != null && repostedProfile == null) { + database.profileDao().insertPlaceholderProfile( + Profile( + displayName = "LOADING...", + publicKey = repostedProfilePublicKey, + createdAt = GENESIS_AT, + nostrEventId = nostrEvent.id, // Will get overwriting by sync, + ) + ) + } + database.repostedRelationDao().upsert( + repostedRelation.copy( + repostedProfilePublicKey = repostedProfilePublicKey + ) + ) + } + + // Sync tagged events and authors... nostrEvent.tags.taggedEvents().take(4).forEach { taggedEvent -> // We only look at the first 3 tagged events... val taggedNostrEvent = database.nostrEventDao().getNostrEventById(taggedEvent.eventId) @@ -227,7 +287,7 @@ abstract class NostrDao( // Save a placeholder database.profileDao().insertPlaceholderProfile( Profile( - displayName = "LOADING PROFILE...", + displayName = "LOADING...", publicKey = taggedUser.pubKey, createdAt = GENESIS_AT, nostrEventId = nostrEvent.id, // Will get overwriting by sync, @@ -243,11 +303,10 @@ abstract class NostrDao( ) ) if (recommendedRelay != null) { - if (profilePublicKeysToSync[relayURL] == null) { - profilePublicKeysToSync[relayURL] = mutableSetOf() + if (profilePublicKeysToSync[recommendedRelay] == null) { + profilePublicKeysToSync[recommendedRelay] = mutableSetOf() } - profilePublicKeysToSync[relayURL]?.add(taggedUser.pubKey) - + profilePublicKeysToSync[recommendedRelay]?.add(taggedUser.pubKey) } else { if (profilePublicKeysToSync[relayURL] == null) { profilePublicKeysToSync[relayURL] = mutableSetOf() @@ -262,6 +321,100 @@ abstract class NostrDao( database.profileDao().upsert(profile) } + nostrEvent.toGiftWrapMessageWithReceiverPTag()?.let { giftWrapMessage -> + val giftWrapReceiverProfile = database.profileDao().getProfileByPublicKey(giftWrapMessage.receiverPublicKey) + + if (giftWrapReceiverProfile == null) { + // Save a placeholder + database.profileDao().insertPlaceholderProfile( + Profile( + displayName = "LOADING...", + publicKey = giftWrapMessage.receiverPublicKey, + createdAt = GENESIS_AT, + nostrEventId = nostrEvent.id, // Will get overwriting by sync, + ) + ) + + val recommendRelayUrl = giftWrapMessage.receiverRelayHit + + if (recommendRelayUrl != null) { + if (profilePublicKeysToSync[recommendRelayUrl] == null) { + profilePublicKeysToSync[recommendRelayUrl] = mutableSetOf() + } + profilePublicKeysToSync[recommendRelayUrl]?.add(giftWrapMessage.receiverPublicKey) + } else { + if (profilePublicKeysToSync[relayURL] == null) { + profilePublicKeysToSync[relayURL] = mutableSetOf() + } + profilePublicKeysToSync[relayURL]?.add( + giftWrapMessage.receiverPublicKey + ) + } + } + + + database.giftWrapMessageDao().upsert( + giftWrapMessage + ) + + // TODO: Might want to process this somewhere else where privateKey is available... + SeedManager.activeKeyPair().privKey?.let { activeUserPrivateKey -> + giftWrapMessage.decryptGiftWrapSeal( + activeUserPrivateKey + )?.let { giftWrapSeal -> + database.giftWrapSealDao().upsert( + giftWrapSeal + ) + + giftWrapSeal.decryptGiftWrapPayload( + activeUserPrivateKey + )?.let { giftWrapPayload -> + + database.giftWrapUnsignedDao().upsert( + giftWrapPayload + ) + + val chatRoomId = Musig2.aggregateKeys( + giftWrapPayload.receiverPublicKeys() + ).value.toHex() + // Process chatMessage and chatRoom stuff... + // Find or create chatRoom + val chatRoom = database.chatRoomDao().findChatRoomById( + chatRoomId + ) + + if (chatRoom == null) { + database.chatRoomDao().upsert( + ChatRoom( + id = chatRoomId, + userPublicKey = SeedManager.activeKeyPair().pubKey.toHex(), + subject = null, // TODO: Get subject from tags... + createdAt = giftWrapPayload.createdAt, + initialGiftWrapUnsignedId = giftWrapPayload.id + ) + ) + } else { + // TODO: Update subject from tags... + } + + // Save chatMessage + database.chatMessageDao().upsert( + ChatMessage( + id = giftWrapPayload.id, + senderPublicKey = giftWrapPayload.publicKey, + isUserMessage = SeedManager.activePublicKey().toHex() == giftWrapPayload.publicKey, + chatRoomId = chatRoomId, + createdAt = giftWrapPayload.createdAt, + content = giftWrapPayload.content, + ) + ) + + } + } + } + + } + nostrEvent.toPost()?.let { post -> if (post.replyToId != null) { // Find or create placeHolder note that is replyTo @@ -334,7 +487,7 @@ abstract class NostrDao( if (quotedProfilePublicKey != null && quotedProfile == null) { database.profileDao().insertPlaceholderProfile( Profile( - displayName = "LOADING PROFILE...", + displayName = "LOADING...", publicKey = quotedProfilePublicKey, createdAt = GENESIS_AT, nostrEventId = nostrEvent.id, // Will get overwriting by sync, @@ -349,52 +502,6 @@ abstract class NostrDao( ) ) } - 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... - if (eventIdsToSync[relayURL] == null) { - eventIdsToSync[relayURL] = mutableSetOf() - } - eventIdsToSync[relayURL]?.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() - ) - ) - - repostedRelation.repostedProfilePublicKey - } else { - repostedRelation.repostedProfilePublicKey ?: repostedNostrEvent.pubKey - } - - val repostedProfile = repostedProfilePublicKey?.let { database.profileDao().getProfileByPublicKey(it) } - - if (repostedProfilePublicKey != null && repostedProfile == null) { - database.profileDao().insertPlaceholderProfile( - Profile( - displayName = "LOADING 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 -> @@ -442,7 +549,7 @@ abstract class NostrDao( if (inReplyToRootProfilePublicKey != null && inReplyToProfile == null) { database.profileDao().insertPlaceholderProfile( Profile( - displayName = "LOADING PROFILE...", + displayName = "LOADING...", publicKey = inReplyToRootProfilePublicKey, createdAt = GENESIS_AT, nostrEventId = nostrEvent.id, // Will get overwriting by sync, @@ -502,7 +609,7 @@ abstract class NostrDao( if (profile == null) { val placeHolderProfile = Profile( - displayName = "LOADING PROFILE...", + displayName = "LOADING...", publicKey = followedHexKEys, createdAt = GENESIS_AT, nostrEventId = nostrEvent.id, // Will get overwriting by sync, diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/ChatMessage.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/ChatMessage.kt index fb2ac8df..72f25ab6 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/ChatMessage.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/ChatMessage.kt @@ -8,6 +8,7 @@ import androidx.room3.Entity import androidx.room3.ForeignKey import androidx.room3.PrimaryKey import com.vitorpamplona.quartz.nip01Core.core.HexKey +import kotlin.time.Clock import kotlin.time.Instant @Entity( @@ -19,9 +20,9 @@ import kotlin.time.Instant onDelete = ForeignKey.CASCADE, ), ForeignKey( - entity = GiftWrapUnsigned::class, + entity = GiftWrapPayload::class, parentColumns = ["id"], - childColumns = ["giftWrapUnsignedId"], + childColumns = ["id"], onDelete = ForeignKey.CASCADE, ), ], @@ -35,17 +36,15 @@ data class ChatMessage( val chatRoomId: String, - val giftWrapUnsignedId: String, - - val replyToEventId: String?, - val quotedEventId: String?, + val replyToEventId: String? = null, + val quotedEventId: String? = null, val content: String, - override val createdAt: Instant, - override val updatedAt: Instant, - override val savedAt: Instant, - override val viewedAt: Instant?, - override val deletedAt: Instant?, // Who this message should be seen by (used to derive conversationId) + 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, // Who this message should be seen by (used to derive conversationId) ): TimestampedEntity, LocalStoreEntity, UserViewableEntity, SoftDeletableEntity { } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/ChatRoom.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/ChatRoom.kt index 3dabf29c..aa127d1d 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/ChatRoom.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/ChatRoom.kt @@ -20,7 +20,7 @@ import kotlin.time.Instant onDelete = ForeignKey.CASCADE, ), ForeignKey( - entity = GiftWrapUnsigned::class, + entity = GiftWrapPayload::class, parentColumns = ["id"], childColumns = ["initialGiftWrapUnsignedId"], onDelete = ForeignKey.CASCADE, @@ -50,11 +50,6 @@ data class ChatRoom( */ val subject: String?, - /** - * Should probably be the human-readable name of whichever participant is not the user... - */ - val name: String?, - /** * Derived from kind14/15 tags * [ diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapMessage.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapMessage.kt index 49d89fec..fd1fb340 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapMessage.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapMessage.kt @@ -11,6 +11,15 @@ import androidx.room3.PrimaryKey import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Kind import com.vitorpamplona.quartz.nip01Core.core.TagArray +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl +import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal +import com.vitorpamplona.quartz.nip01Core.tags.people.PTag +import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent +import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent +import fr.acinq.bitcoin.PrivateKey +import kotlin.time.Clock import kotlin.time.Instant @Entity( @@ -33,12 +42,19 @@ data class GiftWrapMessage( val publicKey: String, val kind: Kind = 1059, + /** + * Receiver Public Key Derived from tags + * * [ * ["p", receiverPublicKey, ""] // receiver * ] + * */ - val tags: Array>, + val receiverPublicKey: String, + + val receiverRelayHit: String?, + /** * * encrypt(GiftWrapSeal, randomPrivateKey, receiverPublicKey) @@ -57,47 +73,50 @@ data class GiftWrapMessage( /** * randomTimeUpTo2DaysInThePast */ - override val createdAt: Instant, - override val updatedAt: Instant, - override val savedAt: Instant, - override val viewedAt: Instant?, - override val deletedAt: Instant?, // Who this message should be seen by (used to derive conversationId) + 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, // Who this message should be seen by (used to derive conversationId) ): NostrEventEntity, TimestampedEntity, LocalStoreEntity, UserViewableEntity, SoftDeletableEntity { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class != other::class) return false - other as GiftWrapMessage + suspend fun decryptGiftWrapSeal( + privateKey: ByteArray + ): GiftWrapSeal? { + if (kind == GiftWrapEvent.KIND) { + val giftWrapEvent = GiftWrapEvent( + id = id, + pubKey = publicKey, + tags = arrayOf( + PTag( + pubKey = receiverPublicKey, + relayHint = receiverRelayHit?.let { NormalizedRelayUrl(it) } + ).toTagArray() + ), + createdAt = createdAt.epochSeconds, + content = content, + sig = signature + ) - if (kind != other.kind) return false - if (id != other.id) return false - if (publicKey != other.publicKey) return false - if (!tags.contentDeepEquals(other.tags)) return false - if (content != other.content) return false - if (signature != other.signature) return false - if (nostrEventId != other.nostrEventId) 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 + val nostrSigner = NostrSignerInternal( + keyPair = KeyPair(privKey = privateKey) + ) - return true - } + giftWrapEvent.unwrapOrNull( + nostrSigner + )?.let { giftWrapSeal -> + return GiftWrapSeal( + id = giftWrapSeal.id, + publicKey = giftWrapSeal.pubKey, + content = giftWrapSeal.content, + tags = giftWrapSeal.tags, + createdAt = Instant.fromEpochSeconds(giftWrapSeal.createdAt), + signature = giftWrapSeal.sig + ) + } + } - override fun hashCode(): Int { - var result = kind - result = 31 * result + id.hashCode() - result = 31 * result + publicKey.hashCode() - result = 31 * result + tags.contentDeepHashCode() - result = 31 * result + content.hashCode() - result = 31 * result + signature.hashCode() - result = 31 * result + nostrEventId.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) - return result + + return null } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapUnsigned.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapPayload.kt similarity index 78% rename from composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapUnsigned.kt rename to composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapPayload.kt index c2cc7b41..427602de 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapUnsigned.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapPayload.kt @@ -9,6 +9,9 @@ import androidx.room3.ForeignKey import androidx.room3.PrimaryKey import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Kind +import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers +import fr.acinq.bitcoin.PublicKey +import kotlin.time.Clock import kotlin.time.Instant @Entity( @@ -21,7 +24,7 @@ import kotlin.time.Instant ), ], ) -data class GiftWrapUnsigned( +data class GiftWrapPayload( // TODO: Rename this to GiftWrapPayload... @PrimaryKey val id: HexKey, @@ -31,9 +34,9 @@ data class GiftWrapUnsigned( val publicKey: String, /** - * Chat Message Kind + * Payload Event Kind */ - val kind: Kind = 14, + val kind: Kind, /** * Tags for the unsigned giftwrap @@ -54,7 +57,7 @@ data class GiftWrapUnsigned( * content MUST be plain text. Fields id and created_at are required. */ val content: String, - val quotedEventId: String?, + val quotedEventId: String? = null, /** * Associated GiftWrapSeal @@ -64,17 +67,28 @@ data class GiftWrapUnsigned( /** * Current time */ - override val createdAt: Instant, - override val updatedAt: Instant, - override val savedAt: Instant, - override val viewedAt: Instant?, - override val deletedAt: Instant?, // Who this message should be seen by (used to derive conversationId) + 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, // Who this message should be seen by (used to derive conversationId) ): TimestampedEntity, LocalStoreEntity, UserViewableEntity, SoftDeletableEntity { + + fun receiverPublicKeys(): List = try { + tags.taggedUsers().map { + PublicKey( + it.pubKey.hexToByteArray() + ) + } + } catch (e: Throwable) { + return emptyList() + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || this::class != other::class) return false - other as GiftWrapUnsigned + other as GiftWrapPayload if (kind != other.kind) return false if (id != other.id) return false diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapSeal.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapSeal.kt index 40e03a14..bfe95913 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapSeal.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapSeal.kt @@ -9,7 +9,10 @@ import androidx.room3.ForeignKey import androidx.room3.PrimaryKey import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Kind -import com.vitorpamplona.quartz.nip01Core.core.TagArray +import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair +import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal +import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent +import kotlin.time.Clock import kotlin.time.Instant @Entity( @@ -56,12 +59,48 @@ data class GiftWrapSeal( /** * randomTimeUpTo2DaysInThePast() */ - override val createdAt: Instant, - override val updatedAt: Instant, - override val savedAt: Instant, - override val viewedAt: Instant?, - override val deletedAt: Instant?, // Who this message should be seen by (used to derive conversationId) + 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, // Who this message should be seen by (used to derive conversationId) ): TimestampedEntity, LocalStoreEntity, UserViewableEntity, SoftDeletableEntity { + + suspend fun decryptGiftWrapPayload( + privateKey: ByteArray + ): GiftWrapPayload? { + if (kind == SealedRumorEvent.KIND) { + val sealedRumorEvent = SealedRumorEvent( + id = id, + pubKey = publicKey, + tags = tags, + createdAt = createdAt.epochSeconds, + content = content, + sig = signature + ) + + val nostrSigner = NostrSignerInternal( + keyPair = KeyPair(privKey = privateKey) + ) + + sealedRumorEvent.unsealOrNull( + nostrSigner + )?.let { giftWrapPayload -> + return GiftWrapPayload( + id = giftWrapPayload.id, + publicKey = giftWrapPayload.pubKey, + kind = giftWrapPayload.kind, + tags = giftWrapPayload.tags, + content = giftWrapPayload.content, + createdAt = Instant.fromEpochSeconds(giftWrapPayload.createdAt), + ) + } + + } + + return null + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || this::class != other::class) return false 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 47be292c..50101ffc 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,7 +5,6 @@ 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 ac.cord.auxiliary.compose.ui.composable.widgets.content.ContentParser import ac.cord.auxiliary.compose.ui.composable.widgets.content.ContentSegment import androidx.room3.Entity @@ -16,10 +15,10 @@ import co.touchlab.kermit.Logger import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey 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.events.taggedEvents +import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip18Reposts.RepostEvent import com.vitorpamplona.quartz.nip18Reposts.quotes.QAddressableTag @@ -27,6 +26,7 @@ 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.nip59Giftwrap.wraps.GiftWrapEvent import com.vitorpamplona.quartz.utils.EventFactory import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonObject @@ -180,6 +180,40 @@ data class NostrEvent( return null } + fun toGiftWrapMessageWithReceiverPTag(): GiftWrapMessage? = try { + if (kind == GiftWrapEvent.KIND) { + val giftWrapEvent = GiftWrapEvent( + id = id, + pubKey = pubKey, + createdAt = createdAt.epochSeconds, + kind = kind, + tags = tags, + content = content, + sig = sig + ) + + + + return giftWrapEvent.taggedUsers().firstOrNull()?.let { receiverPTag -> + GiftWrapMessage( + id = giftWrapEvent.id, + kind = giftWrapEvent.kind, + publicKey = giftWrapEvent.pubKey, + content = giftWrapEvent.content, + createdAt = Instant.fromEpochSeconds(giftWrapEvent.createdAt), + receiverPublicKey = receiverPTag.pubKey, + receiverRelayHit = receiverPTag.relayHint?.url, + signature = giftWrapEvent.sig, + nostrEventId = id, + ) + } + + } + return null + } catch (e: Throwable) { + return null + } + fun toReaction(): Reaction? = try { if (kind == ReactionEvent.KIND) { val reactionEvent = EventFactory.create( @@ -212,7 +246,7 @@ data class NostrEvent( return null } - fun toRepostedRelation(): RepostedRelation? = try { + fun toRepostedRelationWithContainedPost(): Pair? = try { if (kind == RepostEvent.KIND) { val repostEvent = EventFactory.create( id = id, @@ -224,16 +258,37 @@ data class NostrEvent( sig = sig ) - return repostEvent.containedPost()?.let { containedPost -> - RepostedRelation( - repostedNostrEventId = containedPost.id, - repostedProfilePublicKey = containedPost.pubKey, + return repostEvent.containedPost().let { containedPost -> + if (containedPost != null) { + Pair( + RepostedRelation( + repostedNostrEventId = containedPost.id, + repostedProfilePublicKey = containedPost.pubKey, - repostingNostrEventId = repostEvent.id, + repostingNostrEventId = repostEvent.id, - createdAt = Instant.fromEpochSeconds(repostEvent.createdAt), - updatedAt = Instant.fromEpochSeconds(repostEvent.createdAt), - ) + createdAt = Instant.fromEpochSeconds(repostEvent.createdAt), + updatedAt = Instant.fromEpochSeconds(repostEvent.createdAt), + ), + containedPost + ) + } else { + repostEvent.boostedEvent()?.let { eTag -> + Pair( + RepostedRelation( + repostedNostrEventId = eTag.eventId, + repostedProfilePublicKey = eTag.author, + + repostingNostrEventId = repostEvent.id, + + createdAt = Instant.fromEpochSeconds(repostEvent.createdAt), + updatedAt = Instant.fromEpochSeconds(repostEvent.createdAt), + ), + null + ) + } + + } } } diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalChatRoom.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalChatRoom.kt new file mode 100644 index 00000000..88f4b804 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalChatRoom.kt @@ -0,0 +1,11 @@ +package ac.cord.auxiliary.compose.database.model.intermdiate + +import ac.cord.auxiliary.compose.database.model.ChatRoom +import ac.cord.auxiliary.compose.database.model.Post +import ac.cord.auxiliary.compose.database.model.Profile +import androidx.room3.Embedded +import androidx.room3.Relation + +data class LocalChatRoom( + @Embedded val chatRoom: ChatRoom, +) diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/repository/DatabaseNostrRepository.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/repository/DatabaseNostrRepository.kt index 64d49d05..c08cf0c8 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/repository/DatabaseNostrRepository.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/repository/DatabaseNostrRepository.kt @@ -15,6 +15,7 @@ import ac.cord.auxiliary.compose.database.model.UnsignedNostrEvent import ac.cord.auxiliary.compose.database.model.intermdiate.LocalBroadcastNostrEventRequest import ac.cord.auxiliary.compose.database.model.intermdiate.LocalNostrEvent import ac.cord.auxiliary.compose.database.model.intermdiate.LocalAccount +import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfile import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfileWithFollowers import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfileWithFollowing @@ -682,6 +683,10 @@ class DatabaseNostrRepository( return database.profileDao().observeProfileWithFollowingByPublicKey(publicKey) } + override suspend fun observeChatRoomsByPublicKey(publicKey: String): Flow> { + return database.chatRoomDao().observeChatRoomByUserPublicKey(publicKey) + } + override suspend fun getRecentSearches(): List { return database.recentSearchDao().getAllRecentSearches() } diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/repository/NostrRepository.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/repository/NostrRepository.kt index e027b66c..fdbb71e0 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/repository/NostrRepository.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/repository/NostrRepository.kt @@ -12,6 +12,7 @@ import ac.cord.auxiliary.compose.database.model.UnsignedNostrEvent import ac.cord.auxiliary.compose.database.model.intermdiate.LocalBroadcastNostrEventRequest import ac.cord.auxiliary.compose.database.model.intermdiate.LocalNostrEvent import ac.cord.auxiliary.compose.database.model.intermdiate.LocalAccount +import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfile import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfileWithFollowers import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfileWithFollowing @@ -122,6 +123,8 @@ interface NostrRepository { suspend fun observeProfileWithFollowing(publicKey: String): Flow + suspend fun observeChatRoomsByPublicKey(publicKey: String): Flow> + suspend fun getRecentSearches(): List suspend fun removeRecentSearch(recentSearch: RecentSearch) @@ -289,6 +292,10 @@ interface NostrRepository { TODO("Not yet implemented") } + override suspend fun observeChatRoomsByPublicKey(publicKey: String): Flow> { + TODO("Not yet implemented") + } + override suspend fun getRecentSearches(): List { TODO("Not yet implemented") } diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/ChatRoomListViewModel.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/ChatRoomListViewModel.kt index c765cd8d..4e378e13 100755 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/ChatRoomListViewModel.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/ChatRoomListViewModel.kt @@ -8,7 +8,6 @@ import ac.cord.auxiliary.compose.repository.NostrRepository import ac.cord.auxiliary.compose.ui.view.state.ChatRoomListUIState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -49,51 +48,20 @@ class ChatRoomListViewModel( var chatRoomListUIState: ChatRoomListUIState by mutableStateOf(initialChatRoomListUIState) private set - val isSynchronizationPending: MutableState = mutableStateOf(false) - fun initiate() { logger.d("init") -// scheduleSynchronization() - observeLocalNostrFeed() -// observeSynchronizeNostrEventRequestByPurposeAndStatusCount() + scheduleSynchronization() + observeChatRoomFeed() } - fun observeLocalNostrFeed() { - logger.d("observeLocalNostrFeed") + + fun observeChatRoomFeed() { + logger.d("observeChatRoomFeed") viewModelScope.launch(Dispatchers.IO) { - nostrRepository.observeProfileWithFollowing( + nostrRepository.observeChatRoomsByPublicKey( publicKey ).distinctUntilChanged().collect { chatRoomListUIState = ChatRoomListUIState.Loaded( - chatRoomList = listOf( - ChatRoom( - id ="steveBikoId", - userPublicKey = "steveBikoId", - name = "Steve Biko", - subject = "BC: Systems", - initialGiftWrapUnsignedId = "", - ), - ChatRoom( - id = "chrisHaniId", - userPublicKey = "chrisHaniId", - name = "Chris Hani", - subject = "MK: Gym", - initialGiftWrapUnsignedId = "", - ), - ChatRoom( - id = "desmondTutuId", - userPublicKey = "desmondTutuId", - name = "Desmond Tutu", - subject = "TRC: Masses", - initialGiftWrapUnsignedId = "", - ), - ChatRoom( - id = "hipHopPantsulaId", - userPublicKey = "hipHopPantsulaId", - name = "Hip Hop Pantsula", - subject = "Motswako: Jabba", - initialGiftWrapUnsignedId = "", - ) - ) + chatRoomList = it ) } } @@ -110,7 +78,7 @@ class ChatRoomListViewModel( relayURL = normalizedRelayUrl.url, synchronizationFilter = synchronizationFilter ), - purpose = "following", + purpose = "chat", synchronizationFilter = synchronizationFilter, relayURL = normalizedRelayUrl.url, level = 0 @@ -120,16 +88,6 @@ class ChatRoomListViewModel( } } - fun observeSynchronizeNostrEventRequestByPurposeAndStatusCount() { - logger.d("observeSynchronizeNostrEventRequestByPurposeAndStatusCount") - viewModelScope.launch(Dispatchers.IO) { - nostrRepository.observeSynchronizeNostrEventRequestByPurposeAndStatusCount("feed").distinctUntilChanged().collect { count -> - logger.d("Pending/Processing Synchronization Request: $count") - isSynchronizationPending.value = count > 0 - } - } - } - @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun RenderFeed( @@ -177,8 +135,8 @@ class ChatRoomListViewModel( items( items = chatRoomListUIState.chatRoomList, - key = { it.id } - ) { chatRoom -> + key = { it.chatRoom.id } + ) { localChatRoom -> Card( modifier = Modifier.fillMaxWidth(), onClick = { @@ -190,16 +148,13 @@ class ChatRoomListViewModel( horizontalAlignment = Alignment.CenterHorizontally, ) { Text( - text = chatRoom.name ?: chatRoom.subject ?: "Unnamed chat" + text = localChatRoom.chatRoom.subject ?: "Unnamed chat" ) } - } } } } - - } } ChatRoomListUIState.Loading -> { @@ -226,7 +181,7 @@ class ChatRoomListViewModel( } companion object { - const val TAG = "MessageListViewModel" + const val TAG = "ChatRoomListViewModel" fun factory( initialChatRoomListUIState: ChatRoomListUIState = ChatRoomListUIState.Loading, diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/HomeViewModel.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/HomeViewModel.kt index 8ee29388..13f1d890 100755 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/HomeViewModel.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/HomeViewModel.kt @@ -23,6 +23,7 @@ import com.vitorpamplona.quartz.nip18Reposts.RepostEvent import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent +import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent import com.vitorpamplona.quartz.nipB7Blossom.BlossomServersEvent @@ -116,11 +117,11 @@ class HomeViewModel( } HomeScreenType.Messages -> { SynchronizationFilter( - authors = arrayOf( - publicKey - ), kinds = arrayOf( - ContactListEvent.KIND, + GiftWrapEvent.KIND, + ), + tags = mapOf( + Pair("p", listOf(publicKey)) ), limit = 50 ) diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/state/ChatRoomListUIState.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/state/ChatRoomListUIState.kt index 4c1bfda3..14942d9f 100755 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/state/ChatRoomListUIState.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/state/ChatRoomListUIState.kt @@ -1,10 +1,10 @@ package ac.cord.auxiliary.compose.ui.view.state -import ac.cord.auxiliary.compose.database.model.ChatRoom +import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom sealed interface ChatRoomListUIState { data class Loaded( - val chatRoomList: List + val chatRoomList: List ): ChatRoomListUIState data object Error: ChatRoomListUIState