From 0cf891bb4d3549a5de712cded57f12a41309d15d Mon Sep 17 00:00:00 2001 From: toadlyBroodle Date: Sat, 11 Mar 2023 18:16:47 +0900 Subject: [PATCH] pollViewModel -> override postViewModel funs, remove imageUpload from pollView, --- .../amethyst/ui/actions/NewPollView.kt | 43 +---- .../amethyst/ui/actions/NewPollViewModel.kt | 173 +++--------------- .../amethyst/ui/actions/NewPostViewModel.kt | 28 +-- .../amethyst/ui/buttons/NewPollButton.kt | 8 +- app/src/main/res/values/strings.xml | 3 +- 5 files changed, 49 insertions(+), 206 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPollView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPollView.kt index f4e67fac6..05a7c7764 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPollView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPollView.kt @@ -16,12 +16,10 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.painterResource @@ -32,7 +30,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import androidx.lifecycle.viewmodel.compose.viewModel -import coil.compose.AsyncImage import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Note @@ -101,7 +98,7 @@ fun NewPollView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n PollButton( onPost = { - pollViewModel.sendPoll() + pollViewModel.sendPost() onClose() }, isActive = pollViewModel.message.text.isNotBlank() && @@ -148,7 +145,7 @@ fun NewPollView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n }, placeholder = { Text( - text = stringResource(R.string.what_s_on_your_mind), + text = stringResource(R.string.primary_poll_description), color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) ) }, @@ -160,40 +157,6 @@ fun NewPollView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n visualTransformation = UrlUserTagTransformation(MaterialTheme.colors.primary), textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content) ) - - val myUrlPreview = pollViewModel.urlPreview - if (myUrlPreview != null) { - Row(modifier = Modifier.padding(top = 5.dp)) { - if (isValidURL(myUrlPreview)) { - val removedParamsFromUrl = - myUrlPreview.split("?")[0].lowercase() - if (imageExtension.matcher(removedParamsFromUrl).matches()) { - AsyncImage( - model = myUrlPreview, - contentDescription = myUrlPreview, - contentScale = ContentScale.FillWidth, - modifier = Modifier - .padding(top = 4.dp) - .fillMaxWidth() - .clip(shape = RoundedCornerShape(15.dp)) - .border( - 1.dp, - MaterialTheme.colors.onSurface.copy(alpha = 0.12f), - RoundedCornerShape(15.dp) - ) - ) - } else if (videoExtension.matcher(removedParamsFromUrl) - .matches() - ) { - VideoView(myUrlPreview) - } else { - UrlPreview(myUrlPreview, myUrlPreview) - } - } else if (noProtocolUrlValidator.matcher(myUrlPreview).matches()) { - UrlPreview("https://$myUrlPreview", myUrlPreview) - } - } - } } } @@ -265,6 +228,6 @@ fun PollButton(modifier: Modifier = Modifier, onPost: () -> Unit = {}, isActive: backgroundColor = if (isActive) MaterialTheme.colors.primary else Color.Gray ) ) { - Text(text = stringResource(R.string.poll), color = Color.White) + Text(text = stringResource(R.string.post_poll), color = Color.White) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPollViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPollViewModel.kt index f9b72569e..39b43f96b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPollViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPollViewModel.kt @@ -1,174 +1,53 @@ package com.vitorpamplona.amethyst.ui.actions -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue -import androidx.lifecycle.ViewModel -import com.vitorpamplona.amethyst.model.* -import com.vitorpamplona.amethyst.service.nip19.Nip19 -import com.vitorpamplona.amethyst.ui.components.isValidURL -import com.vitorpamplona.amethyst.ui.components.noProtocolUrlValidator -import kotlinx.coroutines.flow.MutableSharedFlow +import com.vitorpamplona.amethyst.model.Account +import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.model.User -class NewPollViewModel : ViewModel() { - private var account: Account? = null - private var originalNote: Note? = null +class NewPollViewModel : NewPostViewModel() { - var mentions by mutableStateOf?>(null) - var replyTos by mutableStateOf?>(null) - - var message by mutableStateOf(TextFieldValue("")) - var urlPreview by mutableStateOf(null) - var isUploadingImage by mutableStateOf(false) - val imageUploadingError = MutableSharedFlow() - - var userSuggestions by mutableStateOf>(emptyList()) - var userSuggestionAnchor: TextRange? = null - - fun load(account: Account, replyingTo: Note?, quote: Note?) { - originalNote = replyingTo - replyingTo?.let { replyNote -> - this.replyTos = (replyNote.replyTo ?: emptyList()).plus(replyNote) - replyNote.author?.let { replyUser -> - val currentMentions = replyNote.mentions ?: emptyList() - if (currentMentions.contains(replyUser)) { - this.mentions = currentMentions - } else { - this.mentions = currentMentions.plus(replyUser) - } - } - } - - quote?.let { - message = TextFieldValue(message.text + "\n\n@${it.idNote()}") - } - - this.account = account + override fun load(account: Account, replyingTo: Note?, quote: Note?) { + super.load(account, replyingTo, quote) } - fun addUserToMentions(user: User) { - mentions = if (mentions?.contains(user) == true) mentions else mentions?.plus(user) ?: listOf(user) + override fun addUserToMentions(user: User) { + super.addUserToMentions(user) } - fun addNoteToReplyTos(note: Note) { - note.author?.let { addUserToMentions(it) } - replyTos = if (replyTos?.contains(note) == true) replyTos else replyTos?.plus(note) ?: listOf(note) + override fun addNoteToReplyTos(note: Note) { + super.addNoteToReplyTos(note) } - fun tagIndex(user: User): Int { - // Postr Events assembles replies before mentions in the tag order - return (if (originalNote?.channel() != null) 1 else 0) + (replyTos?.size ?: 0) + (mentions?.indexOf(user) ?: 0) + override fun tagIndex(user: User): Int { + return super.tagIndex(user) } - fun tagIndex(note: Note): Int { - // Postr Events assembles replies before mentions in the tag order - return (if (originalNote?.channel() != null) 1 else 0) + (replyTos?.indexOf(note) ?: 0) + override fun tagIndex(note: Note): Int { + return super.tagIndex(note) } - fun sendPoll() { - // adds all references to mentions and reply tos - message.text.split('\n').forEach { paragraph: String -> - paragraph.split(' ').forEach { word: String -> - val results = parseDirtyWordForKey(word) - - if (results?.key?.type == Nip19.Type.USER) { - addUserToMentions(LocalCache.getOrCreateUser(results.key.hex)) - } else if (results?.key?.type == Nip19.Type.NOTE) { - addNoteToReplyTos(LocalCache.getOrCreateNote(results.key.hex)) - } else if (results?.key?.type == Nip19.Type.ADDRESS) { - val note = LocalCache.checkGetOrCreateAddressableNote(results.key.hex) - if (note != null) { - addNoteToReplyTos(note) - } - } - } - } - - // Tags the text in the correct order. - val newMessage = message.text.split('\n').map { paragraph: String -> - paragraph.split(' ').map { word: String -> - val results = parseDirtyWordForKey(word) - if (results?.key?.type == Nip19.Type.USER) { - val user = LocalCache.getOrCreateUser(results.key.hex) - - "#[${tagIndex(user)}]${results.restOfWord}" - } else if (results?.key?.type == Nip19.Type.NOTE) { - val note = LocalCache.getOrCreateNote(results.key.hex) - - "#[${tagIndex(note)}]${results.restOfWord}" - } else if (results?.key?.type == Nip19.Type.ADDRESS) { - val note = LocalCache.checkGetOrCreateAddressableNote(results.key.hex) - if (note != null) { - "#[${tagIndex(note)}]${results.restOfWord}" - } else { - word - } - } else { - word - } - }.joinToString(" ") - }.joinToString("\n") - - if (originalNote?.channel() != null) { - account?.sendChannelMessage(newMessage, originalNote!!.channel()!!.idHex, originalNote!!, mentions) - } else { - account?.sendPost(newMessage, replyTos, mentions) - } - - message = TextFieldValue("") - urlPreview = null - isUploadingImage = false - mentions = null + override fun sendPost() { + super.sendPost() } - fun cancel() { - message = TextFieldValue("") - urlPreview = null - isUploadingImage = false - mentions = null + override fun cancel() { + super.cancel() } - fun findUrlInMessage(): String? { - return message.text.split('\n').firstNotNullOfOrNull { paragraph -> - paragraph.split(' ').firstOrNull { word: String -> - isValidURL(word) || noProtocolUrlValidator.matcher(word).matches() - } - } + override fun findUrlInMessage(): String? { + return super.findUrlInMessage() } - fun removeFromReplyList(it: User) { - mentions = mentions?.minus(it) + override fun removeFromReplyList(it: User) { + super.removeFromReplyList(it) } - fun updateMessage(it: TextFieldValue) { - message = it - urlPreview = findUrlInMessage() - - if (it.selection.collapsed) { - val lastWord = it.text.substring(0, it.selection.end).substringAfterLast("\n").substringAfterLast(" ") - userSuggestionAnchor = it.selection - if (lastWord.startsWith("@") && lastWord.length > 2) { - userSuggestions = LocalCache.findUsersStartingWith(lastWord.removePrefix("@")) - } else { - userSuggestions = emptyList() - } - } + override fun updateMessage(it: TextFieldValue) { + super.updateMessage(it) } - fun autocompleteWithUser(item: User) { - userSuggestionAnchor?.let { - val lastWord = message.text.substring(0, it.end).substringAfterLast("\n").substringAfterLast(" ") - val lastWordStart = it.end - lastWord.length - val wordToInsert = "@${item.pubkeyNpub()} " - - message = TextFieldValue( - message.text.replaceRange(lastWordStart, it.end, wordToInsert), - TextRange(lastWordStart + wordToInsert.length, lastWordStart + wordToInsert.length) - ) - userSuggestionAnchor = null - userSuggestions = emptyList() - } + override fun autocompleteWithUser(item: User) { + super.autocompleteWithUser(item) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt index 1df19f6c3..98feff723 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt @@ -16,9 +16,9 @@ import com.vitorpamplona.amethyst.ui.components.noProtocolUrlValidator import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.launch -class NewPostViewModel : ViewModel() { - private var account: Account? = null - private var originalNote: Note? = null +open class NewPostViewModel : ViewModel() { + var account: Account? = null + var originalNote: Note? = null var mentions by mutableStateOf?>(null) var replyTos by mutableStateOf?>(null) @@ -31,7 +31,7 @@ class NewPostViewModel : ViewModel() { var userSuggestions by mutableStateOf>(emptyList()) var userSuggestionAnchor: TextRange? = null - fun load(account: Account, replyingTo: Note?, quote: Note?) { + open fun load(account: Account, replyingTo: Note?, quote: Note?) { originalNote = replyingTo replyingTo?.let { replyNote -> this.replyTos = (replyNote.replyTo ?: emptyList()).plus(replyNote) @@ -52,26 +52,26 @@ class NewPostViewModel : ViewModel() { this.account = account } - fun addUserToMentions(user: User) { + open fun addUserToMentions(user: User) { mentions = if (mentions?.contains(user) == true) mentions else mentions?.plus(user) ?: listOf(user) } - fun addNoteToReplyTos(note: Note) { + open fun addNoteToReplyTos(note: Note) { note.author?.let { addUserToMentions(it) } replyTos = if (replyTos?.contains(note) == true) replyTos else replyTos?.plus(note) ?: listOf(note) } - fun tagIndex(user: User): Int { + open fun tagIndex(user: User): Int { // Postr Events assembles replies before mentions in the tag order return (if (originalNote?.channel() != null) 1 else 0) + (replyTos?.size ?: 0) + (mentions?.indexOf(user) ?: 0) } - fun tagIndex(note: Note): Int { + open fun tagIndex(note: Note): Int { // Postr Events assembles replies before mentions in the tag order return (if (originalNote?.channel() != null) 1 else 0) + (replyTos?.indexOf(note) ?: 0) } - fun sendPost() { + open fun sendPost() { // adds all references to mentions and reply tos message.text.split('\n').forEach { paragraph: String -> paragraph.split(' ').forEach { word: String -> @@ -147,14 +147,14 @@ class NewPostViewModel : ViewModel() { ) } - fun cancel() { + open fun cancel() { message = TextFieldValue("") urlPreview = null isUploadingImage = false mentions = null } - fun findUrlInMessage(): String? { + open fun findUrlInMessage(): String? { return message.text.split('\n').firstNotNullOfOrNull { paragraph -> paragraph.split(' ').firstOrNull { word: String -> isValidURL(word) || noProtocolUrlValidator.matcher(word).matches() @@ -162,11 +162,11 @@ class NewPostViewModel : ViewModel() { } } - fun removeFromReplyList(it: User) { + open fun removeFromReplyList(it: User) { mentions = mentions?.minus(it) } - fun updateMessage(it: TextFieldValue) { + open fun updateMessage(it: TextFieldValue) { message = it urlPreview = findUrlInMessage() @@ -181,7 +181,7 @@ class NewPostViewModel : ViewModel() { } } - fun autocompleteWithUser(item: User) { + open fun autocompleteWithUser(item: User) { userSuggestionAnchor?.let { val lastWord = message.text.substring(0, it.end).substringAfterLast("\n").substringAfterLast(" ") val lastWordStart = it.end - lastWord.length diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/NewPollButton.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/NewPollButton.kt index 5923caaab..5169f5b1e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/NewPollButton.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/NewPollButton.kt @@ -22,16 +22,16 @@ import com.vitorpamplona.amethyst.ui.actions.NewPollView @Composable fun NewPollButton(account: Account) { - var wantsToPost by remember { + var wantsToPoll by remember { mutableStateOf(false) } - if (wantsToPost) { - NewPollView({ wantsToPost = false }, account = account) + if (wantsToPoll) { + NewPollView({ wantsToPoll = false }, account = account) } OutlinedButton( - onClick = { wantsToPost = true }, + onClick = { wantsToPoll = true }, modifier = Modifier.size(55.dp), shape = CircleShape, colors = ButtonDefaults.outlinedButtonColors(backgroundColor = MaterialTheme.colors.primary), diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fee140f6e..5a4a6a4e8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -220,6 +220,7 @@ https://twitter.com/<user>/status/<proof post> "<Unable to decrypt private message>\n\nYou were cited in a private/encrypted conversation between %1$s and %2$s." - Poll + Post Poll + Primary poll description…