Associate unsignedNostrEvent to broadcast request/receipt
This commit is contained in:
@@ -2,11 +2,11 @@
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "3d48904218bb18d9efb6dba14449f27c",
|
||||
"identityHash": "5b27659e597a301316dcc8ed9eaf8a3e",
|
||||
"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 )",
|
||||
"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",
|
||||
@@ -20,6 +20,11 @@
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unsignedNostrEventId",
|
||||
"columnName": "unsignedNostrEventId",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAccepted",
|
||||
"columnName": "isAccepted",
|
||||
@@ -75,12 +80,23 @@
|
||||
"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, `status` TEXT NOT NULL, `relayURL` TEXT NOT NULL, FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"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, 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",
|
||||
@@ -94,6 +110,11 @@
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unsignedNostrEventId",
|
||||
"columnName": "unsignedNostrEventId",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "status",
|
||||
"columnName": "status",
|
||||
@@ -144,12 +165,23 @@
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "UnsignedNostrEvent",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"unsignedNostrEventId"
|
||||
],
|
||||
"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`))",
|
||||
"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, `unsignedNostrEventId` INTEGER, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
@@ -187,6 +219,11 @@
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unsignedNostrEventId",
|
||||
"columnName": "unsignedNostrEventId",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "createdAt",
|
||||
"columnName": "createdAt",
|
||||
@@ -836,7 +873,7 @@
|
||||
},
|
||||
{
|
||||
"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, `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 )",
|
||||
"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, `content` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
@@ -868,11 +905,6 @@
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "signedNostrEventId",
|
||||
"columnName": "signedNostrEventId",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "createdAt",
|
||||
"columnName": "createdAt",
|
||||
@@ -910,26 +942,22 @@
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_UnsignedNostrEvent_signedNostrEventId",
|
||||
"name": "index_UnsignedNostrEvent_pubKey",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"signedNostrEventId"
|
||||
"pubKey"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_UnsignedNostrEvent_signedNostrEventId` ON `${TABLE_NAME}` (`signedNostrEventId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_UnsignedNostrEvent_pubKey` ON `${TABLE_NAME}` (`pubKey`)"
|
||||
},
|
||||
{
|
||||
"table": "NostrEvent",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"signedNostrEventId"
|
||||
"name": "index_UnsignedNostrEvent_kind",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"kind"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_UnsignedNostrEvent_kind` ON `${TABLE_NAME}` (`kind`)"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1101,7 +1129,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, '3d48904218bb18d9efb6dba14449f27c')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5b27659e597a301316dcc8ed9eaf8a3e')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -112,6 +112,7 @@ abstract class NostrDao(
|
||||
database.broadcastNostrEventRequestDao().upsert(
|
||||
BroadcastNostrEventRequest(
|
||||
nostrEventId = nostrEvent.id,
|
||||
unsignedNostrEventId = nostrEvent.unsignedNostrEventId,
|
||||
relayURL = relayURL
|
||||
)
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ interface UnsignedNostrEventDao {
|
||||
@Query("SELECT * FROM UnsignedNostrEvent")
|
||||
fun getAUnsignedNostrEvents(): List<UnsignedNostrEvent>
|
||||
|
||||
@Query("SELECT * FROM UnsignedNostrEvent WHERE signedNostrEventId IS NULL")
|
||||
@Query("SELECT * FROM UnsignedNostrEvent") // TODO: where nostrEvent.unsignedNostrEventId is 0
|
||||
fun getAllUnprocessedUnsignedNostrEvents(): List<UnsignedNostrEvent>
|
||||
|
||||
@Query("SELECT * FROM UnsignedNostrEvent WHERE id = :id")
|
||||
@@ -26,6 +26,6 @@ interface UnsignedNostrEventDao {
|
||||
@Query("SELECT * FROM UnsignedNostrEvent WHERE kind = 0 AND pubKey = :publicKey")
|
||||
fun observeProfile(publicKey: String): Flow<LocalProfile?>
|
||||
|
||||
@Query("SELECT * FROM UnsignedNostrEvent WHERE pubKey = :publicKey AND signedNostrEventId IS NULL")
|
||||
fun observeUnsignedNostrEvents(publicKey: String): Flow<UnsignedNostrEvent>
|
||||
@Query("SELECT * FROM UnsignedNostrEvent WHERE pubKey = :publicKey") // TODO: where nostrEvent.unsignedNostrEventId is 0
|
||||
fun observeUnsignedNostrEvents(publicKey: String): Flow<UnsignedNostrEvent?>
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import ac.aux.compose.database.model.traits.NostrEventEntity
|
||||
import ac.aux.compose.database.model.traits.UnsignedNostrEventEntity
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
@@ -15,6 +16,12 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
childColumns = ["nostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
ForeignKey(
|
||||
entity = UnsignedNostrEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["unsignedNostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
indices = [
|
||||
Index("nostrEventId"),
|
||||
@@ -25,7 +32,8 @@ data class BroadcastNostrEventReceipt(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Long = 0,
|
||||
override val nostrEventId: HexKey,
|
||||
override val unsignedNostrEventId: Long? = null,
|
||||
val isAccepted: Boolean,
|
||||
val relayURL: String,
|
||||
val messages: String? = null
|
||||
): NostrEventEntity
|
||||
): NostrEventEntity, UnsignedNostrEventEntity
|
||||
@@ -1,6 +1,7 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import ac.aux.compose.database.model.traits.NostrEventEntity
|
||||
import ac.aux.compose.database.model.traits.UnsignedNostrEventEntity
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
@@ -15,6 +16,12 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
childColumns = ["nostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
ForeignKey(
|
||||
entity = UnsignedNostrEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["unsignedNostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
indices = [
|
||||
Index("nostrEventId"),
|
||||
@@ -25,6 +32,7 @@ data class BroadcastNostrEventRequest(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Long = 0,
|
||||
override val nostrEventId: HexKey,
|
||||
override val unsignedNostrEventId: Long? = null,
|
||||
val status: String = "pending",
|
||||
val relayURL: String,
|
||||
): NostrEventEntity
|
||||
): NostrEventEntity, UnsignedNostrEventEntity
|
||||
@@ -4,7 +4,9 @@ import ac.aux.compose.database.model.traits.BroadcastableEntity
|
||||
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 ac.aux.compose.database.model.traits.UnsignedNostrEventEntity
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
@@ -37,12 +39,14 @@ data class NostrEvent(
|
||||
val content: String,
|
||||
val sig: HexKey,
|
||||
|
||||
override val unsignedNostrEventId: Long? = null,
|
||||
|
||||
override val createdAt: Instant = Clock.System.now(),
|
||||
override val updatedAt: Instant = createdAt,
|
||||
override val savedAt: Instant = Clock.System.now(),
|
||||
override val deletedAt: Instant? = null,
|
||||
override val broadcastedAt: Instant? = null,
|
||||
): TimestampedEntity, LocalStoreEntity, BroadcastableEntity, SoftDeletableEntity {
|
||||
): UnsignedNostrEventEntity, TimestampedEntity, LocalStoreEntity, BroadcastableEntity, SoftDeletableEntity {
|
||||
@Ignore
|
||||
private val logger = Logger.withTag("NostrEvent")
|
||||
|
||||
|
||||
@@ -23,16 +23,9 @@ import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = NostrEvent::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["signedNostrEventId"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
)
|
||||
],
|
||||
indices = [
|
||||
Index("signedNostrEventId"),
|
||||
Index("pubKey"),
|
||||
Index("kind"),
|
||||
],
|
||||
)
|
||||
data class UnsignedNostrEvent(
|
||||
@@ -44,8 +37,6 @@ data class UnsignedNostrEvent(
|
||||
val tags: TagArray,
|
||||
val content: String,
|
||||
|
||||
val signedNostrEventId: HexKey? = null,
|
||||
|
||||
override val createdAt: Instant = Clock.System.now(),
|
||||
override val updatedAt: Instant = Clock.System.now(),
|
||||
override val savedAt: Instant = Clock.System.now(),
|
||||
|
||||
@@ -9,11 +9,11 @@ import androidx.room.Embedded
|
||||
import androidx.room.Relation
|
||||
|
||||
data class LocalProfile(
|
||||
@Embedded val unsignedNostrEvent: UnsignedNostrEvent,
|
||||
@Embedded val unsignedNostrEvent: UnsignedNostrEvent?,
|
||||
|
||||
@Relation(
|
||||
parentColumn = "signedNostrEventId",
|
||||
entityColumn = "id"
|
||||
parentColumn = "id",
|
||||
entityColumn = "unsignedNostrEventId"
|
||||
)
|
||||
val nostrEvent: NostrEvent?,
|
||||
|
||||
@@ -24,14 +24,14 @@ data class LocalProfile(
|
||||
val profile: Profile?,
|
||||
|
||||
@Relation(
|
||||
parentColumn = "signedNostrEventId",
|
||||
entityColumn = "nostrEventId"
|
||||
parentColumn = "id",
|
||||
entityColumn = "unsignedNostrEventId"
|
||||
)
|
||||
val broadcastNostrEventRequest: BroadcastNostrEventRequest?,
|
||||
|
||||
@Relation(
|
||||
parentColumn = "signedNostrEventId",
|
||||
entityColumn = "nostrEventId"
|
||||
parentColumn = "id",
|
||||
entityColumn = "unsignedNostrEventId"
|
||||
)
|
||||
val broadcastNostrEventReceipt: BroadcastNostrEventReceipt?
|
||||
)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package ac.aux.compose.database.model.traits
|
||||
|
||||
interface UnsignedNostrEventEntity {
|
||||
val unsignedNostrEventId: Long?
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class DatabaseNostrRepository(
|
||||
return database.unsignedNostrEventDao().observeProfile(publicKey)
|
||||
}
|
||||
|
||||
override suspend fun observeUnsignedNostrEvents(publicKey: HexKey): Flow<UnsignedNostrEvent> {
|
||||
override suspend fun observeUnsignedNostrEvents(publicKey: HexKey): Flow<UnsignedNostrEvent?> {
|
||||
return database.unsignedNostrEventDao().observeUnsignedNostrEvents(publicKey)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.Flow
|
||||
interface NostrRepository {
|
||||
suspend fun observeProfile(publicKey: HexKey): Flow<LocalProfile?>
|
||||
|
||||
suspend fun observeUnsignedNostrEvents(publicKey: HexKey): Flow<UnsignedNostrEvent>
|
||||
suspend fun observeUnsignedNostrEvents(publicKey: HexKey): Flow<UnsignedNostrEvent?>
|
||||
|
||||
suspend fun createNewProfile(
|
||||
publicKey: HexKey,
|
||||
@@ -37,7 +37,7 @@ interface NostrRepository {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun observeUnsignedNostrEvents(publicKey: HexKey): Flow<UnsignedNostrEvent> {
|
||||
override suspend fun observeUnsignedNostrEvents(publicKey: HexKey): Flow<UnsignedNostrEvent?> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package ac.aux.compose.ui.composable
|
||||
|
||||
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.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
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
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
|
||||
import androidx.compose.material3.Text
|
||||
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 UnindexedProfileScreen() {
|
||||
Scaffold { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().padding(
|
||||
10.dp
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(20.dp)
|
||||
) {
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Text(
|
||||
text = "Everything is cryptographical sound. Just indexing your profile on the device.",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Events are indexed so that we can deliver a premium local first experience.",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
LoadingDataIndicator(
|
||||
fillScreen = false
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.weight(8f)
|
||||
)
|
||||
|
||||
// TODO: Give user ability to start over...
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun UnannouncedProfileScreenPreview() {
|
||||
AuxTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
UnindexedProfileScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package ac.aux.compose.ui.composable
|
||||
|
||||
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.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
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
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
|
||||
import androidx.compose.material3.Text
|
||||
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 UnqueuedProfileScreen() {
|
||||
Scaffold { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().padding(
|
||||
10.dp
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(20.dp)
|
||||
) {
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Text(
|
||||
text = "Everything is cryptographical sound. Just need to queue your profile and announce it to the world.",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "All broadcasts are queued so that we can manage data usage on metered connections.",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
LoadingDataIndicator(
|
||||
fillScreen = false
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.weight(8f)
|
||||
)
|
||||
|
||||
// TODO: Give user ability to start over...
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun UnannouncedProfileScreenPreview() {
|
||||
AuxTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
UnqueuedProfileScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import ac.aux.compose.ui.composable.LandingScreen
|
||||
import ac.aux.compose.ui.composable.LoadingScreen
|
||||
import ac.aux.compose.ui.composable.SocialPreconditionScreen
|
||||
import ac.aux.compose.ui.composable.UnannouncedProfileScreen
|
||||
import ac.aux.compose.ui.composable.UnindexedProfileScreen
|
||||
import ac.aux.compose.ui.composable.UnqueuedProfileScreen
|
||||
import ac.aux.compose.ui.composable.UnsignedProfileScreen
|
||||
import ac.aux.compose.ui.composable.navigation.routes.BlankRoute
|
||||
import ac.aux.compose.ui.composable.navigation.routes.CreateProfileRoute
|
||||
@@ -24,6 +26,8 @@ import androidx.navigation.compose.composable
|
||||
import androidx.navigation.toRoute
|
||||
import ac.aux.compose.ui.composable.navigation.routes.SocialPreconditionRoute
|
||||
import ac.aux.compose.ui.composable.navigation.routes.UnannouncedProfileRoute
|
||||
import ac.aux.compose.ui.composable.navigation.routes.UnindexedProfileRoute
|
||||
import ac.aux.compose.ui.composable.navigation.routes.UnqueuedProfileRoute
|
||||
import ac.aux.compose.ui.composable.navigation.routes.UnsignedProfileRoute
|
||||
import ac.aux.compose.ui.composable.widgets.feed.DUMMY_EVENTS
|
||||
import ac.aux.compose.ui.view.model.NavigationViewModel
|
||||
@@ -91,10 +95,26 @@ fun AuxNavHost(
|
||||
popUpTo(0)
|
||||
}
|
||||
}
|
||||
is NavigationUIState.UnindexedProfile -> {
|
||||
navController.navigate(
|
||||
route = UnindexedProfileRoute(
|
||||
state.nostrEvent.id
|
||||
)
|
||||
) {
|
||||
popUpTo(0)
|
||||
}
|
||||
}
|
||||
is NavigationUIState.UnqueuedProfile -> {
|
||||
navController.navigate(
|
||||
route = UnqueuedProfileRoute(
|
||||
state.profile.nostrEventId
|
||||
)
|
||||
)
|
||||
}
|
||||
is NavigationUIState.UnannouncedProfile -> {
|
||||
navController.navigate(
|
||||
route = UnannouncedProfileRoute(
|
||||
state.nostrEvent.id
|
||||
state.broadcastNostrEventRequest.nostrEventId
|
||||
)
|
||||
) {
|
||||
popUpTo(0)
|
||||
@@ -170,6 +190,12 @@ fun AuxNavHost(
|
||||
unsignedNostrEvent = route.toPlaceholderUnsignedNostrEvent()
|
||||
)
|
||||
}
|
||||
composable<UnindexedProfileRoute> {
|
||||
UnindexedProfileScreen()
|
||||
}
|
||||
composable<UnqueuedProfileRoute> {
|
||||
UnqueuedProfileScreen()
|
||||
}
|
||||
composable<UnannouncedProfileRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<UnannouncedProfileRoute>()
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package ac.aux.compose.ui.composable.navigation.routes
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.time.Clock
|
||||
|
||||
@Serializable
|
||||
data class UnindexedProfileRoute(
|
||||
val nostrEventId: String,
|
||||
): Route() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ac.aux.compose.ui.composable.navigation.routes
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.time.Clock
|
||||
|
||||
@Serializable
|
||||
data class UnqueuedProfileRoute(
|
||||
val nostrEventId: String,
|
||||
): Route() {
|
||||
|
||||
}
|
||||
@@ -64,27 +64,29 @@ class NavigationViewModel(
|
||||
viewModelScope.launch {
|
||||
nostrRepository.observeUnsignedNostrEvents(
|
||||
publicKey = SeedManager.activePublicKey().toHexKey()
|
||||
).collect { unsignedNostrEvent ->
|
||||
).collect { unsignedNostrEventOrNull ->
|
||||
unsignedNostrEventOrNull?.let { unsignedNostrEvent ->
|
||||
val event = tempSigner.signNormal<Event>(
|
||||
createdAt = unsignedNostrEvent.createdAt.toEpochMilliseconds(),
|
||||
kind = unsignedNostrEvent.kind,
|
||||
tags = unsignedNostrEvent.tags,
|
||||
content = unsignedNostrEvent.content
|
||||
)
|
||||
|
||||
val event = tempSigner.signNormal<Event>(
|
||||
createdAt = unsignedNostrEvent.createdAt.toEpochMilliseconds(),
|
||||
kind = unsignedNostrEvent.kind,
|
||||
tags = unsignedNostrEvent.tags,
|
||||
content = unsignedNostrEvent.content
|
||||
)
|
||||
|
||||
nostrRepository.publishNostrEvent(
|
||||
NostrEvent(
|
||||
id = event.id,
|
||||
pubKey = event.pubKey,
|
||||
kind = event.kind,
|
||||
tags = event.tags,
|
||||
content = event.content,
|
||||
createdAt = Instant.fromEpochMilliseconds(event.createdAt),
|
||||
sig = event.sig
|
||||
),
|
||||
relayURLs = Relays.eventFinderRelaySet.map { normalizedRelayUrl -> normalizedRelayUrl.url }
|
||||
)
|
||||
nostrRepository.publishNostrEvent(
|
||||
NostrEvent(
|
||||
id = event.id,
|
||||
pubKey = event.pubKey,
|
||||
kind = event.kind,
|
||||
tags = event.tags,
|
||||
content = event.content,
|
||||
createdAt = Instant.fromEpochMilliseconds(event.createdAt),
|
||||
sig = event.sig,
|
||||
unsignedNostrEventId = unsignedNostrEvent.id
|
||||
),
|
||||
relayURLs = Relays.eventFinderRelaySet.map { normalizedRelayUrl -> normalizedRelayUrl.url }
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -105,14 +107,27 @@ class NavigationViewModel(
|
||||
if (localProfile == null) {
|
||||
logger.i("We need to be on the landing screen so user creates account")
|
||||
NavigationUIState.Landing
|
||||
} else if (localProfile.profile != null) {
|
||||
logger.i("We have a profile: ${localProfile.profile}")
|
||||
} else if (localProfile.unsignedNostrEvent == null) {
|
||||
logger.i("We need to be on the landing screen so user creates account")
|
||||
NavigationUIState.Landing
|
||||
} else if (localProfile.broadcastNostrEventReceipt != null) {
|
||||
logger.i("We have a broadcast receipt: ${localProfile.profile}")
|
||||
NavigationUIState.ProfileLoaded(
|
||||
broadcastNostrEventReceipt = localProfile.broadcastNostrEventReceipt
|
||||
)
|
||||
} else if (localProfile.broadcastNostrEventRequest != null) {
|
||||
logger.i("We have a profile: ${localProfile.profile}")
|
||||
NavigationUIState.UnannouncedProfile(
|
||||
broadcastNostrEventRequest = localProfile.broadcastNostrEventRequest
|
||||
)
|
||||
} else if (localProfile.profile != null) {
|
||||
logger.i("We have a profile that needs to queued for broadcast: ${localProfile.profile}")
|
||||
NavigationUIState.UnqueuedProfile(
|
||||
profile = localProfile.profile
|
||||
)
|
||||
} else if (localProfile.nostrEvent != null) {
|
||||
logger.i("Nostr event with the profile needs to be announced")
|
||||
NavigationUIState.UnannouncedProfile(
|
||||
logger.i("Nostr event with the profile needs to be indexed: ${localProfile.nostrEvent}")
|
||||
NavigationUIState.UnindexedProfile(
|
||||
nostrEvent = localProfile.nostrEvent
|
||||
)
|
||||
} else {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package ac.aux.compose.ui.view.state
|
||||
|
||||
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.Profile
|
||||
import ac.aux.compose.database.model.UnsignedNostrEvent
|
||||
import ac.aux.compose.database.model.intermdiate.LocalProfile
|
||||
|
||||
abstract class NavigationUIState {
|
||||
data object Error: NavigationUIState()
|
||||
@@ -15,11 +18,22 @@ abstract class NavigationUIState {
|
||||
val unsignedNostrEvent: UnsignedNostrEvent,
|
||||
) : NavigationUIState()
|
||||
|
||||
data class UnannouncedProfile(
|
||||
data class UnindexedProfile(
|
||||
val nostrEvent: NostrEvent,
|
||||
) : NavigationUIState()
|
||||
|
||||
data class UnqueuedProfile(
|
||||
val profile: Profile,
|
||||
) : NavigationUIState()
|
||||
|
||||
|
||||
data class UnannouncedProfile(
|
||||
val broadcastNostrEventRequest: BroadcastNostrEventRequest,
|
||||
) : NavigationUIState()
|
||||
|
||||
|
||||
|
||||
data class ProfileLoaded(
|
||||
val profile: Profile
|
||||
val broadcastNostrEventReceipt: BroadcastNostrEventReceipt
|
||||
) : NavigationUIState()
|
||||
}
|
||||
Reference in New Issue
Block a user