Introduce navigationViewModel
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "61eb893d34ad04f95f8b007f8448ad7f",
|
||||
"identityHash": "80c7032502d17a1893ea8dc67debd365",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "BroadcastNostrEventReceipt",
|
||||
@@ -221,7 +221,27 @@
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_NostrEvent_kind",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"kind"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEvent_kind` ON `${TABLE_NAME}` (`kind`)"
|
||||
},
|
||||
{
|
||||
"name": "index_NostrEvent_pubKey",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"pubKey"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEvent_pubKey` ON `${TABLE_NAME}` (`pubKey`)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "Post",
|
||||
@@ -1075,7 +1095,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, '61eb893d34ad04f95f8b007f8448ad7f')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '80c7032502d17a1893ea8dc67debd365')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.vitorpamplona.quartz.utils.platform
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -17,7 +18,12 @@ class MainActivity : ComponentActivity() {
|
||||
val navController = rememberNavController()
|
||||
|
||||
AuxApp(
|
||||
navController
|
||||
navController = navController,
|
||||
auxGlobal = AuxGlobal(
|
||||
platformContext = PlatformContext(
|
||||
applicationContext = applicationContext
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,17 +12,16 @@ import androidx.navigation.NavHostController
|
||||
@Composable
|
||||
fun AuxApp(
|
||||
navController: NavHostController,
|
||||
auxGlobal: AuxGlobal
|
||||
) {
|
||||
AuxTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
AuxNavHost(
|
||||
|
||||
navController = navController,
|
||||
startDestination = LandingRoute
|
||||
auxGlobal = auxGlobal,
|
||||
navController = navController
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import ac.aux.compose.database.model.Zap
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.room.immediateTransaction
|
||||
import androidx.room.useWriterConnection
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
@@ -52,4 +54,12 @@ abstract class AuxDatabase: RoomDatabase() {
|
||||
|
||||
abstract fun unsignedNostrEventDao(): UnsignedNostrEventDao
|
||||
abstract fun zapDao(): ZapDao
|
||||
|
||||
suspend fun wipeData() {
|
||||
useWriterConnection { transactor ->
|
||||
transactor.immediateTransaction {
|
||||
// TODO: Actually delete the data...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import androidx.room.Transaction
|
||||
abstract class NostrDao(
|
||||
private val database: AuxDatabase
|
||||
) {
|
||||
|
||||
@Transaction
|
||||
open suspend fun insertNostrEvent(
|
||||
nostrEvent: NostrEvent,
|
||||
|
||||
@@ -3,10 +3,14 @@ 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.UnsignedNostrEvent
|
||||
import ac.aux.compose.database.model.intermdiate.LocalProfile
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Relation
|
||||
import androidx.room.Upsert
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@@ -16,7 +20,7 @@ interface ProfileDao {
|
||||
fun getAllPosts(): List<Profile>
|
||||
|
||||
@Query("SELECT * FROM Profile WHERE publicKey = :publicKey")
|
||||
fun getProfileByPublicKey(publicKey: String): Flow<Profile?>
|
||||
fun getProfileByPublicKey(publicKey: String): Profile?
|
||||
|
||||
@Insert(
|
||||
onConflict = OnConflictStrategy.IGNORE
|
||||
@@ -25,4 +29,8 @@ interface ProfileDao {
|
||||
|
||||
@Upsert
|
||||
suspend fun upsert(profile: Profile)
|
||||
|
||||
@Query("SELECT * FROM NostrEvent WHERE kind = 0 AND pubKey = :publicKey")
|
||||
fun observeProfile(publicKey: String): Flow<LocalProfile?>
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ 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.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
@@ -21,7 +22,12 @@ import kotlin.String
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Entity
|
||||
@Entity(
|
||||
indices = [
|
||||
Index("kind"),
|
||||
Index("pubKey"),
|
||||
]
|
||||
)
|
||||
data class NostrEvent(
|
||||
@PrimaryKey
|
||||
val id: String,
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package ac.aux.compose.database.model.intermdiate
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.database.model.Profile
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Relation
|
||||
|
||||
data class LocalProfile(
|
||||
@Embedded val nostrEvent: NostrEvent,
|
||||
|
||||
@Relation(
|
||||
parentColumn = "pubKey",
|
||||
entityColumn = "publicKey"
|
||||
)
|
||||
val profile: Profile?
|
||||
)
|
||||
@@ -4,15 +4,21 @@ import ac.aux.compose.database.AuxDatabase
|
||||
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
|
||||
import ac.aux.compose.repository.NostrRepository
|
||||
import ac.aux.compose.ui.view.state.CreateProfileUIState
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlin.time.Instant
|
||||
|
||||
class DatabaseNostrRepository(
|
||||
private val database: AuxDatabase
|
||||
): NostrRepository {
|
||||
override suspend fun observeProfile(publicKey: HexKey): Flow<LocalProfile?> {
|
||||
return database.profileDao().observeProfile(publicKey)
|
||||
}
|
||||
|
||||
override suspend fun createNewProfile(
|
||||
name: String,
|
||||
biography: String,
|
||||
@@ -76,4 +82,8 @@ class DatabaseNostrRepository(
|
||||
null
|
||||
}
|
||||
|
||||
override suspend fun wipeDatabase() {
|
||||
database.wipeData()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ac.aux.compose.managers
|
||||
|
||||
import ac.aux.compose.AuxGlobal
|
||||
import ac.aux.compose.database.AuxDatabaseConstructor
|
||||
import ac.aux.compose.database.builder.PlatformDatabaseBuilder
|
||||
import ac.aux.compose.database.builder.getRoomDatabase
|
||||
|
||||
class DatabaseManager(
|
||||
auxGlobal: AuxGlobal
|
||||
) {
|
||||
val auxDatabase by lazy {
|
||||
getRoomDatabase(
|
||||
PlatformDatabaseBuilder.getDatabaseBuilder(
|
||||
auxGlobal.platformContext
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
package ac.aux.compose.repository
|
||||
|
||||
import ac.aux.compose.database.dao.ProfileDao
|
||||
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
|
||||
import ac.aux.compose.ui.view.state.CreateProfileUIState
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface NostrRepository {
|
||||
suspend fun observeProfile(publicKey: HexKey): Flow<LocalProfile?>
|
||||
|
||||
suspend fun createNewProfile(
|
||||
name: String,
|
||||
biography: String,
|
||||
@@ -16,4 +22,31 @@ interface NostrRepository {
|
||||
)
|
||||
|
||||
suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent?
|
||||
|
||||
suspend fun wipeDatabase()
|
||||
|
||||
companion object {
|
||||
val NO_OP_NOSTR_REPOSITORY = object : NostrRepository {
|
||||
override suspend fun observeProfile(publicKey: HexKey): Flow<LocalProfile?> {
|
||||
TODO("")
|
||||
}
|
||||
|
||||
override suspend fun createNewProfile(
|
||||
name: String,
|
||||
biography: String,
|
||||
onError: () -> Unit,
|
||||
onUnsignProfile: (UnsignedNostrEvent) -> Unit,
|
||||
onUnbroadcastedProfile: (NostrEvent) -> Unit,
|
||||
onProfileReady: (Profile) -> Unit
|
||||
) {
|
||||
}
|
||||
|
||||
override suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent? {
|
||||
return null
|
||||
}
|
||||
|
||||
override suspend fun wipeDatabase() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
fun CreateProfileScreen(
|
||||
initialCreateProfileUIState: CreateProfileUIState = CreateProfileUIState.Declaration,
|
||||
onNavigateToSocialPreconditionRoute: () -> Unit,
|
||||
onEndThis: () -> Unit,
|
||||
onNavigateToEndThis: () -> Unit,
|
||||
nostrRepository: NostrRepository
|
||||
) {
|
||||
val createProfileViewModel: CreateProfileViewModel = viewModel (
|
||||
@@ -386,7 +386,9 @@ fun CreateProfileScreen(
|
||||
),
|
||||
onClick = {
|
||||
// TODO: Delete everything and close the app
|
||||
onEndThis.invoke()
|
||||
createProfileViewModel.wipeDatabase {
|
||||
onNavigateToEndThis.invoke()
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
@@ -450,23 +452,8 @@ fun CreateAccountScreenPreview() {
|
||||
)
|
||||
),
|
||||
onNavigateToSocialPreconditionRoute = {},
|
||||
onEndThis = {},
|
||||
nostrRepository = object : NostrRepository {
|
||||
override suspend fun createNewProfile(
|
||||
name: String,
|
||||
biography: String,
|
||||
onError: () -> Unit,
|
||||
onUnsignProfile: (UnsignedNostrEvent) -> Unit,
|
||||
onUnbroadcastedProfile: (NostrEvent) -> Unit,
|
||||
onProfileReady: (Profile) -> Unit
|
||||
) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
})
|
||||
onNavigateToEndThis = {},
|
||||
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package ac.aux.compose.ui.composable
|
||||
|
||||
import ac.aux.compose.ui.composable.widgets.LoadingDataIndicator
|
||||
import ac.aux.compose.ui.theme.AuxTheme
|
||||
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.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun LoadingScreen(
|
||||
) {
|
||||
Scaffold { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
) {
|
||||
LoadingDataIndicator()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun LoadingScreenPreview() {
|
||||
AuxTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LoadingScreen(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
package ac.aux.compose.ui.composable.navigation
|
||||
|
||||
import ac.aux.compose.AuxGlobal
|
||||
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.database.repository.DatabaseNostrRepository
|
||||
import ac.aux.compose.managers.DatabaseManager
|
||||
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.LoadingScreen
|
||||
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
|
||||
import ac.aux.compose.ui.composable.navigation.routes.LandingRoute
|
||||
import ac.aux.compose.ui.composable.navigation.routes.LoadingRoute
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
@@ -22,24 +24,98 @@ import androidx.navigation.toRoute
|
||||
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.model.NavigationViewModel
|
||||
import ac.aux.compose.ui.view.state.CreateProfileUIState
|
||||
import ac.aux.compose.ui.view.state.FeedListUIState
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import ac.aux.compose.ui.view.state.NavigationUIState
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import kotlinx.coroutines.delay
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
|
||||
@Composable
|
||||
fun AuxNavHost(
|
||||
navController: NavHostController,
|
||||
startDestination: Route
|
||||
auxGlobal: AuxGlobal,
|
||||
navController: NavHostController
|
||||
) {
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val databaseManager = DatabaseManager(auxGlobal)
|
||||
|
||||
val nostrRepository = DatabaseNostrRepository(
|
||||
database = databaseManager.auxDatabase
|
||||
)
|
||||
|
||||
val navigationViewModel: NavigationViewModel = viewModel (
|
||||
factory = NavigationViewModel.factory(
|
||||
NavigationUIState.Landing,
|
||||
nostrRepository
|
||||
)
|
||||
)
|
||||
|
||||
LaunchedEffect(lifecycleOwner) {
|
||||
navigationViewModel.navigationUIState.collect { state ->
|
||||
when (state) {
|
||||
is NavigationUIState.Error -> {
|
||||
navController.navigate(
|
||||
route = ImplementationPendingRoute("Error screen")
|
||||
) {
|
||||
popUpTo(0)
|
||||
}
|
||||
}
|
||||
is NavigationUIState.Loading -> {
|
||||
navController.navigate(
|
||||
route = LoadingRoute
|
||||
) {
|
||||
popUpTo(0)
|
||||
}
|
||||
}
|
||||
is NavigationUIState.Landing -> {
|
||||
navController.navigate(
|
||||
route = LandingRoute
|
||||
) {
|
||||
popUpTo(0)
|
||||
}
|
||||
}
|
||||
is NavigationUIState.UnbroadcastedProfile -> {
|
||||
navController.navigate(
|
||||
route = CreateProfileRoute(
|
||||
nostrEventId = state.nostrEvent.id
|
||||
)
|
||||
) {
|
||||
popUpTo(0)
|
||||
}
|
||||
}
|
||||
is NavigationUIState.ProfileLoaded -> {
|
||||
navController.navigate(
|
||||
route = FeedRoute
|
||||
) {
|
||||
popUpTo(0)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
navController.navigate(
|
||||
route = ImplementationPendingRoute("Unsupported $state")
|
||||
) {
|
||||
popUpTo(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = startDestination
|
||||
startDestination = LoadingRoute
|
||||
) {
|
||||
composable<LoadingRoute> {
|
||||
LoadingScreen()
|
||||
}
|
||||
composable<LandingRoute> {
|
||||
LandingScreen(
|
||||
onNavigateToLearnMore = {
|
||||
@@ -54,43 +130,34 @@ fun AuxNavHost(
|
||||
},
|
||||
onNavigateToCreateProfile = {
|
||||
navController.navigate(
|
||||
route = CreateProfileRoute
|
||||
route = CreateProfileRoute()
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
composable<CreateProfileRoute> {
|
||||
composable<CreateProfileRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<CreateProfileRoute>()
|
||||
|
||||
CreateProfileScreen(
|
||||
initialCreateProfileUIState = route.toPlaceholderNostrEvent()?.let { placeholderNostrEvent ->
|
||||
CreateProfileUIState.UnbroadcastedProfile(
|
||||
placeholderNostrEvent
|
||||
)
|
||||
} ?: CreateProfileUIState.Declaration,
|
||||
onNavigateToSocialPreconditionRoute = {
|
||||
navController.navigate(
|
||||
route = SocialPreconditionRoute
|
||||
)
|
||||
},
|
||||
onEndThis = {
|
||||
// TODO: Delete everything and close the app
|
||||
onNavigateToEndThis = {
|
||||
navController.navigate(
|
||||
route = BlankRoute
|
||||
) {
|
||||
popUpTo(0)
|
||||
}
|
||||
},
|
||||
nostrRepository = object : NostrRepository {
|
||||
override suspend fun createNewProfile(
|
||||
name: String,
|
||||
biography: String,
|
||||
onError: () -> Unit,
|
||||
onUnsignProfile: (UnsignedNostrEvent) -> Unit,
|
||||
onUnbroadcastedProfile: (NostrEvent) -> Unit,
|
||||
onProfileReady: (Profile) -> Unit
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
override suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent? {
|
||||
delay(2_100)
|
||||
return unsignedNostrEvent
|
||||
}
|
||||
})
|
||||
nostrRepository = nostrRepository
|
||||
)
|
||||
}
|
||||
composable<SocialPreconditionRoute> {
|
||||
SocialPreconditionScreen(
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
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
|
||||
object CreateProfileRoute: Route() {
|
||||
data class CreateProfileRoute(
|
||||
val nostrEventId: String? = null,
|
||||
): Route() {
|
||||
|
||||
fun toPlaceholderNostrEvent(): NostrEvent? = nostrEventId?.let {
|
||||
NostrEvent(
|
||||
id = nostrEventId,
|
||||
pubKey = "",
|
||||
createdAt = Clock.System.now(),
|
||||
tags = emptyArray(),
|
||||
kind = 0,
|
||||
sig = "",
|
||||
content = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package ac.aux.compose.ui.composable.navigation.routes
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
object LoadingRoute: Route()
|
||||
@@ -90,8 +90,6 @@ class CreateProfileViewModel(
|
||||
onUnbroadcastedProfile = { unbroadcastedNostrEvent ->
|
||||
createProfileUIState.value = CreateProfileUIState.UnbroadcastedProfile(
|
||||
nostrEvent = unbroadcastedNostrEvent,
|
||||
name = createProfileFormState.nameField.text.value,
|
||||
bio = createProfileFormState.biographyField.text.value
|
||||
)
|
||||
},
|
||||
onProfileReady = { profile ->
|
||||
@@ -102,4 +100,13 @@ class CreateProfileViewModel(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun wipeDatabase(
|
||||
onNavigateToEndThis: () -> Unit
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
nostrRepository.wipeDatabase()
|
||||
onNavigateToEndThis.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package ac.aux.compose.ui.view.model
|
||||
|
||||
import ac.aux.compose.repository.NostrRepository
|
||||
import ac.aux.compose.ui.view.state.NavigationUIState
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.getAndUpdate
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.random.Random
|
||||
|
||||
class NavigationViewModel(
|
||||
initialNavigationUIState: NavigationUIState,
|
||||
val nostrRepository: NostrRepository
|
||||
): ViewModel() {
|
||||
companion object {
|
||||
private const val TAG = "NavigationViewModel"
|
||||
|
||||
fun factory(
|
||||
initialNavigationUIState: NavigationUIState,
|
||||
nostrRepository: NostrRepository
|
||||
): ViewModelProvider.Factory = viewModelFactory {
|
||||
initializer {
|
||||
NavigationViewModel(
|
||||
initialNavigationUIState = initialNavigationUIState,
|
||||
nostrRepository = nostrRepository
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val tempDevelopmentKeyPair = KeyPair(
|
||||
privKey = Random(21_000_000L).nextBytes(32)
|
||||
)
|
||||
private val _navigationUIState = MutableStateFlow(
|
||||
initialNavigationUIState
|
||||
)
|
||||
val navigationUIState = _navigationUIState.asStateFlow()
|
||||
|
||||
init {
|
||||
observeProfile()
|
||||
}
|
||||
|
||||
private val logger = Logger.withTag(TAG)
|
||||
|
||||
|
||||
|
||||
private fun observeProfile() {
|
||||
viewModelScope.launch {
|
||||
nostrRepository.observeProfile(tempDevelopmentKeyPair.pubKey.toHexKey()).map { localProfile ->
|
||||
|
||||
_navigationUIState.getAndUpdate {
|
||||
if (localProfile == null) {
|
||||
NavigationUIState.Landing
|
||||
} else if (localProfile.profile != null) {
|
||||
NavigationUIState.ProfileLoaded(
|
||||
profile = localProfile.profile
|
||||
)
|
||||
} else if (localProfile.nostrEvent != null) {
|
||||
NavigationUIState.UnbroadcastedProfile(
|
||||
nostrEvent = localProfile.nostrEvent
|
||||
)
|
||||
// TODO: Support unsignedNostrEvent with pubKey so we can have multi account support...
|
||||
// } else if (localProfile.nostrEvent != null) {
|
||||
// NavigationUIState.UnbroadcastedProfile(
|
||||
// nostrEvent = localProfile.nostrEvent
|
||||
// )
|
||||
} else {
|
||||
NavigationUIState.Error
|
||||
}
|
||||
}
|
||||
}.catch {
|
||||
NavigationUIState.Error
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), NavigationUIState.Landing)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,8 +24,6 @@ abstract class CreateProfileUIState {
|
||||
|
||||
data class UnbroadcastedProfile(
|
||||
val nostrEvent: NostrEvent,
|
||||
val name: String,
|
||||
val bio: String
|
||||
) : CreateProfileUIState()
|
||||
|
||||
data class ProfileReady(
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
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 NavigationUIState {
|
||||
data object Error: NavigationUIState()
|
||||
|
||||
data object Loading: NavigationUIState()
|
||||
|
||||
data object Landing: NavigationUIState()
|
||||
|
||||
data class UnsignedProfile(
|
||||
val unsignedNostrEvent: UnsignedNostrEvent,
|
||||
) : NavigationUIState()
|
||||
|
||||
data class UnbroadcastedProfile(
|
||||
val nostrEvent: NostrEvent,
|
||||
) : NavigationUIState()
|
||||
|
||||
data class ProfileLoaded(
|
||||
val profile: Profile
|
||||
) : NavigationUIState()
|
||||
}
|
||||
@@ -6,5 +6,10 @@ import androidx.navigation.compose.rememberNavController
|
||||
fun MainViewController() = ComposeUIViewController {
|
||||
val navController = rememberNavController()
|
||||
|
||||
AuxApp(navController)
|
||||
AuxApp(
|
||||
navController = navController,
|
||||
auxGlobal = AuxGlobal(
|
||||
platformContext = PlatformContext()
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -10,6 +10,11 @@ fun main() = application {
|
||||
title = "Aux",
|
||||
) {
|
||||
val navController = rememberNavController()
|
||||
AuxApp(navController)
|
||||
AuxApp(
|
||||
navController = navController,
|
||||
auxGlobal = AuxGlobal(
|
||||
platformContext = PlatformContext()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user