Making NewPost Screen to be about Kind 1 and polls only

This commit is contained in:
Vitor Pamplona
2025-05-20 16:30:35 -04:00
parent 00048606fa
commit fcfa2bb05a
39 changed files with 449 additions and 724 deletions
@@ -94,8 +94,8 @@ import com.vitorpamplona.amethyst.ui.note.creators.uploads.ImageVideoDescription
import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.ShowUserSuggestionList import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.ShowUserSuggestionList
import com.vitorpamplona.amethyst.ui.painterRes import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.PostButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.PostButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
@@ -43,6 +43,7 @@ import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing
import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.UserSuggestionAnchor
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.ammolite.relays.RelaySetupInfo import com.vitorpamplona.ammolite.relays.RelaySetupInfo
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
@@ -70,10 +70,10 @@ import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.note.creators.contentWarning.SettingSwitchItem import com.vitorpamplona.amethyst.ui.note.creators.contentWarning.SettingSwitchItem
import com.vitorpamplona.amethyst.ui.painterRes import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.PostButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.PostButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsRow import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsRow
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Size5dp import com.vitorpamplona.amethyst.ui.theme.Size5dp
@@ -41,13 +41,13 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.ShortNotePostViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@Composable @Composable
fun NewPollClosing(pollViewModel: NewPostViewModel) { fun NewPollClosing(pollViewModel: ShortNotePostViewModel) {
var text by rememberSaveable { mutableStateOf("") } var text by rememberSaveable { mutableStateOf("") }
pollViewModel.isValidClosedAt.value = true pollViewModel.isValidClosedAt.value = true
@@ -106,5 +106,5 @@ fun NewPollClosing(pollViewModel: NewPostViewModel) {
@Preview @Preview
@Composable @Composable
fun NewPollClosingPreview() { fun NewPollClosingPreview() {
NewPollClosing(NewPostViewModel()) NewPollClosing(ShortNotePostViewModel())
} }
@@ -41,13 +41,13 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.ShortNotePostViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@Composable @Composable
fun NewPollConsensusThreshold(pollViewModel: NewPostViewModel) { fun NewPollConsensusThreshold(pollViewModel: ShortNotePostViewModel) {
var text by rememberSaveable { mutableStateOf("") } var text by rememberSaveable { mutableStateOf("") }
pollViewModel.isValidConsensusThreshold.value = true pollViewModel.isValidConsensusThreshold.value = true
@@ -106,5 +106,5 @@ fun NewPollConsensusThreshold(pollViewModel: NewPostViewModel) {
@Preview @Preview
@Composable @Composable
fun NewPollConsensusThresholdPreview() { fun NewPollConsensusThresholdPreview() {
NewPollConsensusThreshold(NewPostViewModel()) NewPollConsensusThreshold(ShortNotePostViewModel())
} }
@@ -35,12 +35,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.ShortNotePostViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
@Composable @Composable
fun NewPollOption( fun NewPollOption(
pollViewModel: NewPostViewModel, pollViewModel: ShortNotePostViewModel,
optionIndex: Int, optionIndex: Int,
) { ) {
Row { Row {
@@ -90,5 +91,5 @@ fun NewPollOption(
@Preview @Preview
@Composable @Composable
fun NewPollOptionPreview() { fun NewPollOptionPreview() {
NewPollOption(NewPostViewModel(), 0) NewPollOption(ShortNotePostViewModel(), 0)
} }
@@ -41,14 +41,14 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.style.TextDirection import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel
import com.vitorpamplona.amethyst.ui.actions.UrlUserTagTransformation import com.vitorpamplona.amethyst.ui.actions.UrlUserTagTransformation
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.ShortNotePostViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
@OptIn(ExperimentalComposeUiApi::class) @OptIn(ExperimentalComposeUiApi::class)
@Composable @Composable
fun NewPollPrimaryDescription(pollViewModel: NewPostViewModel) { fun NewPollPrimaryDescription(pollViewModel: ShortNotePostViewModel) {
// initialize focus reference to be able to request focus programmatically // initialize focus reference to be able to request focus programmatically
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current val keyboardController = LocalSoftwareKeyboardController.current
@@ -28,13 +28,13 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.ShortNotePostViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
@Composable @Composable
fun NewPollRecipientsField( fun NewPollRecipientsField(
pollViewModel: NewPostViewModel, pollViewModel: ShortNotePostViewModel,
account: Account, account: Account,
) { ) {
// if no recipients, add user's pubkey // if no recipients, add user's pubkey
@@ -39,12 +39,13 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.ShortNotePostViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
@Composable @Composable
fun NewPollVoteValueRange(pollViewModel: NewPostViewModel) { fun NewPollVoteValueRange(pollViewModel: ShortNotePostViewModel) {
val colorInValid = val colorInValid =
OutlinedTextFieldDefaults.colors( OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.error, focusedBorderColor = MaterialTheme.colorScheme.error,
@@ -122,6 +123,6 @@ fun NewPollVoteValueRangePreview() {
Column( Column(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) { ) {
NewPollVoteValueRange(NewPostViewModel()) NewPollVoteValueRange(ShortNotePostViewModel())
} }
} }
@@ -54,8 +54,8 @@ import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectSingleFromGallery import com.vitorpamplona.amethyst.ui.actions.uploads.SelectSingleFromGallery
import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.SaveButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.MinHorzSpacer import com.vitorpamplona.amethyst.ui.theme.MinHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
@@ -52,8 +52,8 @@ import com.vitorpamplona.amethyst.service.Nip11Retriever
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.SaveButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationDialog
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.FeedPadding import com.vitorpamplona.amethyst.ui.theme.FeedPadding
@@ -59,8 +59,8 @@ import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.SaveButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategoryWithButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategoryWithButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
@@ -55,7 +55,6 @@ import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountSwitcherAndLeftDrawerLayout import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountSwitcherAndLeftDrawerLayout
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.NewPostScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.bookmarks.BookmarkListScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.bookmarks.BookmarkListScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.ChatroomByAuthorScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.ChatroomByAuthorScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.ChatroomScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.ChatroomScreen
@@ -75,6 +74,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.geohash.GeoHashScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.HashtagPostScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.HashtagPostScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.HashtagScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.HashtagScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.HomeScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.HomeScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.ShortNotePostScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.NotificationScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.NotificationScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.privacy.PrivacyOptionsScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.privacy.PrivacyOptionsScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ProfileScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ProfileScreen
@@ -193,7 +193,7 @@ fun AppNavigation(
} }
composableFromBottomArgs<Route.NewPost> { composableFromBottomArgs<Route.NewPost> {
NewPostScreen( ShortNotePostScreen(
message = it.message, message = it.message,
attachment = it.attachment?.ifBlank { null }?.toUri(), attachment = it.attachment?.ifBlank { null }?.toUri(),
baseReplyTo = it.baseReplyTo?.let { hex -> accountViewModel.getNoteIfExists(hex) }, baseReplyTo = it.baseReplyTo?.let { hex -> accountViewModel.getNoteIfExists(hex) },
@@ -86,8 +86,8 @@ import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.routeFor import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.note.types.RenderEmojiPack import com.vitorpamplona.amethyst.ui.note.types.RenderEmojiPack
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.SaveButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
@@ -88,10 +88,10 @@ import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.painterRes import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.SaveButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.keyBackup.getFragmentActivity import com.vitorpamplona.amethyst.ui.screen.loggedIn.keyBackup.getFragmentActivity
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.SimpleQrCodeScanner import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.SimpleQrCodeScanner
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
@@ -67,9 +67,9 @@ import com.vitorpamplona.amethyst.service.ZapPaymentHandler
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
import com.vitorpamplona.amethyst.ui.components.toasts.multiline.UserBasedErrorMessage import com.vitorpamplona.amethyst.ui.components.toasts.multiline.UserBasedErrorMessage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
@@ -32,13 +32,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.ui.actions.NewPollOption import com.vitorpamplona.amethyst.ui.actions.NewPollOption
import com.vitorpamplona.amethyst.ui.actions.NewPollVoteValueRange import com.vitorpamplona.amethyst.ui.actions.NewPollVoteValueRange
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel
import com.vitorpamplona.amethyst.ui.painterRes import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.ShortNotePostViewModel
import com.vitorpamplona.amethyst.ui.theme.Size18Modifier import com.vitorpamplona.amethyst.ui.theme.Size18Modifier
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
@Composable @Composable
fun PollField(postViewModel: NewPostViewModel) { fun PollField(postViewModel: ShortNotePostViewModel) {
val optionsList = postViewModel.pollOptions val optionsList = postViewModel.pollOptions
Column( Column(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@@ -65,8 +65,8 @@ import com.vitorpamplona.amethyst.ui.note.ZapIcon
import com.vitorpamplona.amethyst.ui.note.ZappedIcon import com.vitorpamplona.amethyst.ui.note.ZappedIcon
import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.amethyst.ui.note.showAmount
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.PostButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.PostButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
@@ -43,7 +43,6 @@ import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator
import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
import com.vitorpamplona.amethyst.ui.actions.UserSuggestionAnchor
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing
@@ -59,6 +58,7 @@ import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.SplitBuilder
import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.toZapSplitSetup import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.toZapSplitSetup
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.UserSuggestionAnchor
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
@@ -84,9 +84,9 @@ import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapTo
import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapToButton import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapToButton
import com.vitorpamplona.amethyst.ui.painterRes import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.Notifying import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.Notifying
import com.vitorpamplona.amethyst.ui.screen.loggedIn.PostButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.PostButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Size10dp import com.vitorpamplona.amethyst.ui.theme.Size10dp
import com.vitorpamplona.amethyst.ui.theme.Size35dp import com.vitorpamplona.amethyst.ui.theme.Size35dp
@@ -49,8 +49,8 @@ import androidx.compose.ui.window.DialogProperties
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.PostButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.PostButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
@@ -40,7 +40,6 @@ import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.model.nip30CustomEmojis.EmojiPackState import com.vitorpamplona.amethyst.model.nip30CustomEmojis.EmojiPackState
import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.location.LocationState
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
import com.vitorpamplona.amethyst.ui.actions.UserSuggestionAnchor
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState
@@ -58,6 +57,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.upload
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.upload.ChatFileUploader import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.upload.ChatFileUploader
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.upload.SuccessfulUploads import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.upload.SuccessfulUploads
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.utils.ChatFileUploadState import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.utils.ChatFileUploadState
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.UserSuggestionAnchor
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohash import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohash
import com.vitorpamplona.quartz.nip01Core.tags.geohash.getGeoHash import com.vitorpamplona.quartz.nip01Core.tags.geohash.getGeoHash
@@ -115,9 +115,9 @@ import com.vitorpamplona.amethyst.ui.note.creators.zapraiser.ZapRaiserRequest
import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapTo import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapTo
import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapToButton import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapToButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.PostButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.upload.SuccessfulUploads import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.upload.SuccessfulUploads
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.PostButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.DividerThickness import com.vitorpamplona.amethyst.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.ui.theme.Font14SP import com.vitorpamplona.amethyst.ui.theme.Font14SP
@@ -53,8 +53,8 @@ import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.routeFor import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.JoinButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.JoinButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
@@ -60,9 +60,9 @@ import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.routeFor import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.note.LoadChannel import com.vitorpamplona.amethyst.ui.note.LoadChannel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CreateButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CreateButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.SaveButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog
@@ -48,7 +48,6 @@ import com.vitorpamplona.amethyst.service.location.LocationState
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
import com.vitorpamplona.amethyst.ui.actions.UserSuggestionAnchor
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState
@@ -59,6 +58,7 @@ import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.SplitBuilder
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.utils.ChatFileUploadState import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.utils.ChatFileUploadState
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.UserSuggestionAnchor
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
@@ -88,8 +88,8 @@ import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapTo
import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapToButton import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapToButton
import com.vitorpamplona.amethyst.ui.painterRes import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.PostButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.PostButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Size10dp import com.vitorpamplona.amethyst.ui.theme.Size10dp
import com.vitorpamplona.amethyst.ui.theme.Size35dp import com.vitorpamplona.amethyst.ui.theme.Size35dp
@@ -44,7 +44,6 @@ import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator
import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
import com.vitorpamplona.amethyst.ui.actions.UserSuggestionAnchor
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing
@@ -60,6 +59,7 @@ import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.SplitBuilder
import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.toZapSplitSetup import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.toZapSplitSetup
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.UserSuggestionAnchor
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
package com.vitorpamplona.amethyst.ui.screen.loggedIn package com.vitorpamplona.amethyst.ui.screen.loggedIn.home
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
@@ -61,10 +61,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -76,13 +74,13 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.util.Consumer import androidx.core.util.Consumer
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUser import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUser
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel import com.vitorpamplona.amethyst.ui.actions.RelaySelectionDialogEasy
import com.vitorpamplona.amethyst.ui.actions.RelaySelectionDialog
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
@@ -117,6 +115,7 @@ import com.vitorpamplona.amethyst.ui.note.creators.zapraiser.ZapRaiserRequest
import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapTo import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapTo
import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapToButton import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapToButton
import com.vitorpamplona.amethyst.ui.painterRes import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsRow import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsRow
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
@@ -138,15 +137,11 @@ import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3Api::class, FlowPreview::class) @OptIn(ExperimentalMaterial3Api::class, FlowPreview::class)
@Composable @Composable
fun NewPostScreen( fun ShortNotePostScreen(
message: String? = null, message: String? = null,
attachment: Uri? = null, attachment: Uri? = null,
baseReplyTo: Note? = null, baseReplyTo: Note? = null,
@@ -157,28 +152,12 @@ fun NewPostScreen(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: Nav, nav: Nav,
) { ) {
val postViewModel: NewPostViewModel = viewModel() val postViewModel: ShortNotePostViewModel = viewModel()
postViewModel.init(accountViewModel) postViewModel.init(accountViewModel)
val context = LocalContext.current val context = LocalContext.current
val activity = context.getActivity() val activity = context.getActivity()
val scrollState = rememberScrollState()
val scope = rememberCoroutineScope()
var showRelaysDialog by remember { mutableStateOf(false) }
var relayList = remember { accountViewModel.account.activeWriteRelays().toImmutableList() }
LaunchedEffect(key1 = postViewModel.draftTag) {
launch(Dispatchers.IO) {
postViewModel.draftTextChanges
.receiveAsFlow()
.debounce(1000)
.collectLatest {
postViewModel.sendDraft(relayList = relayList)
}
}
}
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
postViewModel.load(baseReplyTo, quote, fork, version, draft) postViewModel.load(baseReplyTo, quote, fork, version, draft)
@@ -194,7 +173,6 @@ fun NewPostScreen(
DisposableEffect(nav, activity) { DisposableEffect(nav, activity) {
// Microsoft's swift key sends Gifs as new actions // Microsoft's swift key sends Gifs as new actions
val consumer = val consumer =
Consumer<Intent> { intent -> Consumer<Intent> { intent ->
if (intent.action == Intent.ACTION_SEND) { if (intent.action == Intent.ACTION_SEND) {
@@ -213,6 +191,18 @@ fun NewPostScreen(
onDispose { activity.removeOnNewIntentListener(consumer) } onDispose { activity.removeOnNewIntentListener(consumer) }
} }
NewPostScreenInner(postViewModel, accountViewModel, nav)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun NewPostScreenInner(
postViewModel: ShortNotePostViewModel,
accountViewModel: AccountViewModel,
nav: Nav,
) {
val scope = rememberCoroutineScope()
WatchAndLoadMyEmojiList(accountViewModel) WatchAndLoadMyEmojiList(accountViewModel)
Scaffold( Scaffold(
@@ -222,14 +212,14 @@ fun NewPostScreen(
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically, verticalAlignment = CenterVertically,
) { ) {
Spacer(modifier = StdHorzSpacer) Spacer(modifier = StdHorzSpacer)
Box { Box {
IconButton( IconButton(
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
onClick = { showRelaysDialog = true }, onClick = { postViewModel.showRelaysDialog = true },
) { ) {
Icon( Icon(
painter = painterRes(R.drawable.relays), painter = painterRes(R.drawable.relays),
@@ -241,8 +231,10 @@ fun NewPostScreen(
} }
PostButton( PostButton(
onPost = { onPost = {
postViewModel.sendPost(relayList = relayList) // uses the accountViewModel scope to avoid cancelling this
scope.launch { // function when the postViewModel is released
accountViewModel.viewModelScope.launch(Dispatchers.IO) {
postViewModel.sendPostSync()
delay(100) delay(100)
nav.popBack() nav.popBack()
} }
@@ -256,13 +248,12 @@ fun NewPostScreen(
Spacer(modifier = StdHorzSpacer) Spacer(modifier = StdHorzSpacer)
CloseButton( CloseButton(
onPress = { onPress = {
scope.launch { // uses the accountViewModel scope to avoid cancelling this
withContext(Dispatchers.IO) { // function when the postViewModel is released
postViewModel.sendDraftSync(relayList = relayList) accountViewModel.viewModelScope.launch(Dispatchers.IO) {
postViewModel.cancel() postViewModel.sendDraftSync()
}
delay(100)
nav.popBack() nav.popBack()
postViewModel.cancel()
} }
}, },
) )
@@ -275,11 +266,11 @@ fun NewPostScreen(
) )
}, },
) { pad -> ) { pad ->
if (showRelaysDialog) { if (postViewModel.showRelaysDialog) {
RelaySelectionDialog( RelaySelectionDialogEasy(
preSelectedList = relayList, preSelectedList = postViewModel.relayList ?: persistentListOf(),
onClose = { showRelaysDialog = false }, onClose = { postViewModel.showRelaysDialog = false },
onPost = { relayList = it }, onPost = { postViewModel.relayList = it.map { it.url }.toImmutableList() },
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -291,199 +282,210 @@ fun NewPostScreen(
.consumeWindowInsets(pad) .consumeWindowInsets(pad)
.imePadding(), .imePadding(),
) { ) {
Column( NewPostScreenBody(postViewModel, accountViewModel, nav)
modifier =
Modifier.fillMaxSize(),
) {
Row(
modifier =
Modifier
.fillMaxWidth()
.padding(
start = Size10dp,
end = Size10dp,
).weight(1f),
) {
Column(
modifier =
Modifier
.fillMaxWidth()
.verticalScroll(scrollState),
) {
postViewModel.originalNote?.let {
Row {
NoteCompose(
baseNote = it,
modifier = MaterialTheme.colorScheme.replyModifier,
isQuotedNote = true,
unPackReply = false,
makeItShort = true,
quotesLeft = 1,
accountViewModel = accountViewModel,
nav = nav,
)
Spacer(modifier = StdVertSpacer)
}
}
Row {
Notifying(postViewModel.pTags?.toImmutableList(), accountViewModel) {
postViewModel.removeFromReplyList(it)
}
}
Row(
modifier = Modifier.padding(vertical = Size10dp),
) {
BaseUserPicture(
accountViewModel.userProfile(),
Size35dp,
accountViewModel = accountViewModel,
)
MessageField(
R.string.what_s_on_your_mind,
postViewModel,
)
}
if (postViewModel.wantsPoll) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp),
) {
PollField(postViewModel)
}
}
DisplayPreviews(postViewModel, accountViewModel, nav)
if (postViewModel.wantsToMarkAsSensitive) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp),
) {
ContentSensitivityExplainer()
}
}
if (postViewModel.wantsToAddGeoHash) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp),
) {
LocationAsHash(postViewModel) {
SettingsRow(
R.string.geohash_exclusive,
R.string.geohash_exclusive_explainer,
) {
Switch(postViewModel.wantsExclusiveGeoPost, onCheckedChange = { postViewModel.wantsExclusiveGeoPost = it })
}
}
}
}
if (postViewModel.wantsForwardZapTo) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(top = Size5dp, bottom = Size5dp, start = Size10dp),
) {
ForwardZapTo(postViewModel, accountViewModel)
}
}
postViewModel.multiOrchestrator?.let {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp),
) {
ImageVideoDescription(
it,
accountViewModel.account.settings.defaultFileServer,
onAdd = { alt, server, sensitiveContent, mediaQuality ->
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, false, server, accountViewModel.toastManager::toast, context)
if (server.type != ServerType.NIP95) {
accountViewModel.account.settings.changeDefaultFileServer(server)
}
},
onDelete = postViewModel::deleteMediaToUpload,
onCancel = { postViewModel.multiOrchestrator = null },
accountViewModel = accountViewModel,
)
}
}
if (postViewModel.wantsInvoice) {
postViewModel.lnAddress()?.let { lud16 ->
InvoiceRequest(
lud16,
accountViewModel.account.userProfile().pubkeyHex,
accountViewModel,
stringRes(id = R.string.lightning_invoice),
stringRes(id = R.string.lightning_create_and_add_invoice),
onSuccess = {
postViewModel.insertAtCursor(it)
postViewModel.wantsInvoice = false
},
onError = { title, message -> accountViewModel.toastManager.toast(title, message) },
)
}
}
if (postViewModel.wantsSecretEmoji) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp),
) {
Column(Modifier.fillMaxWidth()) {
SecretEmojiRequest {
postViewModel.insertAtCursor(it)
postViewModel.wantsSecretEmoji = false
}
}
}
}
if (postViewModel.wantsZapraiser && postViewModel.hasLnAddress()) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp),
) {
ZapRaiserRequest(
stringRes(id = R.string.zapraiser),
postViewModel,
)
}
}
}
}
postViewModel.userSuggestions?.let {
ShowUserSuggestionList(
it,
postViewModel::autocompleteWithUser,
accountViewModel,
modifier = Modifier.heightIn(0.dp, 300.dp),
)
}
postViewModel.emojiSuggestions?.let {
ShowEmojiSuggestionList(
it,
postViewModel::autocompleteWithEmoji,
postViewModel::autocompleteWithEmojiUrl,
accountViewModel,
modifier = Modifier.heightIn(0.dp, 300.dp),
)
}
BottomRowActions(postViewModel)
}
} }
} }
} }
@Composable @Composable
private fun BottomRowActions(postViewModel: NewPostViewModel) { private fun NewPostScreenBody(
postViewModel: ShortNotePostViewModel,
accountViewModel: AccountViewModel,
nav: Nav,
) {
val scrollState = rememberScrollState()
Column(
modifier =
Modifier.fillMaxSize(),
) {
Row(
modifier =
Modifier
.fillMaxWidth()
.padding(
start = Size10dp,
end = Size10dp,
).weight(1f),
) {
Column(
modifier =
Modifier
.fillMaxWidth()
.verticalScroll(scrollState),
) {
postViewModel.originalNote?.let {
Row {
NoteCompose(
baseNote = it,
modifier = MaterialTheme.colorScheme.replyModifier,
isQuotedNote = true,
unPackReply = false,
makeItShort = true,
quotesLeft = 1,
accountViewModel = accountViewModel,
nav = nav,
)
Spacer(modifier = StdVertSpacer)
}
}
Row {
Notifying(postViewModel.pTags?.toImmutableList(), accountViewModel) {
postViewModel.removeFromReplyList(it)
}
}
Row(
modifier = Modifier.padding(vertical = Size10dp),
) {
BaseUserPicture(
accountViewModel.userProfile(),
Size35dp,
accountViewModel = accountViewModel,
)
MessageField(
R.string.what_s_on_your_mind,
postViewModel,
)
}
if (postViewModel.wantsPoll) {
Row(
verticalAlignment = CenterVertically,
modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp),
) {
PollField(postViewModel)
}
}
DisplayPreviews(postViewModel, accountViewModel, nav)
if (postViewModel.wantsToMarkAsSensitive) {
Row(
verticalAlignment = CenterVertically,
modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp),
) {
ContentSensitivityExplainer()
}
}
if (postViewModel.wantsToAddGeoHash) {
Row(
verticalAlignment = CenterVertically,
modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp),
) {
LocationAsHash(postViewModel) {
SettingsRow(
R.string.geohash_exclusive,
R.string.geohash_exclusive_explainer,
) {
Switch(postViewModel.wantsExclusiveGeoPost, onCheckedChange = { postViewModel.wantsExclusiveGeoPost = it })
}
}
}
}
if (postViewModel.wantsForwardZapTo) {
Row(
verticalAlignment = CenterVertically,
modifier = Modifier.padding(top = Size5dp, bottom = Size5dp, start = Size10dp),
) {
ForwardZapTo(postViewModel, accountViewModel)
}
}
postViewModel.multiOrchestrator?.let {
Row(
verticalAlignment = CenterVertically,
modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp),
) {
val context = LocalContext.current
ImageVideoDescription(
it,
accountViewModel.account.settings.defaultFileServer,
onAdd = { alt, server, sensitiveContent, mediaQuality ->
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, server, accountViewModel.toastManager::toast, context)
if (server.type != ServerType.NIP95) {
accountViewModel.account.settings.changeDefaultFileServer(server)
}
},
onDelete = postViewModel::deleteMediaToUpload,
onCancel = { postViewModel.multiOrchestrator = null },
accountViewModel = accountViewModel,
)
}
}
if (postViewModel.wantsInvoice) {
postViewModel.lnAddress()?.let { lud16 ->
InvoiceRequest(
lud16,
accountViewModel.account.userProfile().pubkeyHex,
accountViewModel,
stringRes(id = R.string.lightning_invoice),
stringRes(id = R.string.lightning_create_and_add_invoice),
onSuccess = {
postViewModel.insertAtCursor(it)
postViewModel.wantsInvoice = false
},
onError = { title, message -> accountViewModel.toastManager.toast(title, message) },
)
}
}
if (postViewModel.wantsSecretEmoji) {
Row(
verticalAlignment = CenterVertically,
modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp),
) {
Column(Modifier.fillMaxWidth()) {
SecretEmojiRequest {
postViewModel.insertAtCursor(it)
postViewModel.wantsSecretEmoji = false
}
}
}
}
if (postViewModel.wantsZapraiser && postViewModel.hasLnAddress()) {
Row(
verticalAlignment = CenterVertically,
modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp),
) {
ZapRaiserRequest(
stringRes(id = R.string.zapraiser),
postViewModel,
)
}
}
}
}
postViewModel.userSuggestions?.let {
ShowUserSuggestionList(
it,
postViewModel::autocompleteWithUser,
accountViewModel,
modifier = Modifier.heightIn(0.dp, 300.dp),
)
}
postViewModel.emojiSuggestions?.let {
ShowEmojiSuggestionList(
it,
postViewModel::autocompleteWithEmoji,
postViewModel::autocompleteWithEmojiUrl,
accountViewModel,
modifier = Modifier.heightIn(0.dp, 300.dp),
)
}
BottomRowActions(postViewModel)
}
}
@Composable
private fun BottomRowActions(postViewModel: ShortNotePostViewModel) {
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
Row( Row(
modifier = modifier =
@@ -548,7 +550,7 @@ private fun BottomRowActions(postViewModel: NewPostViewModel) {
@Composable @Composable
fun DisplayPreviews( fun DisplayPreviews(
postViewModel: NewPostViewModel, postViewModel: ShortNotePostViewModel,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
package com.vitorpamplona.amethyst.ui.actions package com.vitorpamplona.amethyst.ui.screen.loggedIn.home
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
@@ -38,20 +38,19 @@ import com.vitorpamplona.amethyst.commons.compose.currentWord
import com.vitorpamplona.amethyst.commons.compose.insertUrlAtCursor import com.vitorpamplona.amethyst.commons.compose.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.compose.replaceCurrentWord import com.vitorpamplona.amethyst.commons.compose.replaceCurrentWord
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LiveActivitiesChannel
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.PublicChatChannel
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.model.nip30CustomEmojis.EmojiPackState
import com.vitorpamplona.amethyst.model.nip30CustomEmojis.EmojiPackState.EmojiMedia import com.vitorpamplona.amethyst.model.nip30CustomEmojis.EmojiPackState.EmojiMedia
import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.location.LocationState
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator
import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing
import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState
import com.vitorpamplona.amethyst.ui.note.creators.emojiSuggestions.EmojiSuggestionState import com.vitorpamplona.amethyst.ui.note.creators.emojiSuggestions.EmojiSuggestionState
import com.vitorpamplona.amethyst.ui.note.creators.location.ILocationGrabber import com.vitorpamplona.amethyst.ui.note.creators.location.ILocationGrabber
import com.vitorpamplona.amethyst.ui.note.creators.messagefield.IMessageField import com.vitorpamplona.amethyst.ui.note.creators.messagefield.IMessageField
@@ -64,7 +63,6 @@ import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.toZapSplitSetup
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send.IMetaAttachments
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.ammolite.relays.RelaySetupInfo
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
@@ -76,9 +74,7 @@ import com.vitorpamplona.quartz.experimental.zapPolls.tags.PollOptionTag
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.eTags
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohash import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohash
import com.vitorpamplona.quartz.nip01Core.tags.geohash.getGeoHash import com.vitorpamplona.quartz.nip01Core.tags.geohash.getGeoHash
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
@@ -90,27 +86,16 @@ import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
import com.vitorpamplona.quartz.nip10Notes.content.findNostrUris import com.vitorpamplona.quartz.nip10Notes.content.findNostrUris
import com.vitorpamplona.quartz.nip10Notes.content.findURLs import com.vitorpamplona.quartz.nip10Notes.content.findURLs
import com.vitorpamplona.quartz.nip10Notes.tags.notify import com.vitorpamplona.quartz.nip10Notes.tags.notify
import com.vitorpamplona.quartz.nip10Notes.tags.positionalMarkedTags
import com.vitorpamplona.quartz.nip18Reposts.quotes.quotes import com.vitorpamplona.quartz.nip18Reposts.quotes.quotes
import com.vitorpamplona.quartz.nip18Reposts.quotes.taggedQuoteIds
import com.vitorpamplona.quartz.nip22Comments.CommentEvent import com.vitorpamplona.quartz.nip22Comments.CommentEvent
import com.vitorpamplona.quartz.nip22Comments.RootScope
import com.vitorpamplona.quartz.nip22Comments.notify
import com.vitorpamplona.quartz.nip28PublicChat.base.notify
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
import com.vitorpamplona.quartz.nip34Git.issue.GitIssueEvent
import com.vitorpamplona.quartz.nip34Git.reply.GitReplyEvent
import com.vitorpamplona.quartz.nip34Git.reply.notify
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
import com.vitorpamplona.quartz.nip53LiveActivities.chat.notify
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetupLnAddress import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetupLnAddress
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup
@@ -118,7 +103,6 @@ import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiser import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiser
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
import com.vitorpamplona.quartz.nip73ExternalIds.location.GeohashId
import com.vitorpamplona.quartz.nip92IMeta.IMetaTagBuilder import com.vitorpamplona.quartz.nip92IMeta.IMetaTagBuilder
import com.vitorpamplona.quartz.nip92IMeta.imetas import com.vitorpamplona.quartz.nip92IMeta.imetas
import com.vitorpamplona.quartz.nip94FileMetadata.alt import com.vitorpamplona.quartz.nip94FileMetadata.alt
@@ -131,12 +115,11 @@ import com.vitorpamplona.quartz.nip94FileMetadata.originalHash
import com.vitorpamplona.quartz.nip94FileMetadata.sensitiveContent import com.vitorpamplona.quartz.nip94FileMetadata.sensitiveContent
import com.vitorpamplona.quartz.nip94FileMetadata.size import com.vitorpamplona.quartz.nip94FileMetadata.size
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.UUID
enum class UserSuggestionAnchor { enum class UserSuggestionAnchor {
MAIN_MESSAGE, MAIN_MESSAGE,
@@ -145,13 +128,21 @@ enum class UserSuggestionAnchor {
} }
@Stable @Stable
open class NewPostViewModel : open class ShortNotePostViewModel :
ViewModel(), ViewModel(),
ILocationGrabber, ILocationGrabber,
IMessageField, IMessageField,
IZapField, IZapField,
IZapRaiser { IZapRaiser {
var draftTag: String by mutableStateOf(UUID.randomUUID().toString()) val draftTag = DraftTagState()
init {
viewModelScope.launch(Dispatchers.IO) {
draftTag.versions.collectLatest {
sendDraftSync()
}
}
}
var accountViewModel: AccountViewModel? = null var accountViewModel: AccountViewModel? = null
var account: Account? = null var account: Account? = null
@@ -220,7 +211,8 @@ open class NewPostViewModel :
var wantsZapraiser by mutableStateOf(false) var wantsZapraiser by mutableStateOf(false)
override val zapRaiserAmount = mutableStateOf<Long?>(null) override val zapRaiserAmount = mutableStateOf<Long?>(null)
val draftTextChanges = Channel<String>(Channel.CONFLATED) var showRelaysDialog by mutableStateOf(false)
var relayList by mutableStateOf<ImmutableList<String>?>(null)
fun lnAddress(): String? = account?.userProfile()?.info?.lnAddress() fun lnAddress(): String? = account?.userProfile()?.info?.lnAddress()
@@ -258,9 +250,9 @@ open class NewPostViewModel :
if (innerNote != null) { if (innerNote != null) {
val oldTag = (draft.event as? AddressableEvent)?.dTag() val oldTag = (draft.event as? AddressableEvent)?.dTag()
if (oldTag != null) { if (oldTag != null) {
draftTag = oldTag draftTag.set(oldTag)
} }
loadFromDraft(innerNote, accountViewModel) loadFromDraft(innerNote)
} }
} }
} }
@@ -372,15 +364,16 @@ open class NewPostViewModel :
urlPreviews.update(message) urlPreviews.update(message)
} }
private fun loadFromDraft( private fun loadFromDraft(draft: Note) {
draft: Note,
accountViewModel: AccountViewModel,
) {
Log.d("draft", draft.event!!.toJson())
val draftEvent = draft.event ?: return val draftEvent = draft.event ?: return
if (draftEvent !is TextNoteEvent) return
canAddInvoice = accountViewModel.userProfile().info?.lnAddress() != null loadFromDraft(draftEvent)
canAddZapRaiser = accountViewModel.userProfile().info?.lnAddress() != null }
private fun loadFromDraft(draftEvent: TextNoteEvent) {
canAddInvoice = accountViewModel?.userProfile()?.info?.lnAddress() != null
canAddZapRaiser = accountViewModel?.userProfile()?.info?.lnAddress() != null
multiOrchestrator = null multiOrchestrator = null
val localfowardZapTo = draftEvent.tags.filter { it.size > 1 && it[0] == "zap" } val localfowardZapTo = draftEvent.tags.filter { it.size > 1 && it[0] == "zap" }
@@ -471,38 +464,59 @@ open class NewPostViewModel :
urlPreviews.update(message) urlPreviews.update(message)
} }
fun sendPost(relayList: List<RelaySetupInfo>) { suspend fun sendPostSync() {
viewModelScope.launch(Dispatchers.IO) { val template = createTemplate() ?: return
innerSendPost(relayList, null) val relayList = relayList
accountViewModel?.deleteDraft(draftTag)
cancel() if (nip95attachments.isNotEmpty() && relayList != null) {
val usedImages = template.tags.taggedQuoteIds().toSet()
nip95attachments.forEach {
if (usedImages.contains(it.second.id) == true) {
account?.sendNip95Privately(it.first, it.second, relayList)
}
}
} }
accountViewModel?.account?.signAndSendPrivatelyOrBroadcast(
template,
relayList = { relayList },
)
accountViewModel?.deleteDraft(draftTag.current)
cancel()
} }
fun sendDraft(relayList: List<RelaySetupInfo>) { suspend fun sendDraftSync() {
viewModelScope.launch(Dispatchers.IO) { val accountViewModel = accountViewModel ?: return
sendDraftSync(relayList)
}
}
suspend fun sendDraftSync(relayList: List<RelaySetupInfo>) {
if (message.text.isBlank()) { if (message.text.isBlank()) {
account?.deleteDraft(draftTag) accountViewModel.account.deleteDraft(draftTag.current)
} else { } else {
innerSendPost(relayList, draftTag) val template = createTemplate() ?: return
accountViewModel.account.createAndSendDraft(draftTag.current, template)
nip95attachments.forEach {
account?.sendToPrivateOutboxAndLocal(it.first)
account?.sendToPrivateOutboxAndLocal(it.second)
}
} }
} }
private suspend fun innerSendPost( private suspend fun createTemplate(): EventTemplate<out Event>? {
relayList: List<RelaySetupInfo>,
localDraft: String?,
) = withContext(Dispatchers.IO) {
if (accountViewModel == null) { if (accountViewModel == null) {
cancel() cancel()
return@withContext return null
} }
val tagger = NewMessageTagger(message.text, pTags, eTags, originalNote?.channelHex(), accountViewModel!!) val tagger =
NewMessageTagger(
message.text,
pTags,
eTags,
originalNote?.channelHex(),
accountViewModel!!,
)
tagger.run() tagger.run()
val zapReceiver = if (wantsForwardZapTo) forwardZapTo.value.toZapSplitSetup() else null val zapReceiver = if (wantsForwardZapTo) forwardZapTo.value.toZapSplitSetup() else null
@@ -510,367 +524,56 @@ open class NewPostViewModel :
val geoHash = (location?.value as? LocationState.LocationResult.Success)?.geoHash?.toString() val geoHash = (location?.value as? LocationState.LocationResult.Success)?.geoHash?.toString()
val localZapRaiserAmount = if (wantsZapraiser) zapRaiserAmount.value else null val localZapRaiserAmount = if (wantsZapraiser) zapRaiserAmount.value else null
nip95attachments.forEach {
if (eTags?.contains(LocalCache.getNoteIfExists(it.second.id)) == true) {
account?.sendNip95(it.first, it.second, relayList)
}
}
val emojis = findEmoji(tagger.message, account?.emoji?.myEmojis?.value) val emojis = findEmoji(tagger.message, account?.emoji?.myEmojis?.value)
val urls = findURLs(tagger.message) val urls = findURLs(tagger.message)
val usedAttachments = iMetaAttachments.filterIsIn(urls.toSet()) val usedAttachments = iMetaAttachments.filterIsIn(urls.toSet())
val replyingTo = originalNote
val contentWarningReason = if (wantsToMarkAsSensitive) "" else null val contentWarningReason = if (wantsToMarkAsSensitive) "" else null
val channel = originalNote?.channelHex()?.let { LocalCache.getChannelIfExists(it) } return if (wantsPoll) {
val options = pollOptions.map { PollOptionTag(it.key, it.value) }
if (replyingTo?.event is CommentEvent || replyingTo?.event is RootScope) { if (options.isEmpty()) return null
val eventHint = replyingTo.toEventHint<Event>() ?: return@withContext
val template = val quotes = findNostrUris(tagger.message)
CommentEvent.replyBuilder(
msg = tagger.message,
replyingTo = eventHint,
) {
tagger.pTags?.let { notify(it.map { it.toPTag() }) }
hashtags(findHashtags(tagger.message)) PollNoteEvent.build(tagger.message, options) {
references(findURLs(tagger.message)) valueMinimum?.let { minAmount(it) }
quotes(findNostrUris(tagger.message)) valueMaximum?.let { maxAmount(it) }
closedAt?.let { closedAt(it) }
consensusThreshold?.let { consensusThreshold(it / 100.0) }
geoHash?.let { geohash(it) } pTags(tagger.directMentionsUsers.map { it.toPTag() })
localZapRaiserAmount?.let { zapraiser(it) } quotes(quotes)
zapReceiver?.let { zapSplits(it) } hashtags(findHashtags(tagger.message))
contentWarningReason?.let { contentWarning(it) }
emojis(emojis) geoHash?.let { geohash(it) }
imetas(usedAttachments) localZapRaiserAmount?.let { zapraiser(it) }
} zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
account?.signAndSend(localDraft, template, relayList, setOf(replyingTo)) emojis(emojis)
} else if (wantsExclusiveGeoPost && geoHash != null && originalNote == null) { imetas(usedAttachments)
val template =
CommentEvent.replyExternalIdentity(
msg = tagger.message,
extId = GeohashId(geoHash),
) {
tagger.pTags?.let { notify(it.map { it.toPTag() }) }
hashtags(findHashtags(tagger.message))
references(findURLs(tagger.message))
quotes(findNostrUris(tagger.message))
localZapRaiserAmount?.let { zapraiser(it) }
zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
emojis(emojis)
imetas(usedAttachments)
}
account?.signAndSend(localDraft, template, relayList, emptyList())
} else if (channel != null) {
if (channel is PublicChatChannel) {
val replyingToEvent = originalNote?.toEventHint<ChannelMessageEvent>()
val channelEvent = channel.event
val channelRelays = channel.relays()
val template =
if (replyingToEvent != null) {
ChannelMessageEvent.reply(tagger.message, replyingToEvent) {
tagger.pTags?.let { notify(it.map { it.toPTag() }) }
hashtags(findHashtags(tagger.message))
references(findURLs(tagger.message))
quotes(findNostrUris(tagger.message))
geoHash?.let { geohash(it) }
localZapRaiserAmount?.let { zapraiser(it) }
zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
emojis(emojis)
imetas(usedAttachments)
}
} else if (channelEvent != null) {
val hint = EventHintBundle(channelEvent, channelRelays.firstOrNull())
ChannelMessageEvent.message(tagger.message, hint) {
tagger.pTags?.let { notify(it.map { it.toPTag() }) }
hashtags(findHashtags(tagger.message))
references(findURLs(tagger.message))
quotes(findNostrUris(tagger.message))
geoHash?.let { geohash(it) }
localZapRaiserAmount?.let { zapraiser(it) }
zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
emojis(emojis)
imetas(usedAttachments)
}
} else {
ChannelMessageEvent.message(tagger.message, ETag(channel.idHex, channelRelays.firstOrNull())) {
tagger.pTags?.let { notify(it.map { it.toPTag() }) }
hashtags(findHashtags(tagger.message))
references(findURLs(tagger.message))
quotes(findNostrUris(tagger.message))
geoHash?.let { geohash(it) }
localZapRaiserAmount?.let { zapraiser(it) }
zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
emojis(emojis)
imetas(usedAttachments)
}
}
val broadcast = tagger.directMentionsNotes + (tagger.eTags ?: emptyList())
account?.signAndSendWithList(draftTag, template, channelRelays, broadcast)
} else if (channel is LiveActivitiesChannel) {
val replyingToEvent = originalNote?.toEventHint<LiveActivitiesChatMessageEvent>()
val activity = channel.info
val channelRelays = channel.relays()
val template =
if (replyingToEvent != null) {
LiveActivitiesChatMessageEvent.reply(tagger.message, replyingToEvent) {
tagger.pTags?.let { notify(it.map { it.toPTag() }) }
hashtags(findHashtags(tagger.message))
references(findURLs(tagger.message))
quotes(findNostrUris(tagger.message))
geoHash?.let { geohash(it) }
localZapRaiserAmount?.let { zapraiser(it) }
zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
emojis(emojis)
imetas(usedAttachments)
}
} else if (activity != null) {
val hint = EventHintBundle(activity, channelRelays.firstOrNull() ?: replyingToEvent?.relay)
LiveActivitiesChatMessageEvent.message(tagger.message, hint) {
tagger.pTags?.let { notify(it.map { it.toPTag() }) }
hashtags(findHashtags(tagger.message))
references(findURLs(tagger.message))
quotes(findNostrUris(tagger.message))
geoHash?.let { geohash(it) }
localZapRaiserAmount?.let { zapraiser(it) }
zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
emojis(emojis)
imetas(usedAttachments)
}
} else {
LiveActivitiesChatMessageEvent.message(tagger.message, channel.toATag()) {
tagger.pTags?.let { notify(it.map { it.toPTag() }) }
hashtags(findHashtags(tagger.message))
references(findURLs(tagger.message))
quotes(findNostrUris(tagger.message))
geoHash?.let { geohash(it) }
localZapRaiserAmount?.let { zapraiser(it) }
zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
emojis(emojis)
imetas(usedAttachments)
}
}
val broadcast = tagger.directMentionsNotes + (tagger.eTags ?: emptyList())
account?.signAndSendWithList(draftTag, template, channelRelays, broadcast)
} }
} else if (originalNote?.event is GitIssueEvent) {
val originalNoteHint = originalNote?.toEventHint<GitIssueEvent>() ?: return@withContext
val template =
GitReplyEvent.replyIssue(
tagger.message,
originalNoteHint,
) {
tagger.pTags?.let { notify(it.map { it.toPTag() }) }
hashtags(findHashtags(tagger.message))
references(findURLs(tagger.message))
quotes(findNostrUris(tagger.message))
geoHash?.let { geohash(it) }
localZapRaiserAmount?.let { zapraiser(it) }
zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
emojis(emojis)
imetas(usedAttachments)
}
val broadcast = tagger.directMentionsNotes + (tagger.eTags ?: emptyList())
account?.signAndSend(localDraft, template, relayList, broadcast)
} else if (originalNote?.event is TorrentCommentEvent) {
val replyToEvent = originalNote?.event as TorrentCommentEvent
val rootETag = replyToEvent.torrent()
val rootNote = rootETag?.eventId?.let { LocalCache.getNoteIfExists(it) }
val rootNoteEvent = rootNote?.event
// only uses the root node if the event is loaded.
val root =
if (rootNoteEvent != null) {
rootNote // refreshes author and relay hint to what we have.
} else {
rootETag?.let { LocalCache.getOrCreateNote(it) } // keeps what came in.
?: originalNote?.replyTo?.firstOrNull { it.event != null && it.replyTo?.isEmpty() == true } // if it has loaded events with zero replies in the reply list
?: originalNote?.replyTo?.firstOrNull() // old rules, first item is root.
?: originalNote
}
if (root != null) {
val replyToSet =
if (forkedFromNote != null) {
(listOfNotNull(forkedFromNote) + (tagger.eTags ?: emptyList())).ifEmpty { null }
} else {
tagger.eTags
}
val sortedAndMarked =
eTags?.map { it.toETag() }?.positionalMarkedTags(
root = root.toETag(),
replyingTo = replyingTo?.toETag(),
forkedFrom = forkedFromNote?.toETag(),
)
val template =
TorrentCommentEvent.build(tagger.message) {
sortedAndMarked?.let { eTags(sortedAndMarked) }
pTags(tagger.directMentionsUsers.map { it.toPTag() })
hashtags(findHashtags(tagger.message))
references(findURLs(tagger.message))
quotes(findNostrUris(tagger.message))
geoHash?.let { geohash(it) }
localZapRaiserAmount?.let { zapraiser(it) }
zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
emojis(emojis)
imetas(usedAttachments)
}
val broadcast = tagger.directMentionsNotes + (replyToSet ?: emptySet())
account?.sendTorrentComment(localDraft, template, broadcast, relayList)
}
} else if (originalNote?.event is TorrentEvent) {
val replyToSet =
if (forkedFromNote != null) {
(listOfNotNull(forkedFromNote) + (tagger.eTags ?: emptyList())).ifEmpty { null }
} else {
tagger.eTags
}
val sortedAndMarked =
eTags?.map { it.toETag() }?.positionalMarkedTags(
root = originalNote?.toETag(),
replyingTo = null,
forkedFrom = forkedFromNote?.toETag(),
)
val template =
TorrentCommentEvent.build(tagger.message) {
sortedAndMarked?.let { eTags(sortedAndMarked) }
hashtags(findHashtags(tagger.message))
references(findURLs(tagger.message))
quotes(findNostrUris(tagger.message))
geoHash?.let { geohash(it) }
localZapRaiserAmount?.let { zapraiser(it) }
zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
emojis(emojis)
imetas(usedAttachments)
}
val broadcast = tagger.directMentionsNotes + (replyToSet ?: emptySet())
account?.sendTorrentComment(localDraft, template, broadcast, relayList)
} else { } else {
if (wantsPoll) { TextNoteEvent.build(
val options = pollOptions.map { PollOptionTag(it.key, it.value) } note = tagger.message,
replyingTo = originalNote?.toEventHint<TextNoteEvent>(),
forkingFrom = forkedFromNote?.toEventHint<TextNoteEvent>(),
) {
tagger.pTags?.let { notify(it.map { it.toPTag() }) }
if (options.isEmpty()) return@withContext hashtags(findHashtags(tagger.message))
references(findURLs(tagger.message))
quotes(findNostrUris(tagger.message))
val quotes = findNostrUris(tagger.message) geoHash?.let { geohash(it) }
localZapRaiserAmount?.let { zapraiser(it) }
zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
val template = emojis(emojis)
PollNoteEvent.build(tagger.message, options) { imetas(usedAttachments)
valueMinimum?.let { minAmount(it) }
valueMaximum?.let { maxAmount(it) }
closedAt?.let { closedAt(it) }
consensusThreshold?.let { consensusThreshold(it / 100.0) }
pTags(tagger.directMentionsUsers.map { it.toPTag() })
quotes(quotes)
hashtags(findHashtags(tagger.message))
geoHash?.let { geohash(it) }
localZapRaiserAmount?.let { zapraiser(it) }
zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
emojis(emojis)
imetas(usedAttachments)
}
account?.signAndSend(localDraft, template, relayList, quotes)
} else {
val replyToSet =
if (forkedFromNote != null) {
(listOfNotNull(forkedFromNote) + (tagger.eTags ?: emptyList())).ifEmpty { null }
} else {
tagger.eTags
}
val template =
TextNoteEvent.build(
note = tagger.message,
replyingTo = originalNote?.toEventHint<TextNoteEvent>(),
forkingFrom = forkedFromNote?.toEventHint<TextNoteEvent>(),
) {
tagger.pTags?.let { notify(it.map { it.toPTag() }) }
hashtags(findHashtags(tagger.message))
references(findURLs(tagger.message))
quotes(findNostrUris(tagger.message))
geoHash?.let { geohash(it) }
localZapRaiserAmount?.let { zapraiser(it) }
zapReceiver?.let { zapSplits(it) }
contentWarningReason?.let { contentWarning(it) }
emojis(emojis)
imetas(usedAttachments)
}
val broadcast = tagger.directMentionsNotes + (replyToSet ?: emptySet())
account?.signAndSend(localDraft, template, relayList, broadcast)
} }
} }
} }
@@ -889,7 +592,6 @@ open class NewPostViewModel :
alt: String?, alt: String?,
contentWarningReason: String?, contentWarningReason: String?,
mediaQuality: Int, mediaQuality: Int,
isPrivate: Boolean = false,
server: ServerName, server: ServerName,
onError: (title: String, message: String) -> Unit, onError: (title: String, message: String) -> Unit,
context: Context, context: Context,
@@ -1000,13 +702,27 @@ open class NewPostViewModel :
emojiSuggestions?.reset() emojiSuggestions?.reset()
draftTag = UUID.randomUUID().toString() showRelaysDialog = false
reloadRelaySet()
draftTag.rotate()
} }
fun deleteDraft() { fun reloadRelaySet() {
viewModelScope.launch(Dispatchers.IO) { val account = accountViewModel?.account ?: return
accountViewModel?.deleteDraft(draftTag)
} val nip65 = account.normalizedNIP65WriteRelayList.value
val private = account.normalizedPrivateOutBoxRelaySet.value
val local = account.settings.localRelayServers
relayList =
if (nip65.isEmpty()) {
account.activeWriteRelays().map { it.url }.toImmutableList()
} else {
val combined: Set<String> = (nip65 + private + local)
combined.toImmutableList()
}
} }
fun deleteMediaToUpload(selected: SelectedMediaProcessing) { fun deleteMediaToUpload(selected: SelectedMediaProcessing) {
@@ -1017,10 +733,6 @@ open class NewPostViewModel :
pTags = pTags?.filter { it != userToRemove } pTags = pTags?.filter { it != userToRemove }
} }
private fun saveDraft() {
draftTextChanges.trySend("")
}
open fun addToMessage(it: String) { open fun addToMessage(it: String) {
updateMessage(TextFieldValue(message.text + " " + it)) updateMessage(TextFieldValue(message.text + " " + it))
} }
@@ -1038,7 +750,7 @@ open class NewPostViewModel :
emojiSuggestions?.processCurrentWord(lastWord) emojiSuggestions?.processCurrentWord(lastWord)
} }
saveDraft() draftTag.newVersion()
} }
override fun updateZapForwardTo(newZapForwardTo: TextFieldValue) { override fun updateZapForwardTo(newZapForwardTo: TextFieldValue) {
@@ -1065,10 +777,10 @@ open class NewPostViewModel :
userSuggestions.reset() userSuggestions.reset()
} }
saveDraft() draftTag.newVersion()
} }
open fun autocompleteWithEmoji(item: EmojiPackState.EmojiMedia) { open fun autocompleteWithEmoji(item: EmojiMedia) {
val wordToInsert = ":${item.code}:" val wordToInsert = ":${item.code}:"
message = message.replaceCurrentWord(wordToInsert) message = message.replaceCurrentWord(wordToInsert)
@@ -1076,10 +788,10 @@ open class NewPostViewModel :
emojiSuggestions?.reset() emojiSuggestions?.reset()
saveDraft() draftTag.newVersion()
} }
open fun autocompleteWithEmojiUrl(item: EmojiPackState.EmojiMedia) { open fun autocompleteWithEmojiUrl(item: EmojiMedia) {
val wordToInsert = item.link.url + " " val wordToInsert = item.link.url + " "
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
@@ -1094,7 +806,7 @@ open class NewPostViewModel :
emojiSuggestions?.reset() emojiSuggestions?.reset()
saveDraft() draftTag.newVersion()
} }
private fun newStateMapPollOptions(): SnapshotStateMap<Int, String> = mutableStateMapOf(Pair(0, ""), Pair(1, "")) private fun newStateMapPollOptions(): SnapshotStateMap<Int, String> = mutableStateMapOf(Pair(0, ""), Pair(1, ""))
@@ -1138,13 +850,13 @@ open class NewPostViewModel :
fun updateMinZapAmountForPoll(textMin: String) { fun updateMinZapAmountForPoll(textMin: String) {
valueMinimum = textMin.toLongOrNull()?.takeIf { it > 0 } valueMinimum = textMin.toLongOrNull()?.takeIf { it > 0 }
checkMinMax() checkMinMax()
saveDraft() draftTag.newVersion()
} }
fun updateMaxZapAmountForPoll(textMax: String) { fun updateMaxZapAmountForPoll(textMax: String) {
valueMaximum = textMax.toLongOrNull()?.takeIf { it > 0 } valueMaximum = textMax.toLongOrNull()?.takeIf { it > 0 }
checkMinMax() checkMinMax()
saveDraft() draftTag.newVersion()
} }
fun checkMinMax() { fun checkMinMax() {
@@ -1166,7 +878,8 @@ open class NewPostViewModel :
override fun updateZapFromText() { override fun updateZapFromText() {
viewModelScope.launch(Dispatchers.Default) { viewModelScope.launch(Dispatchers.Default) {
val tagger = NewMessageTagger(message.text, emptyList(), emptyList(), null, accountViewModel!!) val tagger =
NewMessageTagger(message.text, emptyList(), emptyList(), null, accountViewModel!!)
tagger.run() tagger.run()
tagger.pTags?.forEach { taggedUser -> tagger.pTags?.forEach { taggedUser ->
if (!forwardZapTo.value.items.any { it.key == taggedUser }) { if (!forwardZapTo.value.items.any { it.key == taggedUser }) {
@@ -1178,12 +891,12 @@ open class NewPostViewModel :
override fun updateZapRaiserAmount(newAmount: Long?) { override fun updateZapRaiserAmount(newAmount: Long?) {
zapRaiserAmount.value = newAmount zapRaiserAmount.value = newAmount
saveDraft() draftTag.newVersion()
} }
fun removePollOption(optionIndex: Int) { fun removePollOption(optionIndex: Int) {
pollOptions.removeOrdered(optionIndex) pollOptions.removeOrdered(optionIndex)
saveDraft() draftTag.newVersion()
} }
private fun MutableMap<Int, String>.removeOrdered(index: Int) { private fun MutableMap<Int, String>.removeOrdered(index: Int) {
@@ -1207,12 +920,12 @@ open class NewPostViewModel :
text: String, text: String,
) { ) {
pollOptions[optionIndex] = text pollOptions[optionIndex] = text
saveDraft() draftTag.newVersion()
} }
fun toggleMarkAsSensitive() { fun toggleMarkAsSensitive() {
wantsToMarkAsSensitive = !wantsToMarkAsSensitive wantsToMarkAsSensitive = !wantsToMarkAsSensitive
saveDraft() draftTag.newVersion()
} }
override fun locationManager(): LocationState = Amethyst.instance.locationManager override fun locationManager(): LocationState = Amethyst.instance.locationManager
@@ -44,8 +44,8 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.SaveButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.tor.PrivacySettingsBody import com.vitorpamplona.amethyst.ui.tor.PrivacySettingsBody
@@ -52,8 +52,8 @@ import com.vitorpamplona.amethyst.model.DefaultDMRelayList
import com.vitorpamplona.amethyst.model.DefaultSearchRelayList import com.vitorpamplona.amethyst.model.DefaultSearchRelayList
import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.SaveButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm.DMRelayListViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm.DMRelayListViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm.renderDMItems import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm.renderDMItems
@@ -47,8 +47,8 @@ import com.vitorpamplona.amethyst.model.DefaultDMRelayList
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.SaveButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
@@ -47,8 +47,8 @@ import com.vitorpamplona.amethyst.model.DefaultSearchRelayList
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.SaveButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search.SearchRelayList import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search.SearchRelayList
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search.SearchRelayListViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search.SearchRelayListViewModel
@@ -37,7 +37,7 @@ import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
import com.vitorpamplona.amethyst.ui.note.UpdateZapAmountContent import com.vitorpamplona.amethyst.ui.note.UpdateZapAmountContent
import com.vitorpamplona.amethyst.ui.note.UpdateZapAmountViewModel import com.vitorpamplona.amethyst.ui.note.UpdateZapAmountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.SaveButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
@Composable @Composable
@@ -48,9 +48,9 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.SaveButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsRow import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsRow
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Size10dp import com.vitorpamplona.amethyst.ui.theme.Size10dp
@@ -26,6 +26,7 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.model.nip30CustomEmojis.EmojiPackState.EmojiMedia import com.vitorpamplona.amethyst.model.nip30CustomEmojis.EmojiPackState.EmojiMedia
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.ShortNotePostViewModel
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
@@ -51,13 +52,13 @@ class NewPostViewModelTest {
@MockK(relaxed = true) @MockK(relaxed = true)
lateinit var replyingTo: Note lateinit var replyingTo: Note
private lateinit var newPostViewModelUnderTest: NewPostViewModel private lateinit var shortNoteViewModelUnderTest: ShortNotePostViewModel
@Before @Before
fun setup() { fun setup() {
mockkObject(LocalCache) mockkObject(LocalCache)
every { LocalCache.getOrCreateUser(any<HexKey>()) } returns mockk<User>() every { LocalCache.getOrCreateUser(any<HexKey>()) } returns mockk<User>()
newPostViewModelUnderTest = NewPostViewModel() shortNoteViewModelUnderTest = ShortNotePostViewModel()
MockKAnnotations.init(this) MockKAnnotations.init(this)
} }
@@ -81,8 +82,8 @@ class NewPostViewModelTest {
every { accountViewModel.account.emoji.myEmojis } returns mockk<StateFlow<List<EmojiMedia>>>(relaxed = true) every { accountViewModel.account.emoji.myEmojis } returns mockk<StateFlow<List<EmojiMedia>>>(relaxed = true)
// Act: Call load with mentions // Act: Call load with mentions
newPostViewModelUnderTest.init(accountViewModel) shortNoteViewModelUnderTest.init(accountViewModel)
newPostViewModelUnderTest.load(replyingTo, quote = null, fork = null, version = null, draft = null) shortNoteViewModelUnderTest.load(replyingTo, quote = null, fork = null, version = null, draft = null)
// Assert // Assert
// Two mentions should call LocalCache.getOrCreateUser twice // Two mentions should call LocalCache.getOrCreateUser twice
@@ -102,7 +103,7 @@ class NewPostViewModelTest {
every { accountViewModel.userProfile() } returns mockk<User>(relaxed = true) every { accountViewModel.userProfile() } returns mockk<User>(relaxed = true)
// Act: Call load with empty mentions // Act: Call load with empty mentions
newPostViewModelUnderTest.load(replyingTo, quote = null, fork = null, version = null, draft = null) shortNoteViewModelUnderTest.load(replyingTo, quote = null, fork = null, version = null, draft = null)
// Assert // Assert
// With no mentions LocalCache.getOrCreateUser should not be called // With no mentions LocalCache.getOrCreateUser should not be called
@@ -122,7 +123,7 @@ class NewPostViewModelTest {
every { accountViewModel.userProfile() } returns mockk<User>(relaxed = true) every { accountViewModel.userProfile() } returns mockk<User>(relaxed = true)
// Act: Call load with empty mentions // Act: Call load with empty mentions
newPostViewModelUnderTest.load(replyingTo, quote = null, fork = null, version = null, draft = null) shortNoteViewModelUnderTest.load(replyingTo, quote = null, fork = null, version = null, draft = null)
// Assert // Assert
// Verify LocalCache.getOrCreateUser(it) is not called with empty hex, it will crash the app // Verify LocalCache.getOrCreateUser(it) is not called with empty hex, it will crash the app
@@ -41,6 +41,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.lastNotNullOfOrNull import com.vitorpamplona.quartz.utils.lastNotNullOfOrNull
@Immutable @Immutable
@Deprecated("Replaced by NIP-22")
class GitReplyEvent( class GitReplyEvent(
id: HexKey, id: HexKey,
pubKey: HexKey, pubKey: HexKey,
@@ -68,6 +69,7 @@ class GitReplyEvent(
const val KIND = 1622 const val KIND = 1622
const val ALT_DESCRIPTION = "A Git Reply" const val ALT_DESCRIPTION = "A Git Reply"
@Deprecated("Replaced by NIP-22")
fun reply( fun reply(
post: String, post: String,
replyingTo: EventHintBundle<GitReplyEvent>, replyingTo: EventHintBundle<GitReplyEvent>,
@@ -80,6 +82,7 @@ class GitReplyEvent(
initializer() initializer()
} }
@Deprecated("Replaced by NIP-22")
fun replyIssue( fun replyIssue(
post: String, post: String,
issue: EventHintBundle<GitIssueEvent>, issue: EventHintBundle<GitIssueEvent>,
@@ -92,6 +95,7 @@ class GitReplyEvent(
initializer() initializer()
} }
@Deprecated("Replaced by NIP-22")
fun replyPatch( fun replyPatch(
post: String, post: String,
patch: EventHintBundle<GitPatchEvent>, patch: EventHintBundle<GitPatchEvent>,
@@ -41,6 +41,7 @@ import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
@Immutable @Immutable
@Deprecated("Replaced by NIP-22")
class TorrentCommentEvent( class TorrentCommentEvent(
id: HexKey, id: HexKey,
pubKey: HexKey, pubKey: HexKey,
@@ -92,6 +93,7 @@ class TorrentCommentEvent(
} }
} }
@Deprecated("Replaced by NIP-22")
fun build( fun build(
post: String, post: String,
createdAt: Long = TimeUtils.now(), createdAt: Long = TimeUtils.now(),