Make room for a database.
This commit is contained in:
@@ -7,4 +7,5 @@ plugins {
|
||||
alias(libs.plugins.composeMultiplatform) apply false
|
||||
alias(libs.plugins.composeCompiler) apply false
|
||||
alias(libs.plugins.kotlinMultiplatform) apply false
|
||||
alias(libs.plugins.ksp) apply false
|
||||
}
|
||||
@@ -2,12 +2,14 @@ import org.jetbrains.compose.desktop.application.dsl.TargetFormat
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.androidApplication)
|
||||
alias(libs.plugins.androidx.room)
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.composeMultiplatform)
|
||||
alias(libs.plugins.composeCompiler)
|
||||
alias(libs.plugins.composeHotReload)
|
||||
alias(libs.plugins.kotlinPluginSerialization)
|
||||
alias(libs.plugins.ksp)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@@ -34,6 +36,7 @@ kotlin {
|
||||
implementation(libs.compose.uiToolingPreview)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.okhttp.coroutines)
|
||||
implementation(libs.androidx.room.sqlite.wrapper)
|
||||
}
|
||||
commonMain.dependencies {
|
||||
implementation(libs.compose.runtime)
|
||||
@@ -47,6 +50,11 @@ kotlin {
|
||||
implementation(libs.androidx.lifecycle.viewmodelCompose)
|
||||
implementation(libs.androidx.lifecycle.runtimeCompose)
|
||||
|
||||
implementation("androidx.paging:paging-common:3.5.0-alpha01")
|
||||
implementation("androidx.paging:paging-compose:3.5.0-alpha01")
|
||||
implementation(libs.androidx.room.runtime)
|
||||
implementation(libs.androidx.sqlite.bundled)
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0")
|
||||
implementation(libs.navigation.compose)
|
||||
|
||||
@@ -98,6 +106,16 @@ android {
|
||||
|
||||
dependencies {
|
||||
debugImplementation(libs.compose.uiTooling)
|
||||
add("kspAndroid", libs.androidx.room.compiler)
|
||||
add("kspIosSimulatorArm64", libs.androidx.room.compiler)
|
||||
// add("kspIosX64", libs.androidx.room.compiler)
|
||||
add("kspIosArm64", libs.androidx.room.compiler)
|
||||
add("kspJvm", libs.androidx.room.compiler)
|
||||
// Add any other platform target you use in your project, for example kspDesktop
|
||||
}
|
||||
|
||||
room {
|
||||
schemaDirectory("$projectDir/schemas")
|
||||
}
|
||||
|
||||
compose.desktop {
|
||||
|
||||
701
composeApp/schemas/ac.aux.compose.database.AuxDatabase/1.json
Normal file
701
composeApp/schemas/ac.aux.compose.database.AuxDatabase/1.json
Normal file
@@ -0,0 +1,701 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "9a4e13a505370c8fd3433ef4241cbcc8",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "NostrEvent",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `pubKey` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `broadcastedAt` INTEGER NOT NULL, `kind` INTEGER NOT NULL, `tags` TEXT NOT NULL, `content` TEXT NOT NULL, `sig` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pubKey",
|
||||
"columnName": "pubKey",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "createdAt",
|
||||
"columnName": "createdAt",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "savedAt",
|
||||
"columnName": "savedAt",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "broadcastedAt",
|
||||
"columnName": "broadcastedAt",
|
||||
"affinity": "INTEGER",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "Post",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `replyToId` TEXT, `repostId` TEXT, `quote` TEXT, `profilePublicKey` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `broadcastedAt` INTEGER NOT NULL, `content` TEXT NOT NULL, 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": "nostrEventId",
|
||||
"columnName": "nostrEventId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "replyToId",
|
||||
"columnName": "replyToId",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "repostId",
|
||||
"columnName": "repostId",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "quote",
|
||||
"columnName": "quote",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "profilePublicKey",
|
||||
"columnName": "profilePublicKey",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "createdAt",
|
||||
"columnName": "createdAt",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "savedAt",
|
||||
"columnName": "savedAt",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "broadcastedAt",
|
||||
"columnName": "broadcastedAt",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "content",
|
||||
"columnName": "content",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"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, `nostrEventId` 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, 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": "nostrEventId",
|
||||
"columnName": "nostrEventId",
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"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": "Reaction",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `replyToId` TEXT NOT NULL, `profilePublicKey` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `content` TEXT NOT NULL, 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": "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": "content",
|
||||
"columnName": "content",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"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": "Repost",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `postId` TEXT, `profilePublicKey` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `content` TEXT NOT NULL, 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(`postId`) REFERENCES `Post`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "nostrEventId",
|
||||
"columnName": "nostrEventId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "postId",
|
||||
"columnName": "postId",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "profilePublicKey",
|
||||
"columnName": "profilePublicKey",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "createdAt",
|
||||
"columnName": "createdAt",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "content",
|
||||
"columnName": "content",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_Repost_profilePublicKey",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"profilePublicKey"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_Repost_profilePublicKey` ON `${TABLE_NAME}` (`profilePublicKey`)"
|
||||
},
|
||||
{
|
||||
"name": "index_Repost_nostrEventId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"nostrEventId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_Repost_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)"
|
||||
},
|
||||
{
|
||||
"name": "index_Repost_postId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"postId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_Repost_postId` ON `${TABLE_NAME}` (`postId`)"
|
||||
}
|
||||
],
|
||||
"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": [
|
||||
"postId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "Zap",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `postId` TEXT NOT NULL, `senderPublicKey` TEXT NOT NULL, `receiverPublicKey` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `message` TEXT, `invoice` TEXT, 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": "nostrEventId",
|
||||
"columnName": "nostrEventId",
|
||||
"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": "createdAt",
|
||||
"columnName": "createdAt",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "message",
|
||||
"columnName": "message",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "invoice",
|
||||
"columnName": "invoice",
|
||||
"affinity": "TEXT"
|
||||
}
|
||||
],
|
||||
"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, '9a4e13a505370c8fd3433ef4241cbcc8')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package ac.aux.compose.database
|
||||
|
||||
import ac.aux.compose.database.converters.Converters
|
||||
import ac.aux.compose.database.dao.NostrDao
|
||||
import ac.aux.compose.database.dao.NostrEventDao
|
||||
import ac.aux.compose.database.dao.PostDao
|
||||
import ac.aux.compose.database.dao.ProfileDao
|
||||
import ac.aux.compose.database.dao.ReactionDao
|
||||
import ac.aux.compose.database.dao.RepostDao
|
||||
import ac.aux.compose.database.dao.ZapDao
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import ac.aux.compose.database.model.Reaction
|
||||
import ac.aux.compose.database.model.Repost
|
||||
import ac.aux.compose.database.model.Zap
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
NostrEvent::class,
|
||||
Post::class,
|
||||
Profile::class,
|
||||
Reaction::class,
|
||||
Repost::class,
|
||||
Zap::class
|
||||
],
|
||||
version = 1
|
||||
)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class AuxDatabase: RoomDatabase() {
|
||||
abstract fun nostrDao(): NostrDao
|
||||
|
||||
abstract fun nostrEventDao(): NostrEventDao
|
||||
abstract fun postDao(): PostDao
|
||||
abstract fun profileDao(): ProfileDao
|
||||
abstract fun reactionDao(): ReactionDao
|
||||
abstract fun repostDao(): RepostDao
|
||||
abstract fun zapDao(): ZapDao
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package ac.aux.compose.database.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.time.Instant
|
||||
|
||||
class Converters {
|
||||
@TypeConverter
|
||||
fun fromTimestamp(value: Long?): Instant? {
|
||||
return value?.let { Instant.fromEpochMilliseconds(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun instantToTimestamp(instant: Instant?): Long? {
|
||||
return instant?.toEpochMilliseconds()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromTagArray(value: TagArray?): String? {
|
||||
// Returns null if the value is null, otherwise serializes to JSON string
|
||||
return value?.let { Json.encodeToString(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toTagArray(value: String?): TagArray? {
|
||||
// Decodes the JSON string back into the nested array structure
|
||||
return value?.let { Json.decodeFromString<TagArray>(it) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.AuxDatabase
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Transaction
|
||||
|
||||
@Dao
|
||||
abstract class NostrDao(
|
||||
private val database: AuxDatabase
|
||||
) {
|
||||
@Transaction
|
||||
open suspend fun insertPostAndNostrEvent(post: Post, nostrEvent: NostrEvent) {
|
||||
database.nostrEventDao().insert(nostrEvent)
|
||||
database.postDao().insert(post) // TODO: Make use of the nostrEventId...
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface NostrEventDao {
|
||||
@Query("SELECT * FROM NostrEvent")
|
||||
fun getAllNostrEvents(): List<NostrEvent>
|
||||
|
||||
@Query("SELECT * FROM NostrEvent WHERE id = :id")
|
||||
fun getNostrEventById(id: String): Flow<NostrEvent?>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(nostrEvent: NostrEvent)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.Relation
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface PostDao {
|
||||
@Query("SELECT * FROM Post")
|
||||
fun getAllPosts(): List<PostAndProfile>
|
||||
|
||||
@Query("SELECT * FROM Post WHERE id = :id")
|
||||
fun getPostById(id: String): Flow<Post?>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(post: Post)
|
||||
|
||||
data class PostAndProfile(
|
||||
@Embedded val post: Post,
|
||||
@Relation(
|
||||
parentColumn = "profilePublicKey",
|
||||
entityColumn = "publicKey"
|
||||
)
|
||||
val profile: Profile
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface ProfileDao {
|
||||
@Query("SELECT * FROM Profile")
|
||||
fun getAllPosts(): List<Profile>
|
||||
|
||||
@Query("SELECT * FROM Profile WHERE publicKey = :publicKey")
|
||||
fun getProfileByPublicKey(publicKey: String): Flow<Profile?>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(profile: Profile)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import ac.aux.compose.database.model.Reaction
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface ReactionDao {
|
||||
@Query("SELECT * FROM Reaction")
|
||||
fun getAllReactions(): List<Reaction>
|
||||
|
||||
@Query("SELECT * FROM Reaction WHERE id = :id")
|
||||
fun getReactionById(id: String): Flow<Reaction?>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(reaction: Reaction)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.database.model.Repost
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface RepostDao {
|
||||
@Query("SELECT * FROM Repost")
|
||||
fun getAllReposts(): List<Repost>
|
||||
|
||||
@Query("SELECT * FROM Repost WHERE id = :id")
|
||||
fun getRepostById(id: String): Flow<Repost?>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(repost: Repost)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ac.aux.compose.database.dao
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.database.model.Zap
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface ZapDao {
|
||||
@Query("SELECT * FROM Zap WHERE id = :id")
|
||||
fun getZapById(id: String): Flow<Zap?>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(zap: Zap)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Fts4
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Kind
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Entity
|
||||
data class NostrEvent(
|
||||
@PrimaryKey
|
||||
val id: String,
|
||||
val pubKey: HexKey,
|
||||
val createdAt: Instant,
|
||||
val savedAt: Instant,
|
||||
val broadcastedAt: Instant,
|
||||
val kind: Kind,
|
||||
val tags: TagArray,
|
||||
val content: String,
|
||||
val sig: HexKey,
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || this::class != other::class) return false
|
||||
|
||||
other as NostrEvent
|
||||
|
||||
if (kind != other.kind) return false
|
||||
if (id != other.id) return false
|
||||
if (pubKey != other.pubKey) return false
|
||||
if (createdAt != other.createdAt) return false
|
||||
if (!tags.contentDeepEquals(other.tags)) return false
|
||||
if (content != other.content) return false
|
||||
if (sig != other.sig) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = kind
|
||||
result = 31 * result + id.hashCode()
|
||||
result = 31 * result + pubKey.hashCode()
|
||||
result = 31 * result + createdAt.hashCode()
|
||||
result = 31 * result + tags.contentDeepHashCode()
|
||||
result = 31 * result + content.hashCode()
|
||||
result = 31 * result + sig.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Kind
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = Profile::class,
|
||||
parentColumns = ["publicKey"],
|
||||
childColumns = ["profilePublicKey"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// This might cause problems... but we gonna make the feed so cool if it works...
|
||||
ForeignKey(
|
||||
entity = NostrEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["nostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// Support post replies
|
||||
ForeignKey(
|
||||
entity = Post::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["replyToId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// Support reposts
|
||||
ForeignKey(
|
||||
entity = Post::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["repostId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
indices = [
|
||||
Index("profilePublicKey"),
|
||||
Index("nostrEventId"),
|
||||
Index("replyToId"),
|
||||
Index("repostId")
|
||||
],
|
||||
)
|
||||
data class Post(
|
||||
@PrimaryKey
|
||||
val id: HexKey,
|
||||
val nostrEventId: HexKey, // Might be a replaceable nostr event?
|
||||
val replyToId: HexKey? = null,
|
||||
val repostId: HexKey? = null,
|
||||
val quote: HexKey? = null,
|
||||
val profilePublicKey: HexKey,
|
||||
val createdAt: Instant,
|
||||
val savedAt: Instant,
|
||||
val broadcastedAt: Instant,
|
||||
val content: String,
|
||||
) {
|
||||
companion object {
|
||||
val DUMMY_POSTS = arrayOf(
|
||||
Post(
|
||||
id = "genesisnoteid",
|
||||
nostrEventId = "genesisnoteid", // Need to check how to handle addressableEvents...
|
||||
profilePublicKey = "satoshi",
|
||||
createdAt = Clock.System.now(),
|
||||
savedAt = Clock.System.now(),
|
||||
broadcastedAt = Clock.System.now(),
|
||||
content = "Chancellor the rapper...",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = NostrEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["nostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
)
|
||||
],
|
||||
indices = [
|
||||
Index("nostrEventId"),
|
||||
],
|
||||
)
|
||||
data class Profile(
|
||||
@PrimaryKey
|
||||
val publicKey: String,
|
||||
val nostrEventId: String,
|
||||
val userName: String? = null, // name in the nostr event...
|
||||
val displayName: String? = null,
|
||||
val picture: String? = null,
|
||||
val banner: String? = null,
|
||||
val website: String? = null,
|
||||
val about: String? = null,
|
||||
val bot: Boolean? = null,
|
||||
val pronouns: String? = null,
|
||||
val nip05: String? = null,
|
||||
val domain: String? = null,
|
||||
val lud06: String? = null,
|
||||
val lud16: String? = null,
|
||||
val twitter: String? = null
|
||||
) {
|
||||
companion object {
|
||||
val DUMMAY_PROFILES = arrayOf<Profile>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Kind
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = Profile::class,
|
||||
parentColumns = ["publicKey"],
|
||||
childColumns = ["profilePublicKey"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// This might cause problems... but we gonna make the feed so cool if it works...
|
||||
ForeignKey(
|
||||
entity = NostrEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["nostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// Support post replies
|
||||
ForeignKey(
|
||||
entity = Post::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["replyToId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
indices = [
|
||||
Index("profilePublicKey"),
|
||||
Index("nostrEventId"),
|
||||
Index("replyToId")
|
||||
],
|
||||
)
|
||||
data class Reaction(
|
||||
@PrimaryKey
|
||||
val id: HexKey,
|
||||
val nostrEventId: HexKey, // Might be a replaceable nostr event?
|
||||
val replyToId: HexKey,
|
||||
val profilePublicKey: HexKey,
|
||||
val createdAt: Instant,
|
||||
val content: String,
|
||||
)
|
||||
@@ -0,0 +1,50 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Kind
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = Profile::class,
|
||||
parentColumns = ["publicKey"],
|
||||
childColumns = ["profilePublicKey"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// This might cause problems... but we gonna make the feed so cool if it works...
|
||||
ForeignKey(
|
||||
entity = NostrEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["nostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// Support post replies
|
||||
ForeignKey(
|
||||
entity = Post::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["postId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
indices = [
|
||||
Index("profilePublicKey"),
|
||||
Index("nostrEventId"),
|
||||
Index("postId")
|
||||
],
|
||||
)
|
||||
data class Repost(
|
||||
@PrimaryKey
|
||||
val id: HexKey,
|
||||
val nostrEventId: HexKey, // Might be a replaceable nostr event?
|
||||
val postId: HexKey? = null,
|
||||
val profilePublicKey: HexKey,
|
||||
val createdAt: Instant,
|
||||
val content: String,
|
||||
)
|
||||
@@ -0,0 +1,55 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = Profile::class,
|
||||
parentColumns = ["publicKey"],
|
||||
childColumns = ["senderPublicKey"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
ForeignKey(
|
||||
entity = Profile::class,
|
||||
parentColumns = ["publicKey"],
|
||||
childColumns = ["receiverPublicKey"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// This might cause problems... but we gonna make the feed so cool if it works...
|
||||
ForeignKey(
|
||||
entity = NostrEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["nostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
// Support post replies
|
||||
ForeignKey(
|
||||
entity = Post::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["postId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
indices = [
|
||||
Index("senderPublicKey"),
|
||||
Index("nostrEventId"),
|
||||
Index("postId")
|
||||
],
|
||||
)
|
||||
data class Zap(
|
||||
@PrimaryKey
|
||||
val id: HexKey,
|
||||
val nostrEventId: HexKey, // Might be a replaceable nostr event?
|
||||
val postId: HexKey,
|
||||
val senderPublicKey: HexKey,
|
||||
val receiverPublicKey: HexKey,
|
||||
val createdAt: Instant,
|
||||
val message: String?,
|
||||
val invoice: String?,
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
package ac.aux.compose.database.repository
|
||||
|
||||
import ac.aux.compose.database.AuxDatabase
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
import ac.aux.compose.repository.PostRepository
|
||||
|
||||
class DatabasePostRepository(
|
||||
private val database: AuxDatabase
|
||||
): PostRepository {
|
||||
override suspend fun publishPost(
|
||||
post: Post,
|
||||
nostrEvent: NostrEvent
|
||||
) {
|
||||
val nostrDao = database.nostrDao()
|
||||
|
||||
nostrDao.insertPostAndNostrEvent(
|
||||
post,
|
||||
nostrEvent
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package ac.aux.compose.repository
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Post
|
||||
|
||||
interface PostRepository {
|
||||
suspend fun publishPost(post: Post, nostrEvent: NostrEvent)
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
package ac.aux.compose.ui.composable
|
||||
|
||||
import ac.aux.compose.ui.composable.widgets.LoadingDataIndicator
|
||||
import ac.aux.compose.ui.composable.widgets.feed.FeedListItem
|
||||
import ac.aux.compose.ui.composable.widgets.feed.ListView
|
||||
import ac.aux.compose.ui.composable.widgets.feed.LoadedFeedView
|
||||
import ac.aux.compose.ui.theme.AuxTheme
|
||||
import ac.aux.compose.ui.view.model.FeedListViewModel
|
||||
import ac.aux.compose.ui.view.state.FeedListUIState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -25,6 +26,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@@ -91,10 +93,23 @@ fun FeedScreen(
|
||||
Text("Something went wrong")
|
||||
}
|
||||
is FeedListUIState.Loaded -> {
|
||||
LoadedFeedView(
|
||||
events = feedListUIState.events,
|
||||
onNavigateToEvent = onNavigateToEvent
|
||||
)
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
items(
|
||||
items = feedListUIState.events,
|
||||
key = { e -> e.id }
|
||||
) { event ->
|
||||
FeedListItem(
|
||||
event = event,
|
||||
onNavigateToEvent = {
|
||||
onNavigateToEvent.invoke(event)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ac.aux.compose.ui.composable.widgets.feed
|
||||
import ac.aux.compose.ui.theme.AuxTheme
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
@@ -28,6 +29,9 @@ fun Event.ListView() {
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Row {
|
||||
|
||||
}
|
||||
Text(
|
||||
text = content,
|
||||
)
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package ac.aux.compose.ui.composable.widgets.feed
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
|
||||
@Composable
|
||||
fun LoadedFeedView(
|
||||
events: List<Event>,
|
||||
onNavigateToEvent: (Event) -> Unit
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
items(
|
||||
items = events,
|
||||
key = { e -> e.id }
|
||||
) { event ->
|
||||
FeedListItem(
|
||||
event = event,
|
||||
onNavigateToEvent = {
|
||||
onNavigateToEvent.invoke(event)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,14 @@ junit = "4.13.2"
|
||||
kermit = "2.0.8"
|
||||
kotlin = "2.3.0"
|
||||
kotlinx-coroutines = "1.10.2"
|
||||
ksp = "2.3.6"
|
||||
material3 = "1.10.0-alpha05"
|
||||
materialIconsCore = "1.7.3"
|
||||
materialIconsExtended = "1.7.3"
|
||||
navigationCompose = "2.9.2"
|
||||
okhttp = "5.3.2"
|
||||
room = "2.8.4"
|
||||
sqlite = "2.6.2"
|
||||
|
||||
[libraries]
|
||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||
@@ -30,6 +33,10 @@ androidx-testExt-junit = { module = "androidx.test.ext:junit", version.ref = "an
|
||||
androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" }
|
||||
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
|
||||
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
|
||||
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqlite" }
|
||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
||||
androidx-room-sqlite-wrapper = { module = "androidx.room:room-sqlite-wrapper", version.ref = "room" }
|
||||
compose-uiTooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "composeMultiplatform" }
|
||||
androidx-lifecycle-viewmodelCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
|
||||
androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
|
||||
@@ -49,9 +56,11 @@ okhttp-coroutines = { group = "com.squareup.okhttp3", name = "okhttp-coroutines"
|
||||
|
||||
[plugins]
|
||||
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||
androidx-room = { id = "androidx.room", version.ref = "room" }
|
||||
androidLibrary = { id = "com.android.library", version.ref = "agp" }
|
||||
composeHotReload = { id = "org.jetbrains.compose.hot-reload", version.ref = "composeHotReload" }
|
||||
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }
|
||||
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||
kotlinPluginSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
kotlinPluginSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
||||
Reference in New Issue
Block a user