mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-27 19:06:34 +02:00
Add new poll button, view, viewModel, icons, string; replace fab with pollButton; supress missing translations errors when debugging
This commit is contained in:
@@ -49,6 +49,7 @@ Amethyst brings the best social network to your Android phone. Just insert your
|
|||||||
- [ ] Delegated Event Signing (NIP-26)
|
- [ ] Delegated Event Signing (NIP-26)
|
||||||
- [ ] Account Creation / Backup Guidance (NIP-06)
|
- [ ] Account Creation / Backup Guidance (NIP-06)
|
||||||
- [ ] Message Sent feedback (NIP-20)
|
- [ ] Message Sent feedback (NIP-20)
|
||||||
|
- [ ] Polls (NIP-69)
|
||||||
|
|
||||||
# Development Overview
|
# Development Overview
|
||||||
|
|
||||||
|
@@ -31,6 +31,10 @@ android {
|
|||||||
applicationIdSuffix '.debug'
|
applicationIdSuffix '.debug'
|
||||||
versionNameSuffix '-DEBUG'
|
versionNameSuffix '-DEBUG'
|
||||||
resValue "string", "app_name", "@string/app_name_debug"
|
resValue "string", "app_name", "@string/app_name_debug"
|
||||||
|
|
||||||
|
lintOptions{
|
||||||
|
disable 'MissingTranslation'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
@@ -0,0 +1,270 @@
|
|||||||
|
package com.vitorpamplona.amethyst.ui.actions
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
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
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
|
import androidx.compose.ui.text.style.TextDirection
|
||||||
|
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
|
||||||
|
import com.vitorpamplona.amethyst.service.model.TextNoteEvent
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.*
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.ReplyInformation
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.UserLine
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
|
@Composable
|
||||||
|
fun NewPollView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = null, account: Account) {
|
||||||
|
val pollViewModel: NewPollViewModel = viewModel()
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
// initialize focus reference to be able to request focus programmatically
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
|
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
pollViewModel.load(account, baseReplyTo, quote)
|
||||||
|
delay(100)
|
||||||
|
focusRequester.requestFocus()
|
||||||
|
|
||||||
|
pollViewModel.imageUploadingError.collect { error ->
|
||||||
|
Toast.makeText(context, error, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = { onClose() },
|
||||||
|
properties = DialogProperties(
|
||||||
|
usePlatformDefaultWidth = false,
|
||||||
|
dismissOnClickOutside = false,
|
||||||
|
decorFitsSystemWindows = false
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight()
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight()
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 10.dp, end = 10.dp, top = 10.dp)
|
||||||
|
.imePadding()
|
||||||
|
.weight(1f)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
ClosePollButton(onCancel = {
|
||||||
|
pollViewModel.cancel()
|
||||||
|
onClose()
|
||||||
|
})
|
||||||
|
|
||||||
|
PollButton(
|
||||||
|
onPost = {
|
||||||
|
pollViewModel.sendPoll()
|
||||||
|
onClose()
|
||||||
|
},
|
||||||
|
isActive = pollViewModel.message.text.isNotBlank() &&
|
||||||
|
!pollViewModel.isUploadingImage
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.verticalScroll(scrollState)
|
||||||
|
) {
|
||||||
|
if (pollViewModel.replyTos != null && baseReplyTo?.event is TextNoteEvent) {
|
||||||
|
ReplyInformation(pollViewModel.replyTos, pollViewModel.mentions, account, "✖ ") {
|
||||||
|
pollViewModel.removeFromReplyList(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = pollViewModel.message,
|
||||||
|
onValueChange = {
|
||||||
|
pollViewModel.updateMessage(it)
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions.Default.copy(
|
||||||
|
capitalization = KeyboardCapitalization.Sentences
|
||||||
|
),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.border(
|
||||||
|
width = 1.dp,
|
||||||
|
color = MaterialTheme.colors.surface,
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
.focusRequester(focusRequester)
|
||||||
|
.onFocusChanged {
|
||||||
|
if (it.isFocused) {
|
||||||
|
keyboardController?.show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.what_s_on_your_mind),
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
colors = TextFieldDefaults
|
||||||
|
.outlinedTextFieldColors(
|
||||||
|
unfocusedBorderColor = Color.Transparent,
|
||||||
|
focusedBorderColor = Color.Transparent
|
||||||
|
),
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val userSuggestions = pollViewModel.userSuggestions
|
||||||
|
if (userSuggestions.isNotEmpty()) {
|
||||||
|
LazyColumn(
|
||||||
|
contentPadding = PaddingValues(
|
||||||
|
top = 10.dp
|
||||||
|
),
|
||||||
|
modifier = Modifier.heightIn(0.dp, 300.dp)
|
||||||
|
) {
|
||||||
|
itemsIndexed(
|
||||||
|
userSuggestions,
|
||||||
|
key = { _, item -> item.pubkeyHex }
|
||||||
|
) { index, item ->
|
||||||
|
UserLine(item, account) {
|
||||||
|
pollViewModel.autocompleteWithUser(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
/*UploadFromGallery(
|
||||||
|
isUploading = pollViewModel.isUploadingImage
|
||||||
|
) {
|
||||||
|
pollViewModel.upload(it, context)
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ClosePollButton(onCancel: () -> Unit) {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
onCancel()
|
||||||
|
},
|
||||||
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
colors = ButtonDefaults
|
||||||
|
.buttonColors(
|
||||||
|
backgroundColor = Color.Gray
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.ic_close),
|
||||||
|
contentDescription = stringResource(id = R.string.cancel),
|
||||||
|
modifier = Modifier.size(20.dp),
|
||||||
|
tint = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun PollButton(modifier: Modifier = Modifier, onPost: () -> Unit = {}, isActive: Boolean) {
|
||||||
|
Button(
|
||||||
|
modifier = modifier,
|
||||||
|
onClick = {
|
||||||
|
if (isActive) {
|
||||||
|
onPost()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
colors = ButtonDefaults
|
||||||
|
.buttonColors(
|
||||||
|
backgroundColor = if (isActive) MaterialTheme.colors.primary else Color.Gray
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(R.string.poll), color = Color.White)
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,174 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
class NewPollViewModel : ViewModel() {
|
||||||
|
private var account: Account? = null
|
||||||
|
private var originalNote: Note? = null
|
||||||
|
|
||||||
|
var mentions by mutableStateOf<List<User>?>(null)
|
||||||
|
var replyTos by mutableStateOf<List<Note>?>(null)
|
||||||
|
|
||||||
|
var message by mutableStateOf(TextFieldValue(""))
|
||||||
|
var urlPreview by mutableStateOf<String?>(null)
|
||||||
|
var isUploadingImage by mutableStateOf(false)
|
||||||
|
val imageUploadingError = MutableSharedFlow<String?>()
|
||||||
|
|
||||||
|
var userSuggestions by mutableStateOf<List<User>>(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
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addUserToMentions(user: User) {
|
||||||
|
mentions = if (mentions?.contains(user) == true) mentions else mentions?.plus(user) ?: listOf(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
// 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 {
|
||||||
|
// Postr Events assembles replies before mentions in the tag order
|
||||||
|
return (if (originalNote?.channel() != null) 1 else 0) + (replyTos?.indexOf(note) ?: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
message = TextFieldValue("")
|
||||||
|
urlPreview = null
|
||||||
|
isUploadingImage = false
|
||||||
|
mentions = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findUrlInMessage(): String? {
|
||||||
|
return message.text.split('\n').firstNotNullOfOrNull { paragraph ->
|
||||||
|
paragraph.split(' ').firstOrNull { word: String ->
|
||||||
|
isValidURL(word) || noProtocolUrlValidator.matcher(word).matches()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeFromReplyList(it: User) {
|
||||||
|
mentions = mentions?.minus(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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,47 @@
|
|||||||
|
package com.vitorpamplona.amethyst.buttons
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material.ButtonDefaults
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.OutlinedButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.vitorpamplona.amethyst.R
|
||||||
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.NewPollView
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NewPollButton(account: Account) {
|
||||||
|
var wantsToPost by remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantsToPost) {
|
||||||
|
NewPollView({ wantsToPost = false }, account = account)
|
||||||
|
}
|
||||||
|
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = { wantsToPost = true },
|
||||||
|
modifier = Modifier.size(55.dp),
|
||||||
|
shape = CircleShape,
|
||||||
|
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = MaterialTheme.colors.primary),
|
||||||
|
contentPadding = PaddingValues(0.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_poll),
|
||||||
|
null,
|
||||||
|
modifier = Modifier.size(26.dp),
|
||||||
|
tint = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@@ -18,7 +18,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.vitorpamplona.amethyst.buttons.NewChannelButton
|
import com.vitorpamplona.amethyst.buttons.NewChannelButton
|
||||||
import com.vitorpamplona.amethyst.buttons.NewNoteButton
|
import com.vitorpamplona.amethyst.buttons.NewPollButton
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.AppBottomBar
|
import com.vitorpamplona.amethyst.ui.navigation.AppBottomBar
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.AppNavigation
|
import com.vitorpamplona.amethyst.ui.navigation.AppNavigation
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.AppTopBar
|
import com.vitorpamplona.amethyst.ui.navigation.AppTopBar
|
||||||
@@ -71,7 +71,8 @@ fun FloatingButton(navController: NavHostController, accountViewModel: AccountSt
|
|||||||
// Does nothing.
|
// Does nothing.
|
||||||
}
|
}
|
||||||
is AccountState.LoggedIn -> {
|
is AccountState.LoggedIn -> {
|
||||||
NewNoteButton(state.account)
|
NewPollButton(state.account)
|
||||||
|
// NewNoteButton(state.account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
app/src/main/res/drawable-hdpi/ic_poll.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_poll.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 655 B |
BIN
app/src/main/res/drawable-mdpi/ic_poll.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_poll.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 402 B |
BIN
app/src/main/res/drawable-xhdpi/ic_poll.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_poll.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 822 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_poll.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_poll.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_poll.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_poll.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
@@ -220,5 +220,6 @@
|
|||||||
<string name="twitter_proof_url_template" translatable="false">https://twitter.com/<user>/status/<proof post></string>
|
<string name="twitter_proof_url_template" translatable="false">https://twitter.com/<user>/status/<proof post></string>
|
||||||
|
|
||||||
<string name="private_conversation_notification">"<Unable to decrypt private message>\n\nYou were cited in a private/encrypted conversation between %1$s and %2$s."</string>
|
<string name="private_conversation_notification">"<Unable to decrypt private message>\n\nYou were cited in a private/encrypted conversation between %1$s and %2$s."</string>
|
||||||
|
<string name="poll">Poll</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Reference in New Issue
Block a user