From f8e7a20b984201b7598a414ed96a3a72016c09c5 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Wed, 25 Mar 2026 04:19:06 +0200 Subject: [PATCH] Account creation workflow... still need logic. --- .../1.json | 353 +++++++++++++++--- .../ac/aux/compose/database/AuxDatabase.kt | 9 + .../database/dao/UnsignedNostrEventDao.kt | 2 +- .../aux/compose/database/model/NostrEvent.kt | 4 +- .../repository/DatabaseNostrRepository.kt | 21 ++ .../aux/compose/repository/NostrRepository.kt | 7 + .../ui/composable/CreateProfileScreen.kt | 208 ++++++++++- .../ui/composable/SocialPreconditionScreen.kt | 2 +- .../ui/composable/navigation/AuxNavHost.kt | 37 +- .../navigation/routes/BlankRoute.kt | 6 + .../kotlin/ac/aux/compose/ui/theme/Color.kt | 3 + .../ui/view/model/CreateProfileViewModel.kt | 99 ++++- .../ui/view/state/CreateProfileUIState.kt | 21 ++ 13 files changed, 688 insertions(+), 84 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/ac/aux/compose/database/repository/DatabaseNostrRepository.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/aux/compose/repository/NostrRepository.kt create mode 100755 composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/navigation/routes/BlankRoute.kt diff --git a/composeApp/schemas/ac.aux.compose.database.AuxDatabase/1.json b/composeApp/schemas/ac.aux.compose.database.AuxDatabase/1.json index e28c4492..36debdc9 100644 --- a/composeApp/schemas/ac.aux.compose.database.AuxDatabase/1.json +++ b/composeApp/schemas/ac.aux.compose.database.AuxDatabase/1.json @@ -2,8 +2,151 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "7219ad348874b83380e4072dec332bee", + "identityHash": "61eb893d34ad04f95f8b007f8448ad7f", "entities": [ + { + "tableName": "BroadcastNostrEventReceipt", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nostrEventId` TEXT NOT NULL, `isAccepted` INTEGER NOT NULL, `relayURL` TEXT NOT NULL, `messages` TEXT, FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`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": "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" + ] + } + ] + }, + { + "tableName": "BroadcastNostrEventRequest", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nostrEventId` TEXT NOT NULL, `status` TEXT NOT NULL, `relayURL` TEXT NOT NULL, FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`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": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "relayURL", + "columnName": "relayURL", + "affinity": "TEXT", + "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" + ] + } + ] + }, { "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, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`id`))", @@ -82,7 +225,7 @@ }, { "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, `content` 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 )", + "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", @@ -90,6 +233,22 @@ "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", @@ -101,28 +260,12 @@ "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": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, { "fieldPath": "createdAt", "columnName": "createdAt", @@ -250,7 +393,7 @@ }, { "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, `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 )", + "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", @@ -258,12 +401,6 @@ "affinity": "TEXT", "notNull": true }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, { "fieldPath": "userName", "columnName": "userName", @@ -329,6 +466,12 @@ "columnName": "twitter", "affinity": "TEXT" }, + { + "fieldPath": "nostrEventId", + "columnName": "nostrEventId", + "affinity": "TEXT", + "notNull": true + }, { "fieldPath": "createdAt", "columnName": "createdAt", @@ -396,7 +539,7 @@ }, { "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, `content` 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 )", + "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", @@ -404,6 +547,12 @@ "affinity": "TEXT", "notNull": true }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, { "fieldPath": "nostrEventId", "columnName": "nostrEventId", @@ -422,12 +571,6 @@ "affinity": "TEXT", "notNull": true }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, { "fieldPath": "createdAt", "columnName": "createdAt", @@ -535,7 +678,7 @@ }, { "tableName": "Repost", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `postId` TEXT, `profilePublicKey` TEXT NOT NULL, `content` 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(`postId`) REFERENCES `Post`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `repostedPostId` TEXT, `content` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `profilePublicKey` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`profilePublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`repostedPostId`) REFERENCES `Post`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { "fieldPath": "id", @@ -543,29 +686,29 @@ "affinity": "TEXT", "notNull": true }, + { + "fieldPath": "repostedPostId", + "columnName": "repostedPostId", + "affinity": "TEXT" + }, + { + "fieldPath": "content", + "columnName": "content", + "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": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, { "fieldPath": "createdAt", "columnName": "createdAt", @@ -626,13 +769,13 @@ "createSql": "CREATE INDEX IF NOT EXISTS `index_Repost_nostrEventId` ON `${TABLE_NAME}` (`nostrEventId`)" }, { - "name": "index_Repost_postId", + "name": "index_Repost_repostedPostId", "unique": false, "columnNames": [ - "postId" + "repostedPostId" ], "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Repost_postId` ON `${TABLE_NAME}` (`postId`)" + "createSql": "CREATE INDEX IF NOT EXISTS `index_Repost_repostedPostId` ON `${TABLE_NAME}` (`repostedPostId`)" } ], "foreignKeys": [ @@ -663,7 +806,100 @@ "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "postId" + "repostedPostId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "UnsignedNostrEvent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `kind` INTEGER NOT NULL, `tags` TEXT NOT NULL, `content` TEXT NOT NULL, `signedNostrEventId` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, FOREIGN KEY(`signedNostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "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": "signedNostrEventId", + "columnName": "signedNostrEventId", + "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" + ] + }, + "indices": [ + { + "name": "index_UnsignedNostrEvent_signedNostrEventId", + "unique": false, + "columnNames": [ + "signedNostrEventId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_UnsignedNostrEvent_signedNostrEventId` ON `${TABLE_NAME}` (`signedNostrEventId`)" + } + ], + "foreignKeys": [ + { + "table": "NostrEvent", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "signedNostrEventId" ], "referencedColumns": [ "id" @@ -673,7 +909,7 @@ }, { "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, `message` TEXT, `invoice` TEXT, `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 )", + "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", @@ -681,12 +917,6 @@ "affinity": "TEXT", "notNull": true }, - { - "fieldPath": "nostrEventId", - "columnName": "nostrEventId", - "affinity": "TEXT", - "notNull": true - }, { "fieldPath": "postId", "columnName": "postId", @@ -715,6 +945,17 @@ "columnName": "invoice", "affinity": "TEXT" }, + { + "fieldPath": "amountInMillisatoshis", + "columnName": "amountInMillisatoshis", + "affinity": "INTEGER" + }, + { + "fieldPath": "nostrEventId", + "columnName": "nostrEventId", + "affinity": "TEXT", + "notNull": true + }, { "fieldPath": "createdAt", "columnName": "createdAt", @@ -834,7 +1075,7 @@ ], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7219ad348874b83380e4072dec332bee')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '61eb893d34ad04f95f8b007f8448ad7f')" ] } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/database/AuxDatabase.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/database/AuxDatabase.kt index d6552251..58ecc002 100644 --- a/composeApp/src/commonMain/kotlin/ac/aux/compose/database/AuxDatabase.kt +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/database/AuxDatabase.kt @@ -9,12 +9,16 @@ 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.UnsignedNostrEventDao import ac.aux.compose.database.dao.ZapDao +import ac.aux.compose.database.model.BroadcastNostrEventReceipt +import ac.aux.compose.database.model.BroadcastNostrEventRequest 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.UnsignedNostrEvent import ac.aux.compose.database.model.Zap import androidx.room.Database import androidx.room.RoomDatabase @@ -22,11 +26,14 @@ import androidx.room.TypeConverters @Database( entities = [ + BroadcastNostrEventReceipt::class, + BroadcastNostrEventRequest::class, NostrEvent::class, Post::class, Profile::class, Reaction::class, Repost::class, + UnsignedNostrEvent::class, Zap::class ], version = 1 @@ -42,5 +49,7 @@ abstract class AuxDatabase: RoomDatabase() { abstract fun profileDao(): ProfileDao abstract fun reactionDao(): ReactionDao abstract fun repostDao(): RepostDao + + abstract fun unsignedNostrEventDao(): UnsignedNostrEventDao abstract fun zapDao(): ZapDao } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/database/dao/UnsignedNostrEventDao.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/database/dao/UnsignedNostrEventDao.kt index 5519176f..662336fa 100644 --- a/composeApp/src/commonMain/kotlin/ac/aux/compose/database/dao/UnsignedNostrEventDao.kt +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/database/dao/UnsignedNostrEventDao.kt @@ -20,5 +20,5 @@ interface UnsignedNostrEventDao { fun getUnsignedNostrEventById(id: Long): Flow @Upsert - suspend fun upsert(unsignedNostrEvent: UnsignedNostrEvent) + suspend fun upsert(unsignedNostrEvent: UnsignedNostrEvent): Long } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/database/model/NostrEvent.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/database/model/NostrEvent.kt index f937957a..83600731 100644 --- a/composeApp/src/commonMain/kotlin/ac/aux/compose/database/model/NostrEvent.kt +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/database/model/NostrEvent.kt @@ -5,6 +5,7 @@ import ac.aux.compose.database.model.traits.LocalStoreEntity import ac.aux.compose.database.model.traits.SoftDeletableEntity import ac.aux.compose.database.model.traits.TimestampedEntity import androidx.room.Entity +import androidx.room.Ignore import androidx.room.PrimaryKey import co.touchlab.kermit.Logger import com.vitorpamplona.quartz.nip01Core.core.HexKey @@ -36,7 +37,8 @@ data class NostrEvent( override val deletedAt: Instant? = null, override val broadcastedAt: Instant? = null, ): TimestampedEntity, LocalStoreEntity, BroadcastableEntity, SoftDeletableEntity { - val logger = Logger.withTag("NostrEvent") + @Ignore + private val logger = Logger.withTag("NostrEvent") override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/database/repository/DatabaseNostrRepository.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/database/repository/DatabaseNostrRepository.kt new file mode 100644 index 00000000..47a3a79a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/database/repository/DatabaseNostrRepository.kt @@ -0,0 +1,21 @@ +package ac.aux.compose.database.repository + +import ac.aux.compose.database.AuxDatabase +import ac.aux.compose.database.model.UnsignedNostrEvent +import ac.aux.compose.repository.NostrRepository +import kotlinx.coroutines.delay + +class DatabaseNostrRepository( + private val database: AuxDatabase +): NostrRepository { + + override suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent? = try { + delay(2_100) // Just to make it look like we are doing serious work... + unsignedNostrEvent.copy( + id = database.unsignedNostrEventDao().upsert(unsignedNostrEvent) + ) + } catch (e: Throwable) { + null + } + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/repository/NostrRepository.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/repository/NostrRepository.kt new file mode 100644 index 00000000..cdad6677 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/repository/NostrRepository.kt @@ -0,0 +1,7 @@ +package ac.aux.compose.repository + +import ac.aux.compose.database.model.UnsignedNostrEvent + +interface NostrRepository { + suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent? +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/CreateProfileScreen.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/CreateProfileScreen.kt index 0d88b808..723f91bc 100644 --- a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/CreateProfileScreen.kt +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/CreateProfileScreen.kt @@ -1,6 +1,12 @@ package ac.aux.compose.ui.composable +import ac.aux.compose.database.model.Profile +import ac.aux.compose.database.model.UnsignedNostrEvent +import ac.aux.compose.repository.NostrRepository +import ac.aux.compose.ui.composable.widgets.LoadingDataIndicator import ac.aux.compose.ui.theme.AuxTheme +import ac.aux.compose.ui.theme.BluePill +import ac.aux.compose.ui.theme.RedPill import ac.aux.compose.ui.view.model.CreateProfileViewModel import ac.aux.compose.ui.view.state.CreateProfileUIState import androidx.compose.foundation.layout.Arrangement @@ -14,7 +20,11 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Badge import androidx.compose.material.icons.filled.Description import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon +import androidx.compose.material3.LoadingIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface @@ -23,20 +33,25 @@ import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun CreateProfileScreen( initialCreateProfileUIState: CreateProfileUIState = CreateProfileUIState.Declaration, - onNavigateToChatListRoute: () -> Unit + onNavigateToSocialPreconditionRoute: () -> Unit, + onEndThis: () -> Unit, + nostrRepository: NostrRepository ) { val createProfileViewModel: CreateProfileViewModel = viewModel ( factory = CreateProfileViewModel.factory( - initialCreateProfileUIState + initialCreateProfileUIState, + nostrRepository ) ) Scaffold { innerPadding -> @@ -191,11 +206,17 @@ fun CreateProfileScreen( Button( onClick = { - createProfileViewModel.createProfileUIState.value = - CreateProfileUIState.ConfirmInput( - name = createProfileViewModel.createProfileFormState.nameField.text.value, - bio = createProfileViewModel.createProfileFormState.biographyField.text.value - ) + createProfileViewModel.validateInput().let { isInputValid -> + if (isInputValid) { + createProfileViewModel.createProfileUIState.value = + CreateProfileUIState.ConfirmInput( + name = createProfileViewModel.createProfileFormState.nameField.text.value, + bio = createProfileViewModel.createProfileFormState.biographyField.text.value + ) + } + } + + } ) { Text( @@ -245,13 +266,135 @@ fun CreateProfileScreen( textAlign = TextAlign.Center ) + if (createProfileViewModel.isActionPending.value) { + LoadingIndicator( + modifier = Modifier + ) + } else { + Button( + onClick = { + createProfileViewModel.createAccount() + } + ) { + Text( + text = "Create Profile" + ) + } + } + + } + is CreateProfileUIState.Error -> { + Spacer( + modifier = Modifier.weight(1f) + ) + Text( + text = "Something went wrong and we were unable to sign you up. Please try again later.", + style = MaterialTheme.typography.bodyLarge, + textAlign = TextAlign.Center + ) + Spacer( + modifier = Modifier.weight(1f) + ) + } + is CreateProfileUIState.UnsignedProfile -> { + Spacer( + modifier = Modifier.weight(1f) + ) + Text( + text = "Your profile is almost ready... just getting it's first cryptographic signature together.", + style = MaterialTheme.typography.bodyLarge, + textAlign = TextAlign.Center + ) + + Text( + text = "As long as you control your keys there can be no dispute about who ${createAccountUIState.name} actually is.", + style = MaterialTheme.typography.bodySmall, + textAlign = TextAlign.Center + ) + + LoadingDataIndicator( + fillScreen = false + ) + + Spacer( + modifier = Modifier.weight(2f) + ) + } + is CreateProfileUIState.UnbroadcastedProfile -> { + Spacer( + modifier = Modifier.weight(1f) + ) + Text( + text = "Everything is cryptographical sound. Just announcing your profile to the world.", + style = MaterialTheme.typography.bodyLarge, + textAlign = TextAlign.Center + ) + + Text( + text = "Aux will be broadcast to what you publish to relays so that it's distributed and decentralized.", + style = MaterialTheme.typography.bodySmall, + textAlign = TextAlign.Center + ) + + LoadingDataIndicator( + fillScreen = false + ) + + Spacer( + modifier = Modifier.weight(2f) + ) + } + is CreateProfileUIState.ProfileReady -> { + + Spacer( + modifier = Modifier.weight(1f) + ) + Text( + text = "Profile is ready", + style = MaterialTheme.typography.headlineLarge, + textAlign = TextAlign.Center + ) + + Text( + text = "After this there is no turning back." + ) + Button( - onClick = onNavigateToChatListRoute + colors = ButtonDefaults.buttonColors( + containerColor = RedPill, + contentColor = Color.White + ), + onClick = { + + onNavigateToSocialPreconditionRoute.invoke() + } ) { Text( - text = "Create Profile" + text = "See how deep the rabbit hole goes" ) } + + HorizontalDivider( + modifier = Modifier.padding(20.dp) + ) + + Button( + colors = ButtonDefaults.buttonColors( + containerColor = BluePill, + contentColor = Color.DarkGray + ), + onClick = { + // TODO: Delete everything and close the app + onEndThis.invoke() + } + ) { + Text( + text = "end this" + ) + } + Spacer( + modifier = Modifier.weight(2f) + ) } } @@ -270,13 +413,48 @@ fun CreateAccountScreenPreview() { ) { CreateProfileScreen( initialCreateProfileUIState = -// CreateProfileUIState.InputPrompt - CreateProfileUIState.ConfirmInput( - name = "Alan Turing", - bio = "Polyglot: Math... is my middle name, computer scientist. cryptographer, and gold hider." +// CreateProfileUIState.InputPrompt, +// CreateProfileUIState.Error, +// CreateProfileUIState.ConfirmInput( +// name = "Alan Turing", +// bio = "Polyglot: Math... is my middle name, computer scientist. cryptographer, and gold hider." +// ), +// CreateProfileUIState.UnsignedProfile( +// unsignedNostrEvent = UnsignedNostrEvent( +// kind = 0, +// content = "Something here", +// tags = emptyArray(), +// ), +// name = "Alan Turing", +// bio = "Polyglot: Math... is my middle name, computer scientist. cryptographer, and gold hider." +// ), +// CreateProfileUIState.UnbroadcastedProfile( +// nostrEvent = NostrEvent( +// id = "", +// pubKey = "", +// kind = 0, +// content = "Something here", +// tags = emptyArray(), +// sig = "" +// ), +// name = "Alan Turing", +// bio = "Polyglot: Math... is my middle name, computer scientist. cryptographer, and gold hider." +// ), + CreateProfileUIState.ProfileReady( + profile = Profile( + publicKey = "", + nostrEventId = "", + displayName = "Alan Turing", + about = "Polyglot: Math... is my middle name, computer scientist. cryptographer, and gold hider." + ) ), - onNavigateToChatListRoute = {} - ) + onNavigateToSocialPreconditionRoute = {}, + onEndThis = {}, + nostrRepository = object : NostrRepository { + override suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent? { + TODO("Not yet implemented") + } + }) } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/SocialPreconditionScreen.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/SocialPreconditionScreen.kt index b7bc6e53..07a1c37f 100644 --- a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/SocialPreconditionScreen.kt +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/SocialPreconditionScreen.kt @@ -77,7 +77,7 @@ fun SocialPreconditionScreen( ) Text( - "Tell friends to join you on Aux so your feed stays lively and fresh.", + "Tell friends to join you so your feed stays lively and fresh.", textAlign = TextAlign.Center ) diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/navigation/AuxNavHost.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/navigation/AuxNavHost.kt index e4c700b6..a7382f98 100644 --- a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/navigation/AuxNavHost.kt +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/navigation/AuxNavHost.kt @@ -1,10 +1,13 @@ package ac.aux.compose.ui.composable.navigation +import ac.aux.compose.database.model.UnsignedNostrEvent +import ac.aux.compose.repository.NostrRepository import ac.aux.compose.ui.composable.CreateProfileScreen import ac.aux.compose.ui.composable.FeedScreen import ac.aux.compose.ui.composable.ImplementationPendingScreen import ac.aux.compose.ui.composable.LandingScreen import ac.aux.compose.ui.composable.SocialPreconditionScreen +import ac.aux.compose.ui.composable.navigation.routes.BlankRoute import ac.aux.compose.ui.composable.navigation.routes.CreateProfileRoute import ac.aux.compose.ui.composable.navigation.routes.FeedRoute import ac.aux.compose.ui.composable.navigation.routes.ImplementationPendingRoute @@ -18,6 +21,12 @@ import ac.aux.compose.ui.composable.navigation.routes.Route import ac.aux.compose.ui.composable.navigation.routes.SocialPreconditionRoute import ac.aux.compose.ui.composable.widgets.feed.DUMMY_EVENTS import ac.aux.compose.ui.view.state.FeedListUIState +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Surface +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import kotlinx.coroutines.delay @Composable fun AuxNavHost( @@ -50,12 +59,25 @@ fun AuxNavHost( } composable { CreateProfileScreen( - onNavigateToChatListRoute = { + onNavigateToSocialPreconditionRoute = { navController.navigate( route = SocialPreconditionRoute ) - } - ) + }, + onEndThis = { + // TODO: Delete everything and close the app + navController.navigate( + route = BlankRoute + ) { + popUpTo(0) + } + }, + nostrRepository = object : NostrRepository { + override suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent? { + delay(2_100) + return unsignedNostrEvent + } + }) } composable { SocialPreconditionScreen( @@ -90,6 +112,15 @@ fun AuxNavHost( } ) } + composable { + Surface( + modifier = Modifier.fillMaxSize(), + color = Color.Black + ) { + + + } + } composable { backStackEntry -> val route: ImplementationPendingRoute = backStackEntry.toRoute() ImplementationPendingScreen( diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/navigation/routes/BlankRoute.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/navigation/routes/BlankRoute.kt new file mode 100755 index 00000000..99f6ebec --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/navigation/routes/BlankRoute.kt @@ -0,0 +1,6 @@ +package ac.aux.compose.ui.composable.navigation.routes + +import kotlinx.serialization.Serializable + +@Serializable +object BlankRoute: Route() \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/theme/Color.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/theme/Color.kt index d5d256cd..8f01e9d7 100644 --- a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/theme/Color.kt +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/theme/Color.kt @@ -2,6 +2,9 @@ package ac.aux.compose.ui.theme import androidx.compose.ui.graphics.Color +val BluePill = Color(0xFF5D8DD6) +val RedPill = Color(230, 32, 32, 191) + val primaryLight = Color(0xFF000000) val onPrimaryLight = Color(0xFFFFFFFF) val primaryContainerLight = Color(0xFF1B1B1B) diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/view/model/CreateProfileViewModel.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/view/model/CreateProfileViewModel.kt index 5ee8ea76..b3ae0d28 100644 --- a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/view/model/CreateProfileViewModel.kt +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/view/model/CreateProfileViewModel.kt @@ -1,5 +1,9 @@ package ac.aux.compose.ui.view.model +import ac.aux.compose.database.model.NostrEvent +import ac.aux.compose.database.model.Profile +import ac.aux.compose.database.model.UnsignedNostrEvent +import ac.aux.compose.repository.NostrRepository import ac.aux.compose.ui.view.state.CreateProfileUIState import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf @@ -8,21 +12,31 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import ac.aux.compose.ui.view.state.form.CreateProfileFormState +import androidx.lifecycle.viewModelScope import co.touchlab.kermit.Logger +import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlin.time.Instant class CreateProfileViewModel( val initialCreateProfileUIState: CreateProfileUIState, val createProfileFormState: CreateProfileFormState = CreateProfileFormState(), + val nostrRepository: NostrRepository ): ViewModel() { companion object { private const val TAG = "CreateAccountViewModel" fun factory( - initialCreateProfileUIState: CreateProfileUIState + initialCreateProfileUIState: CreateProfileUIState, + nostrRepository: NostrRepository ): ViewModelProvider.Factory = viewModelFactory { initializer { CreateProfileViewModel( - initialCreateProfileUIState + initialCreateProfileUIState, + nostrRepository = nostrRepository ) } } @@ -36,11 +50,82 @@ class CreateProfileViewModel( initialCreateProfileUIState ) - public fun createAccount( - onSuccess: () -> Unit, - onFailure: () -> Unit - ) { + fun validateInput(): Boolean { + if (createProfileFormState.nameField.text.value.isBlank()) { + createProfileFormState.nameField.errorMessage.value = "Please input a display name" + return false + } else { + createProfileFormState.nameField.errorMessage.value = null + } + + if (createProfileFormState.biographyField.text.value.isBlank()) { + createProfileFormState.biographyField.errorMessage.value = "Please input a biography" + return false + } else { + createProfileFormState.biographyField.errorMessage.value = null + } + + return true + } + + public fun createAccount() { logger.d { "createAccount" } - onFailure.invoke() + isActionPending.value = true + + viewModelScope.launch(Dispatchers.IO) { + + val profileEventTemplate = MetadataEvent.createNew( + name = createProfileFormState.nameField.text.value, + about = createProfileFormState.biographyField.text.value + ) + + val unsignedNostrEvent = nostrRepository.saveUnsignedNostrEvent( + UnsignedNostrEvent( + kind = profileEventTemplate.kind, + createdAt = Instant.fromEpochMilliseconds(profileEventTemplate.createdAt), + tags = profileEventTemplate.tags, + content = profileEventTemplate.content + ) + ) + + isActionPending.value = false + + if (unsignedNostrEvent != null) { + createProfileUIState.value = CreateProfileUIState.UnsignedProfile( + unsignedNostrEvent = unsignedNostrEvent, + name = createProfileFormState.nameField.text.value, + bio = createProfileFormState.biographyField.text.value + ) + + delay(7_500) // Seeming busy is very important... + + createProfileUIState.value = CreateProfileUIState.UnbroadcastedProfile( + nostrEvent = NostrEvent( + id = "", + pubKey = "", + kind = 0, + content = "Something here", + tags = emptyArray(), + sig = "" + ), + name = createProfileFormState.nameField.text.value, + bio = createProfileFormState.biographyField.text.value + ) + + delay(4_500) // Seeming busy is very important... + + createProfileUIState.value = CreateProfileUIState.ProfileReady( + profile = Profile( + publicKey = "", + nostrEventId = "", + displayName = createProfileFormState.nameField.text.value, + about = createProfileFormState.biographyField.text.value, + ) + ) + } else { + createProfileUIState.value = CreateProfileUIState.Error + } + + } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/view/state/CreateProfileUIState.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/view/state/CreateProfileUIState.kt index 0e00709c..592a598b 100644 --- a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/view/state/CreateProfileUIState.kt +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/view/state/CreateProfileUIState.kt @@ -1,6 +1,11 @@ package ac.aux.compose.ui.view.state +import ac.aux.compose.database.model.NostrEvent +import ac.aux.compose.database.model.Profile +import ac.aux.compose.database.model.UnsignedNostrEvent + abstract class CreateProfileUIState { + data object Error: CreateProfileUIState() data object Declaration: CreateProfileUIState() @@ -10,4 +15,20 @@ abstract class CreateProfileUIState { val name: String, val bio: String ) : CreateProfileUIState() + + data class UnsignedProfile( + val unsignedNostrEvent: UnsignedNostrEvent, + val name: String, + val bio: String, + ) : CreateProfileUIState() + + data class UnbroadcastedProfile( + val nostrEvent: NostrEvent, + val name: String, + val bio: String + ) : CreateProfileUIState() + + data class ProfileReady( + val profile: Profile + ) : CreateProfileUIState() } \ No newline at end of file