diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/notify/Notifying.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/notify/Notifying.kt index 3f7bd02376..00b361f211 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/notify/Notifying.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/notify/Notifying.kt @@ -52,20 +52,23 @@ import kotlinx.collections.immutable.ImmutableList fun Notifying( baseMentions: ImmutableList?, accountViewModel: AccountViewModel, + label: String? = null, + showWhenEmpty: Boolean = false, + onAddUser: (() -> Unit)? = null, onClick: (User) -> Unit, ) { val mentions = baseMentions?.toSet() FlowRow(horizontalArrangement = Arrangement.spacedBy(5.dp)) { - if (!mentions.isNullOrEmpty()) { + if (!mentions.isNullOrEmpty() || showWhenEmpty) { Text( - stringRes(R.string.reply_notify), + label ?: stringRes(R.string.reply_notify), fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.placeholderText, modifier = Modifier.align(CenterVertically), ) - mentions.forEachIndexed { _, user -> + mentions?.forEachIndexed { _, user -> Button( shape = ButtonBorder, colors = @@ -77,6 +80,23 @@ fun Notifying( DisplayUserNameWithDeleteMark(user, accountViewModel) } } + + if (onAddUser != null) { + Button( + shape = ButtonBorder, + colors = + ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.mediumImportanceLink, + ), + onClick = onAddUser, + ) { + Text( + text = stringRes(R.string.notify_add_user), + color = Color.White, + textAlign = TextAlign.Center, + ) + } + } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt index 7eeaf1723d..67c08f787a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt @@ -84,6 +84,7 @@ import com.vitorpamplona.amethyst.ui.actions.uploads.TakeVideoButton import com.vitorpamplona.amethyst.ui.actions.uploads.UploadProgressIndicator import com.vitorpamplona.amethyst.ui.actions.uploads.VoiceAnonymizationSection import com.vitorpamplona.amethyst.ui.actions.uploads.VoiceMessagePreview +import com.vitorpamplona.amethyst.ui.components.OutlinedThinPaddingTextField import com.vitorpamplona.amethyst.ui.components.getActivity import com.vitorpamplona.amethyst.ui.navigation.navs.Nav import com.vitorpamplona.amethyst.ui.navigation.routes.Route @@ -128,6 +129,7 @@ import com.vitorpamplona.amethyst.ui.theme.Size35dp import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.ui.theme.SuggestionListDefaultHeightPage import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn +import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.replyModifier import com.vitorpamplona.quartz.nip01Core.core.HexKey import kotlinx.collections.immutable.persistentListOf @@ -306,11 +308,41 @@ private fun NewPostScreenBody( } Row { - Notifying(postViewModel.pTags?.toImmutableList(), accountViewModel) { + Notifying( + baseMentions = postViewModel.pTags?.toImmutableList(), + accountViewModel = accountViewModel, + label = if (postViewModel.wantsPrivateNote) stringRes(R.string.private_note_visible_to) else null, + showWhenEmpty = postViewModel.wantsPrivateNote, + onAddUser = { postViewModel.wantsToAddNotifyUser = !postViewModel.wantsToAddNotifyUser }, + ) { postViewModel.removeFromReplyList(it) } } + if (postViewModel.wantsToAddNotifyUser) { + OutlinedThinPaddingTextField( + state = postViewModel.notifyUserSearchText, + onTextChanged = postViewModel::onNotifyUserSearchTextChanged, + label = { Text(text = stringRes(R.string.notify_search_and_add_user)) }, + modifier = Modifier.fillMaxWidth(), + placeholder = { + Text( + text = stringRes(R.string.zap_split_search_and_add_user_placeholder), + color = MaterialTheme.colorScheme.placeholderText, + ) + }, + singleLine = true, + ) + } + + if (postViewModel.wantsPrivateNote && postViewModel.pTags.isNullOrEmpty()) { + Text( + text = stringRes(R.string.private_note_no_receivers), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.placeholderText, + ) + } + // Only show text input if no voice message is being posted if (postViewModel.voiceMetadata == null && postViewModel.voiceRecording == null) { Row( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt index 214e53d88c..5eeec24635 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt @@ -167,6 +167,7 @@ enum class UserSuggestionAnchor { MAIN_MESSAGE, FORWARD_ZAPS, TO_USERS, + NOTIFY, } @Stable @@ -322,6 +323,26 @@ open class ShortNotePostViewModel : wantsPrivateNote = !wantsPrivateNote } + // Notify / Visible-to editor: lets the user p-tag people who aren't + // cited in the message. For private notes the Notify list IS the + // audience, so this is how receivers are picked. + var wantsToAddNotifyUser by mutableStateOf(false) + val notifyUserSearchText = TextFieldState() + + fun onNotifyUserSearchTextChanged() { + if (notifyUserSearchText.selection.collapsed) { + val lastWord = notifyUserSearchText.text.toString() + userSuggestionsMainMessage = UserSuggestionAnchor.NOTIFY + userSuggestions?.processCurrentWord(lastWord) + } + } + + fun addToReplyList(user: User) { + if (pTags?.contains(user) != true) { + pTags = (pTags ?: emptyList()).plus(user) + } + } + // A single ephemeral signer reused for the whole compose session so that media // uploads (Blossom/NIP-96 auth events) and the final anonymous post are all signed // by the same throwaway key, instead of leaking the real account's pubkey into the @@ -1286,6 +1307,8 @@ open class ShortNotePostViewModel : scheduledForSec = null wantsPrivateNote = false privateNoteLocked = false + wantsToAddNotifyUser = false + notifyUserSearchText.clearText() forwardZapTo.value = SplitBuilder() forwardZapToEditting.clearText() @@ -1350,6 +1373,10 @@ open class ShortNotePostViewModel : } else if (userSuggestionsMainMessage == UserSuggestionAnchor.FORWARD_ZAPS) { forwardZapTo.value.addItem(item) forwardZapToEditting.clearText() + } else if (userSuggestionsMainMessage == UserSuggestionAnchor.NOTIFY) { + addToReplyList(item) + notifyUserSearchText.clearText() + wantsToAddNotifyUser = false } userSuggestionsMainMessage = null diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index c933180082..23413bfa64 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -873,6 +873,10 @@ Make private: gift-wrap the note to the notified users only Make public Replies to a private note always stay private + Visible to + No receivers yet: only you will be able to see this note. Add people to share it with. + + Add + Search and add a user to notify is not a bookmark here Remove bookmark from list Add bookmark to list