Add write note functionality
This commit is contained in:
1 parent
cf81f2d6ba
commit
817c3da8b3
9 files changed
+444
-4
No files matched your search
+28
-2
@@ -12,6 +12,7 @@ import ac.aux.compose.repository.NostrRepository
|
|||||||
import co.touchlab.kermit.Logger
|
import co.touchlab.kermit.Logger
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||||
|
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlin.time.Instant
|
import kotlin.time.Instant
|
||||||
@@ -59,8 +60,6 @@ class DatabaseNostrRepository(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (unsignedNostrEvent == null) {
|
if (unsignedNostrEvent == null) {
|
||||||
onError.invoke()
|
onError.invoke()
|
||||||
} else if (unsignedNostrEvent.kind == 0 ) {
|
} else if (unsignedNostrEvent.kind == 0 ) {
|
||||||
@@ -69,6 +68,33 @@ class DatabaseNostrRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun createNewTextNote(
|
||||||
|
publicKey: HexKey,
|
||||||
|
text: String,
|
||||||
|
onError: () -> Unit,
|
||||||
|
onSuccess: () -> Unit
|
||||||
|
) {
|
||||||
|
val textNote = TextNoteEvent.build(
|
||||||
|
note = text,
|
||||||
|
)
|
||||||
|
|
||||||
|
val unsignedNostrEvent = saveUnsignedNostrEvent(
|
||||||
|
UnsignedNostrEvent(
|
||||||
|
pubKey = publicKey,
|
||||||
|
kind = textNote.kind,
|
||||||
|
createdAt = Instant.fromEpochMilliseconds(textNote.createdAt),
|
||||||
|
tags = textNote.tags,
|
||||||
|
content = textNote.content
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (unsignedNostrEvent == null) {
|
||||||
|
onError.invoke()
|
||||||
|
} else {
|
||||||
|
onSuccess.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent? = try {
|
override suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent? = try {
|
||||||
delay(2_100) // Just to make it look like we are doing serious work...
|
delay(2_100) // Just to make it look like we are doing serious work...
|
||||||
unsignedNostrEvent.copy(
|
unsignedNostrEvent.copy(
|
||||||
|
|||||||
@@ -27,6 +27,13 @@ interface NostrRepository {
|
|||||||
onProfileReady: (Profile) -> Unit
|
onProfileReady: (Profile) -> Unit
|
||||||
)
|
)
|
||||||
|
|
||||||
|
suspend fun createNewTextNote(
|
||||||
|
publicKey: HexKey,
|
||||||
|
text: String,
|
||||||
|
onError: () -> Unit,
|
||||||
|
onSuccess: () -> Unit
|
||||||
|
)
|
||||||
|
|
||||||
suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent?
|
suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent?
|
||||||
|
|
||||||
suspend fun wipeDatabase()
|
suspend fun wipeDatabase()
|
||||||
@@ -64,6 +71,15 @@ interface NostrRepository {
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun createNewTextNote(
|
||||||
|
publicKey: HexKey,
|
||||||
|
text: String,
|
||||||
|
onError: () -> Unit,
|
||||||
|
onSuccess: () -> Unit
|
||||||
|
) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent? {
|
override suspend fun saveUnsignedNostrEvent(unsignedNostrEvent: UnsignedNostrEvent): UnsignedNostrEvent? {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,12 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Adb
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.Create
|
||||||
import androidx.compose.material.icons.filled.Person
|
import androidx.compose.material.icons.filled.Person
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -40,7 +44,8 @@ import kotlin.time.Clock
|
|||||||
@Composable
|
@Composable
|
||||||
fun FeedScreen(
|
fun FeedScreen(
|
||||||
initialFeedListUIState: FeedListUIState,
|
initialFeedListUIState: FeedListUIState,
|
||||||
onNavigateToEvent: (Event) -> Unit
|
onNavigateToEvent: (Event) -> Unit,
|
||||||
|
onNavigateToWriteNewNote: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val feedListViewModel: FeedListViewModel = viewModel(
|
val feedListViewModel: FeedListViewModel = viewModel(
|
||||||
factory = FeedListViewModel.factory(
|
factory = FeedListViewModel.factory(
|
||||||
@@ -80,6 +85,20 @@ fun FeedScreen(
|
|||||||
scrollBehavior = scrollBehavior
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
|
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
ExtendedFloatingActionButton(
|
||||||
|
onClick = onNavigateToWriteNewNote,
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Create,
|
||||||
|
contentDescription = "Write"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text("Write")
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Column(
|
Column(
|
||||||
@@ -125,7 +144,8 @@ private fun FeedScreenPreview() {
|
|||||||
) {
|
) {
|
||||||
FeedScreen(
|
FeedScreen(
|
||||||
initialFeedListUIState = FeedListUIState.Loading,
|
initialFeedListUIState = FeedListUIState.Loading,
|
||||||
onNavigateToEvent = {}
|
onNavigateToEvent = {},
|
||||||
|
onNavigateToWriteNewNote = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,246 @@
|
|||||||
|
package ac.aux.compose.ui.composable
|
||||||
|
|
||||||
|
import ac.aux.compose.database.model.Profile
|
||||||
|
import ac.aux.compose.repository.NostrRepository
|
||||||
|
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.WriteNewNoteViewModel
|
||||||
|
import ac.aux.compose.ui.view.state.CreateProfileUIState
|
||||||
|
import ac.aux.compose.ui.view.state.WriteNewNoteUIState
|
||||||
|
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.automirrored.filled.NavigateNext
|
||||||
|
import androidx.compose.material.icons.filled.Badge
|
||||||
|
import androidx.compose.material.icons.filled.Create
|
||||||
|
import androidx.compose.material.icons.filled.Description
|
||||||
|
import androidx.compose.material.icons.filled.NavigateNext
|
||||||
|
import androidx.compose.material.icons.filled.NextPlan
|
||||||
|
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 WriteNewNoteScreen(
|
||||||
|
initialWriteNewNoteUIState: WriteNewNoteUIState = WriteNewNoteUIState.InputPrompt,
|
||||||
|
onNostrEventPublished: () -> Unit,
|
||||||
|
nostrRepository: NostrRepository
|
||||||
|
) {
|
||||||
|
val writeNewNoteViewModel: WriteNewNoteViewModel = viewModel (
|
||||||
|
factory = WriteNewNoteViewModel.factory(
|
||||||
|
initialWriteNewNoteUIState,
|
||||||
|
nostrRepository
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Scaffold { innerPadding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(innerPadding)
|
||||||
|
) {
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(
|
||||||
|
10.dp
|
||||||
|
),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(20.dp)
|
||||||
|
) {
|
||||||
|
|
||||||
|
when (val writeNewNoteUIState = writeNewNoteViewModel.writeNewNoteUIState.value) {
|
||||||
|
is WriteNewNoteUIState.InputPrompt -> {
|
||||||
|
Text(
|
||||||
|
"Write new note",
|
||||||
|
style = MaterialTheme.typography.headlineSmall
|
||||||
|
)
|
||||||
|
|
||||||
|
TextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = writeNewNoteViewModel.writeNewNoteFormState.textField.text.value,
|
||||||
|
onValueChange = {
|
||||||
|
writeNewNoteViewModel.writeNewNoteFormState.textField.text.value = it
|
||||||
|
},
|
||||||
|
isError = writeNewNoteViewModel.writeNewNoteFormState.textField.errorMessage.value != null,
|
||||||
|
supportingText = writeNewNoteViewModel.writeNewNoteFormState.textField.errorMessage.value?.let {
|
||||||
|
{
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
bottom = 8.dp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions(
|
||||||
|
keyboardType = KeyboardType.Text,
|
||||||
|
),
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
text = "What vibrations do you want to send out?",
|
||||||
|
maxLines = 1,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = "Enter the name you want to use for your profile",
|
||||||
|
maxLines = 1,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Create,
|
||||||
|
contentDescription = "Write note"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
|
||||||
|
writeNewNoteViewModel.validateInput().let { isInputValid ->
|
||||||
|
if (isInputValid) {
|
||||||
|
writeNewNoteViewModel.writeNewNoteUIState.value =
|
||||||
|
WriteNewNoteUIState.ConfirmNote(
|
||||||
|
text = writeNewNoteViewModel.writeNewNoteFormState.textField.text.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"Next"
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
Icons.Default.NavigateNext,
|
||||||
|
contentDescription = "Next"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
is WriteNewNoteUIState.ConfirmNote-> {
|
||||||
|
|
||||||
|
// TODO: Get profile for active pubKey
|
||||||
|
Text(
|
||||||
|
text = "Display Name",
|
||||||
|
style = MaterialTheme.typography.labelLarge
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = writeNewNoteUIState.text,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "The above will be your new note.",
|
||||||
|
style = MaterialTheme.typography.bodySmall
|
||||||
|
)
|
||||||
|
|
||||||
|
if (writeNewNoteViewModel.isActionPending.value) {
|
||||||
|
LoadingIndicator(
|
||||||
|
modifier = Modifier
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
writeNewNoteViewModel.createNewNote(
|
||||||
|
onNostrEventPublished = onNostrEventPublished
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Transmit Note"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
is WriteNewNoteUIState.Error -> {
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Something went wrong and we were unable to write a new note. Please try again later.",
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun WriteNewNoteScreenPreview() {
|
||||||
|
AuxTheme {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
WriteNewNoteScreen(
|
||||||
|
initialWriteNewNoteUIState =
|
||||||
|
WriteNewNoteUIState.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."
|
||||||
|
// ),
|
||||||
|
onNostrEventPublished = {},
|
||||||
|
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -13,6 +13,7 @@ import ac.aux.compose.ui.composable.UnannouncedProfileScreen
|
|||||||
import ac.aux.compose.ui.composable.UnindexedProfileScreen
|
import ac.aux.compose.ui.composable.UnindexedProfileScreen
|
||||||
import ac.aux.compose.ui.composable.UnqueuedProfileScreen
|
import ac.aux.compose.ui.composable.UnqueuedProfileScreen
|
||||||
import ac.aux.compose.ui.composable.UnsignedProfileScreen
|
import ac.aux.compose.ui.composable.UnsignedProfileScreen
|
||||||
|
import ac.aux.compose.ui.composable.WriteNewNoteScreen
|
||||||
import ac.aux.compose.ui.composable.navigation.routes.BlankRoute
|
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.CreateProfileRoute
|
||||||
import ac.aux.compose.ui.composable.navigation.routes.FeedRoute
|
import ac.aux.compose.ui.composable.navigation.routes.FeedRoute
|
||||||
@@ -29,6 +30,7 @@ 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.UnindexedProfileRoute
|
||||||
import ac.aux.compose.ui.composable.navigation.routes.UnqueuedProfileRoute
|
import ac.aux.compose.ui.composable.navigation.routes.UnqueuedProfileRoute
|
||||||
import ac.aux.compose.ui.composable.navigation.routes.UnsignedProfileRoute
|
import ac.aux.compose.ui.composable.navigation.routes.UnsignedProfileRoute
|
||||||
|
import ac.aux.compose.ui.composable.navigation.routes.WriteNewNoteRoute
|
||||||
import ac.aux.compose.ui.composable.widgets.feed.DUMMY_EVENTS
|
import ac.aux.compose.ui.composable.widgets.feed.DUMMY_EVENTS
|
||||||
import ac.aux.compose.ui.view.model.NavigationViewModel
|
import ac.aux.compose.ui.view.model.NavigationViewModel
|
||||||
import ac.aux.compose.ui.view.state.FeedListUIState
|
import ac.aux.compose.ui.view.state.FeedListUIState
|
||||||
@@ -47,6 +49,8 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.IO
|
import kotlinx.coroutines.IO
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AuxNavHost(
|
fun AuxNavHost(
|
||||||
@@ -68,6 +72,7 @@ fun AuxNavHost(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val applicationIOScope = CoroutineScope(Dispatchers.IO + SupervisorJob() + exceptionHandler)
|
val applicationIOScope = CoroutineScope(Dispatchers.IO + SupervisorJob() + exceptionHandler)
|
||||||
|
val applicationMainScope = CoroutineScope(Dispatchers.Main)
|
||||||
|
|
||||||
|
|
||||||
val navigationViewModel: NavigationViewModel = viewModel (
|
val navigationViewModel: NavigationViewModel = viewModel (
|
||||||
@@ -197,6 +202,16 @@ fun AuxNavHost(
|
|||||||
nostrRepository = nostrRepository
|
nostrRepository = nostrRepository
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
composable< WriteNewNoteRoute> {
|
||||||
|
WriteNewNoteScreen(
|
||||||
|
onNostrEventPublished = {
|
||||||
|
applicationMainScope.launch {
|
||||||
|
navController.popBackStack()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nostrRepository = nostrRepository
|
||||||
|
)
|
||||||
|
}
|
||||||
composable<UnsignedProfileRoute> { backStackEntry ->
|
composable<UnsignedProfileRoute> { backStackEntry ->
|
||||||
val route = backStackEntry.toRoute<UnsignedProfileRoute>()
|
val route = backStackEntry.toRoute<UnsignedProfileRoute>()
|
||||||
|
|
||||||
@@ -247,6 +262,11 @@ fun AuxNavHost(
|
|||||||
event.content
|
event.content
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
onNavigateToWriteNewNote = {
|
||||||
|
navController.navigate(
|
||||||
|
route = WriteNewNoteRoute()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/navigation/routes/WriteNewNoteRoute.kt
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
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 WriteNewNoteRoute(
|
||||||
|
val inReplyToEventId: String? = null,
|
||||||
|
): Route() {
|
||||||
|
}
|
||||||
+79
@@ -0,0 +1,79 @@
|
|||||||
|
package ac.aux.compose.ui.view.model
|
||||||
|
|
||||||
|
import ac.aux.compose.database.model.NostrEvent
|
||||||
|
import ac.aux.compose.managers.SeedManager
|
||||||
|
import ac.aux.compose.repository.NostrRepository
|
||||||
|
import ac.aux.compose.ui.view.state.WriteNewNoteUIState
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
|
import ac.aux.compose.ui.view.state.form.WriteNewNoteFormState
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import co.touchlab.kermit.Logger
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.IO
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class WriteNewNoteViewModel(
|
||||||
|
val initialWriteNewNoteUIState: WriteNewNoteUIState,
|
||||||
|
val writeNewNoteFormState: WriteNewNoteFormState = WriteNewNoteFormState(),
|
||||||
|
val nostrRepository: NostrRepository
|
||||||
|
): ViewModel() {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "WriteNewNoteViewModel"
|
||||||
|
|
||||||
|
fun factory(
|
||||||
|
initialWriteNewNoteUIState: WriteNewNoteUIState,
|
||||||
|
nostrRepository: NostrRepository
|
||||||
|
): ViewModelProvider.Factory = viewModelFactory {
|
||||||
|
initializer {
|
||||||
|
WriteNewNoteViewModel(
|
||||||
|
initialWriteNewNoteUIState,
|
||||||
|
nostrRepository = nostrRepository
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val logger = Logger.withTag(TAG)
|
||||||
|
|
||||||
|
val isActionPending: MutableState<Boolean> = mutableStateOf(false)
|
||||||
|
|
||||||
|
var writeNewNoteUIState: MutableState<WriteNewNoteUIState> = mutableStateOf(
|
||||||
|
initialWriteNewNoteUIState
|
||||||
|
)
|
||||||
|
|
||||||
|
fun validateInput(): Boolean {
|
||||||
|
if (writeNewNoteFormState.textField.text.value.isBlank()) {
|
||||||
|
writeNewNoteFormState.textField.errorMessage.value = "Please input a note"
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
writeNewNoteFormState.textField.errorMessage.value = null
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createNewNote(
|
||||||
|
onNostrEventPublished: () -> Unit
|
||||||
|
) {
|
||||||
|
logger.d { "createAccount" }
|
||||||
|
isActionPending.value = true
|
||||||
|
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
|
||||||
|
|
||||||
|
nostrRepository.createNewTextNote(
|
||||||
|
publicKey = SeedManager.activePublicKey().toHexKey(),
|
||||||
|
text = writeNewNoteFormState.textField.text.value,
|
||||||
|
onSuccess = onNostrEventPublished,
|
||||||
|
onError = {
|
||||||
|
writeNewNoteUIState.value = WriteNewNoteUIState.Error
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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 WriteNewNoteUIState {
|
||||||
|
data object Error: WriteNewNoteUIState()
|
||||||
|
|
||||||
|
data object InputPrompt: WriteNewNoteUIState()
|
||||||
|
|
||||||
|
data class ConfirmNote(
|
||||||
|
val text: String,
|
||||||
|
) : WriteNewNoteUIState()
|
||||||
|
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package ac.aux.compose.ui.view.state.form
|
||||||
|
|
||||||
|
data class WriteNewNoteFormState(
|
||||||
|
val textField: TextField = TextField(),
|
||||||
|
)
|
||||||
Reference in new issue
Block a user