From b604751ab4b9b82e28df651647cedab8dccfe1cf Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Mon, 18 May 2026 16:19:36 +0200 Subject: [PATCH] Add chat database models --- .../2.json | 2556 +++++++++++++++++ .../auxiliary/compose/database/AuxDatabase.kt | 35 +- .../compose/database/dao/ChatMessageDao.kt | 17 + .../compose/database/dao/ChatRoomDao.kt | 20 + .../database/dao/GiftWrapMessageDao.kt | 18 + .../compose/database/dao/GiftWrapSealDao.kt | 18 + .../database/dao/GiftWrapUnsignedDao.kt | 19 + .../compose/database/model/ChatMessage.kt | 51 + .../compose/database/model/ChatRoom.kt | 79 + .../compose/database/model/GiftWrapMessage.kt | 103 + .../compose/database/model/GiftWrapSeal.kt | 102 + .../database/model/GiftWrapUnsigned.kt | 110 + .../compose/database/model/Participant.kt | 52 + .../model/intermdiate/LocalParticipant.kt | 16 + 14 files changed, 3195 insertions(+), 1 deletion(-) create mode 100644 composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/2.json create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/ChatMessageDao.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/ChatRoomDao.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapMessageDao.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapSealDao.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapUnsignedDao.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/ChatMessage.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/ChatRoom.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapMessage.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapSeal.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapUnsigned.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/Participant.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalParticipant.kt diff --git a/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/2.json b/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/2.json new file mode 100644 index 00000000..e59cb3f7 --- /dev/null +++ b/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/2.json @@ -0,0 +1,2556 @@ +{ + "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 89a822cd..ddcc03dd 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 @@ -3,7 +3,12 @@ package ac.cord.auxiliary.compose.database import ac.cord.auxiliary.compose.database.converters.AuxConverters import ac.cord.auxiliary.compose.database.dao.BroadcastNostrEventReceiptDao import ac.cord.auxiliary.compose.database.dao.BroadcastNostrEventRequestDao +import ac.cord.auxiliary.compose.database.dao.ChatMessageDao +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.InReplyToRelationDao import ac.cord.auxiliary.compose.database.dao.MentionDao import ac.cord.auxiliary.compose.database.dao.NegentropySynchronizeRequestDao @@ -24,7 +29,12 @@ import ac.cord.auxiliary.compose.database.dao.UnsignedNostrEventDao import ac.cord.auxiliary.compose.database.dao.ZapDao import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventReceipt import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventRequest +import ac.cord.auxiliary.compose.database.model.ChatMessage +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.InReplyToRelation import ac.cord.auxiliary.compose.database.model.Mention import ac.cord.auxiliary.compose.database.model.NegentropySynchronizeRequest @@ -42,6 +52,7 @@ import ac.cord.auxiliary.compose.database.model.SynchronizeNostrEventRequest import ac.cord.auxiliary.compose.database.model.SynchronizeNostrEventResult import ac.cord.auxiliary.compose.database.model.UnsignedNostrEvent import ac.cord.auxiliary.compose.database.model.Zap +import androidx.room3.AutoMigration import androidx.room3.Database import androidx.room3.RoomDatabase import androidx.room3.TypeConverters @@ -56,7 +67,12 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L) BroadcastNostrEventReceipt::class, BroadcastNostrEventRequest::class, Connection::class, + ChatMessage::class, + ChatRoom::class, InReplyToRelation::class, + GiftWrapMessage::class, + GiftWrapSeal::class, + GiftWrapUnsigned::class, Mention::class, NegentropySynchronizeRequest::class, NegentropySynchronizeResult::class, @@ -74,14 +90,31 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L) UnsignedNostrEvent::class, Zap::class ], - version = 1 + version = 2, + autoMigrations = [ + AutoMigration( + from = 1, + to = 2 + ) + ] ) @TypeConverters(AuxConverters::class) abstract class AuxDatabase: RoomDatabase() { abstract fun broadcastNostrEventReceiptDao(): BroadcastNostrEventReceiptDao abstract fun broadcastNostrEventRequestDao(): BroadcastNostrEventRequestDao + + abstract fun chatMessageDao(): ChatMessageDao + + abstract fun chatRoomDao(): ChatRoomDao + abstract fun connectionDao(): ConnectionDao + abstract fun giftWrapMessageDao(): GiftWrapMessageDao + + abstract fun giftWrapSealDao(): GiftWrapSealDao + + abstract fun giftWrapUnsignedDao(): GiftWrapUnsignedDao + abstract fun inReplyToRelationDao(): InReplyToRelationDao abstract fun mentionDao(): MentionDao diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/ChatMessageDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/ChatMessageDao.kt new file mode 100644 index 00000000..5f34999c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/ChatMessageDao.kt @@ -0,0 +1,17 @@ +package ac.cord.auxiliary.compose.database.dao + +import ac.cord.auxiliary.compose.database.model.ChatMessage +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 ChatMessageDao { + @Query("SELECT * FROM ChatMessage WHERE chatRoomId = :chatRoomId") + fun observeChatMessagesByChatRoomId(chatRoomId: String): Flow> + + @Upsert + suspend fun upsert(chatMessage: ChatMessage) +} \ No newline at end of file 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 new file mode 100644 index 00000000..35ee47a6 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/ChatRoomDao.kt @@ -0,0 +1,20 @@ +package ac.cord.auxiliary.compose.database.dao + +import ac.cord.auxiliary.compose.database.model.ChatRoom +import ac.cord.auxiliary.compose.database.model.Relay +import androidx.room3.Dao +import androidx.room3.Query +import androidx.room3.Upsert +import fr.acinq.bitcoin.PublicKey +import kotlinx.coroutines.flow.Flow + +@Dao +interface ChatRoomDao { + + @Query("SELECT * FROM ChatRoom WHERE userPublicKey = :userPublicKey") + fun observeChatRoomByUserPublicKey(userPublicKey: String): Flow> + + @Upsert + suspend fun upsert(chatRoom: ChatRoom) + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapMessageDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapMessageDao.kt new file mode 100644 index 00000000..03da6ac1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapMessageDao.kt @@ -0,0 +1,18 @@ +package ac.cord.auxiliary.compose.database.dao + +import ac.cord.auxiliary.compose.database.model.GiftWrapMessage +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 GiftWrapMessageDao { + @Query("SELECT * FROM GiftWrapMessage") + fun getAllGiftWrapMessages(): List + + @Upsert + suspend fun upsert(giftWrapMessage: GiftWrapMessage) + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapSealDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapSealDao.kt new file mode 100644 index 00000000..7041176a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapSealDao.kt @@ -0,0 +1,18 @@ +package ac.cord.auxiliary.compose.database.dao + +import ac.cord.auxiliary.compose.database.model.GiftWrapSeal +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 GiftWrapSealDao { + @Query("SELECT * FROM GiftWrapSeal") + fun getAllGiftWrapSeals(): List + + @Upsert + suspend fun upsert(giftWrapSeal: GiftWrapSeal) + +} \ 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 new file mode 100644 index 00000000..7e49e52f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/GiftWrapUnsignedDao.kt @@ -0,0 +1,19 @@ +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/model/ChatMessage.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/ChatMessage.kt new file mode 100644 index 00000000..fb2ac8df --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/ChatMessage.kt @@ -0,0 +1,51 @@ +package ac.cord.auxiliary.compose.database.model + +import ac.cord.auxiliary.compose.database.model.traits.LocalStoreEntity +import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity +import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity +import ac.cord.auxiliary.compose.database.model.traits.UserViewableEntity +import androidx.room3.Entity +import androidx.room3.ForeignKey +import androidx.room3.PrimaryKey +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import kotlin.time.Instant + +@Entity( + foreignKeys = [ + ForeignKey( + entity = ChatRoom::class, + parentColumns = ["id"], + childColumns = ["chatRoomId"], + onDelete = ForeignKey.CASCADE, + ), + ForeignKey( + entity = GiftWrapUnsigned::class, + parentColumns = ["id"], + childColumns = ["giftWrapUnsignedId"], + onDelete = ForeignKey.CASCADE, + ), + ], +) +data class ChatMessage( + @PrimaryKey + val id: HexKey, + + val senderPublicKey: String, + val isUserMessage: Boolean, + + val chatRoomId: String, + + val giftWrapUnsignedId: String, + + val replyToEventId: String?, + val quotedEventId: String?, + + 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) +): 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 new file mode 100644 index 00000000..31260af1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/ChatRoom.kt @@ -0,0 +1,79 @@ +package ac.cord.auxiliary.compose.database.model + +import ac.cord.auxiliary.compose.database.model.traits.LocalStoreEntity +import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity +import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity +import ac.cord.auxiliary.compose.database.model.traits.UserViewableEntity +import androidx.room3.Entity +import androidx.room3.ForeignKey +import androidx.room3.PrimaryKey +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import kotlin.time.Instant + +@Entity( + foreignKeys = [ + ForeignKey( + entity = Profile::class, + parentColumns = ["publicKey"], + childColumns = ["userPublicKey"], + onDelete = ForeignKey.CASCADE, + ), + ForeignKey( + entity = GiftWrapUnsigned::class, + parentColumns = ["id"], + childColumns = ["initialGiftWrapUnsignedId"], + onDelete = ForeignKey.CASCADE, + ), + ], +) +data class ChatRoom( + /** + * Derived from the participants... + * + * can aggregate them into one id like musig(participants...) + */ + @PrimaryKey + val id: String, + + /** + * Logged in user PublicKey... should help us have multiple user support... + */ + val userPublicKey: HexKey, + + /** + * Optional retrieved from tags... + * + * Any member can change the topic by simply submitting a new subject to an existing pubkey + p tags room. + * + * There is no need to send subject in every message. The newest subject in the chat room is the subject of the conversation. + */ + 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 + * [ + * ["p", "", ""], + * ["p", "", ""], + * // rest of tags... + * ] + * + * TODO: Might want to add a relation for this... + */ + // TODO: Add a LastChatMessageRelation +// val lastChatMessageId: String? = null, + // Might want to focus on the other last messages... + val initialGiftWrapUnsignedId: String, + + override val createdAt: Instant, + override val updatedAt: Instant, + override val savedAt: Instant, + override val viewedAt: Instant?, + override val deletedAt: Instant?, + +): TimestampedEntity, LocalStoreEntity, UserViewableEntity, SoftDeletableEntity { +} \ No newline at end of file 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 new file mode 100644 index 00000000..49d89fec --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapMessage.kt @@ -0,0 +1,103 @@ +package ac.cord.auxiliary.compose.database.model + +import ac.cord.auxiliary.compose.database.model.traits.LocalStoreEntity +import ac.cord.auxiliary.compose.database.model.traits.NostrEventEntity +import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity +import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity +import ac.cord.auxiliary.compose.database.model.traits.UserViewableEntity +import androidx.room3.Entity +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 kotlin.time.Instant + +@Entity( + foreignKeys = [ + ForeignKey( + entity = NostrEvent::class, + parentColumns = ["id"], + childColumns = ["nostrEventId"], + onDelete = ForeignKey.CASCADE, + ), + ], +) +data class GiftWrapMessage( + @PrimaryKey + val id: HexKey, + + /** + * Random PublicKey (should it be derived from randomPrivateKey?) + */ + val publicKey: String, + + val kind: Kind = 1059, + /** + * [ + * ["p", receiverPublicKey, ""] // receiver + * ] + */ + val tags: Array>, + /** + * + * encrypt(GiftWrapSeal, randomPrivateKey, receiverPublicKey) + */ + val content: String, + /** + * Signed using the randomPrivateKey used to encrypt the GiftWrapSeal + */ + val signature: String, + + /** + * What gets broadcasts? + */ + override val nostrEventId: String, + + /** + * 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) +): 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 + + 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 + + return true + } + + 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 + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..40e03a14 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapSeal.kt @@ -0,0 +1,102 @@ +package ac.cord.auxiliary.compose.database.model + +import ac.cord.auxiliary.compose.database.model.traits.LocalStoreEntity +import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity +import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity +import ac.cord.auxiliary.compose.database.model.traits.UserViewableEntity +import androidx.room3.Entity +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 kotlin.time.Instant + +@Entity( + foreignKeys = [ + ForeignKey( + entity = GiftWrapMessage::class, + parentColumns = ["id"], + childColumns = ["giftWrapMessageId"], + onDelete = ForeignKey.CASCADE, + ), + + ], +) +data class GiftWrapSeal( + @PrimaryKey + val id: HexKey, + /** + * senderPublicKey + */ + val publicKey: String, + + val kind: Kind = 13, + /** + * No tags + */ + val tags: Array> = emptyArray(), + + /** + * + * nip44Encrypt(unsignedKind14, senderPrivateKey, receiverPublicKey) + */ + val content: String, + + /** + * signed by senderPrivateKey + */ + val signature: String, + + /** + * Associated GiftWrapMessage + */ + val giftWrapMessageId: String? = null, + + /** + * 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) +): 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 GiftWrapSeal + + 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 (giftWrapMessageId != other.giftWrapMessageId) 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 + + return true + } + + 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 + (giftWrapMessageId?.hashCode() ?: 0) + 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 + } +} \ 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/GiftWrapUnsigned.kt new file mode 100644 index 00000000..c2cc7b41 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/GiftWrapUnsigned.kt @@ -0,0 +1,110 @@ +package ac.cord.auxiliary.compose.database.model + +import ac.cord.auxiliary.compose.database.model.traits.LocalStoreEntity +import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity +import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity +import ac.cord.auxiliary.compose.database.model.traits.UserViewableEntity +import androidx.room3.Entity +import androidx.room3.ForeignKey +import androidx.room3.PrimaryKey +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.Kind +import kotlin.time.Instant + +@Entity( + foreignKeys = [ + ForeignKey( + entity = GiftWrapSeal::class, + parentColumns = ["id"], + childColumns = ["giftWrapSealId"], + onDelete = ForeignKey.CASCADE, + ), + ], +) +data class GiftWrapUnsigned( + @PrimaryKey + val id: HexKey, + + /** + * Sender Public Key + */ + val publicKey: String, + + /** + * Chat Message Kind + */ + val kind: Kind = 14, + + /** + * Tags for the unsigned giftwrap + * + * [ + * ["p", "", ""], + * ["p", "", ""], + * ["e", "", ""] // if this is a reply + * ["subject", ""], + * // rest of tags... + * ] + * + * q tags MAY be used when citing events in the .content with NIP-21. + */ + val tags: Array>, + + /** + * content MUST be plain text. Fields id and created_at are required. + */ + val content: String, + val quotedEventId: String?, + + /** + * Associated GiftWrapSeal + */ + val giftWrapSealId: String? = null, + + /** + * 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) +): 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 GiftWrapUnsigned + + 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 (quotedEventId != other.quotedEventId) return false + if (giftWrapSealId != other.giftWrapSealId) 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 + + return true + } + + 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 + (quotedEventId?.hashCode() ?: 0) + result = 31 * result + (giftWrapSealId?.hashCode() ?: 0) + 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 + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/Participant.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/Participant.kt new file mode 100644 index 00000000..ea6ce089 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/Participant.kt @@ -0,0 +1,52 @@ +package ac.cord.auxiliary.compose.database.model + +import ac.cord.auxiliary.compose.database.model.traits.BroadcastableEntity +import ac.cord.auxiliary.compose.database.model.traits.LocalStoreEntity +import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity +import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity +import 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 + +/** + * Derived from kind14/15 tags + * [ + * ["p", "", ""], + * ["p", "", ""], + * // rest of tags... + * ] + * + * TODO: Might want to add a relation for this... + */ +@Entity( + foreignKeys = [ + ForeignKey( + entity = Profile::class, + parentColumns = ["publicKey"], + childColumns = ["participantPublicKey"], + onDelete = ForeignKey.CASCADE, + ), + ForeignKey( + entity = ChatRoom::class, + parentColumns = ["id"], + childColumns = ["chatRoomId"], + onDelete = ForeignKey.CASCADE, + ), + ] +) +data class Participant( + @PrimaryKey(autoGenerate = true) + val id: Long = 0, + + val participantPublicKey: HexKey, + val chatRoomId: String, + + override val createdAt: Instant = Clock.System.now(), + override val updatedAt: Instant = Clock.System.now(), + override val savedAt: Instant = Clock.System.now(), + override val deletedAt: Instant? = null, + override val broadcastedAt: Instant? = null, +): TimestampedEntity, LocalStoreEntity, BroadcastableEntity, SoftDeletableEntity diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalParticipant.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalParticipant.kt new file mode 100644 index 00000000..16782776 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalParticipant.kt @@ -0,0 +1,16 @@ +package ac.cord.auxiliary.compose.database.model.intermdiate + +import ac.cord.auxiliary.compose.database.model.Participant +import ac.cord.auxiliary.compose.database.model.Profile +import androidx.room3.Embedded +import androidx.room3.Relation + +data class LocalParticipant( + @Embedded val participant: Participant, + + @Relation( + parentColumn = "participantPublicKey", + entityColumn = "publicKey" + ) + val profile: Profile?, +)