Improves Poll Rendering speed.
This commit is contained in:
@@ -45,7 +45,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
|||||||
import com.vitorpamplona.amethyst.ui.uriToRoute
|
import com.vitorpamplona.amethyst.ui.uriToRoute
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.net.MalformedURLException
|
import java.net.MalformedURLException
|
||||||
import java.net.URISyntaxException
|
import java.net.URISyntaxException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
@@ -128,10 +127,8 @@ private fun RenderRegular(
|
|||||||
mutableStateOf(RichTextViewerState(content, emptySet(), emptyMap(), emptyList(), emptyMap()))
|
mutableStateOf(RichTextViewerState(content, emptySet(), emptyMap(), emptyList(), emptyMap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
LaunchedEffect(key1 = content) {
|
LaunchedEffect(key1 = content) {
|
||||||
scope.launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
val urls = UrlDetector(content, UrlDetectorOptions.Default).detect()
|
val urls = UrlDetector(content, UrlDetectorOptions.Default).detect()
|
||||||
val urlSet = urls.mapTo(LinkedHashSet(urls.size)) { it.originalUrl }
|
val urlSet = urls.mapTo(LinkedHashSet(urls.size)) { it.originalUrl }
|
||||||
val imagesForPager = urlSet.mapNotNull { fullUrl ->
|
val imagesForPager = urlSet.mapNotNull { fullUrl ->
|
||||||
@@ -363,14 +360,14 @@ private fun RenderContentAsMarkdown(content: String, backgroundColor: Color, tag
|
|||||||
var refresh by remember(content) { mutableStateOf(0) }
|
var refresh by remember(content) { mutableStateOf(0) }
|
||||||
|
|
||||||
LaunchedEffect(key1 = content) {
|
LaunchedEffect(key1 = content) {
|
||||||
withContext(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
nip19References = returnNIP19References(content, tags)
|
nip19References = returnNIP19References(content, tags)
|
||||||
markdownWithSpecialContent = returnMarkdownWithSpecialContent(content, tags)
|
markdownWithSpecialContent = returnMarkdownWithSpecialContent(content, tags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(key1 = refresh) {
|
LaunchedEffect(key1 = refresh) {
|
||||||
withContext(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
val newMarkdownWithSpecialContent = returnMarkdownWithSpecialContent(content, tags)
|
val newMarkdownWithSpecialContent = returnMarkdownWithSpecialContent(content, tags)
|
||||||
if (markdownWithSpecialContent != newMarkdownWithSpecialContent) {
|
if (markdownWithSpecialContent != newMarkdownWithSpecialContent) {
|
||||||
markdownWithSpecialContent = newMarkdownWithSpecialContent
|
markdownWithSpecialContent = newMarkdownWithSpecialContent
|
||||||
@@ -425,7 +422,7 @@ private fun ObserveNIP19Event(
|
|||||||
var baseNote by remember(it) { mutableStateOf<Note?>(null) }
|
var baseNote by remember(it) { mutableStateOf<Note?>(null) }
|
||||||
|
|
||||||
LaunchedEffect(key1 = it.hex) {
|
LaunchedEffect(key1 = it.hex) {
|
||||||
withContext(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
if (it.type == Nip19.Type.NOTE || it.type == Nip19.Type.EVENT || it.type == Nip19.Type.ADDRESS) {
|
if (it.type == Nip19.Type.NOTE || it.type == Nip19.Type.EVENT || it.type == Nip19.Type.ADDRESS) {
|
||||||
LocalCache.checkGetOrCreateNote(it.hex)?.let { note ->
|
LocalCache.checkGetOrCreateNote(it.hex)?.let { note ->
|
||||||
baseNote = note
|
baseNote = note
|
||||||
@@ -436,9 +433,12 @@ private fun ObserveNIP19Event(
|
|||||||
|
|
||||||
baseNote?.let { note ->
|
baseNote?.let { note ->
|
||||||
val noteState by note.live().metadata.observeAsState()
|
val noteState by note.live().metadata.observeAsState()
|
||||||
if (noteState?.note?.event != null) {
|
|
||||||
LaunchedEffect(key1 = noteState) {
|
LaunchedEffect(key1 = noteState) {
|
||||||
onRefresh()
|
if (noteState?.note?.event != null) {
|
||||||
|
launch(Dispatchers.IO) {
|
||||||
|
onRefresh()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -452,7 +452,7 @@ private fun ObserveNIP19User(
|
|||||||
var baseUser by remember(it) { mutableStateOf<User?>(null) }
|
var baseUser by remember(it) { mutableStateOf<User?>(null) }
|
||||||
|
|
||||||
LaunchedEffect(key1 = it.hex) {
|
LaunchedEffect(key1 = it.hex) {
|
||||||
withContext(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
if (it.type == Nip19.Type.USER) {
|
if (it.type == Nip19.Type.USER) {
|
||||||
LocalCache.checkGetOrCreateUser(it.hex)?.let { user ->
|
LocalCache.checkGetOrCreateUser(it.hex)?.let { user ->
|
||||||
baseUser = user
|
baseUser = user
|
||||||
@@ -463,9 +463,12 @@ private fun ObserveNIP19User(
|
|||||||
|
|
||||||
baseUser?.let { user ->
|
baseUser?.let { user ->
|
||||||
val userState by user.live().metadata.observeAsState()
|
val userState by user.live().metadata.observeAsState()
|
||||||
if (userState?.user?.info != null) {
|
|
||||||
LaunchedEffect(key1 = userState) {
|
LaunchedEffect(key1 = userState) {
|
||||||
onRefresh()
|
if (userState?.user?.info != null) {
|
||||||
|
launch(Dispatchers.IO) {
|
||||||
|
onRefresh()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -622,10 +625,8 @@ fun BechLink(word: String, canPreview: Boolean, backgroundColor: Color, accountV
|
|||||||
var nip19Route by remember { mutableStateOf<Nip19.Return?>(null) }
|
var nip19Route by remember { mutableStateOf<Nip19.Return?>(null) }
|
||||||
var baseNotePair by remember { mutableStateOf<Pair<Note, String?>?>(null) }
|
var baseNotePair by remember { mutableStateOf<Pair<Note, String?>?>(null) }
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
LaunchedEffect(key1 = word) {
|
LaunchedEffect(key1 = word) {
|
||||||
scope.launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
Nip19.uriToRoute(word)?.let {
|
Nip19.uriToRoute(word)?.let {
|
||||||
if (it.type == Nip19.Type.NOTE || it.type == Nip19.Type.EVENT || it.type == Nip19.Type.ADDRESS) {
|
if (it.type == Nip19.Type.NOTE || it.type == Nip19.Type.EVENT || it.type == Nip19.Type.ADDRESS) {
|
||||||
LocalCache.checkGetOrCreateNote(it.hex)?.let { note ->
|
LocalCache.checkGetOrCreateNote(it.hex)?.let { note ->
|
||||||
@@ -675,10 +676,8 @@ fun BechLink(word: String, canPreview: Boolean, backgroundColor: Color, accountV
|
|||||||
fun HashTag(word: String, nav: (String) -> Unit) {
|
fun HashTag(word: String, nav: (String) -> Unit) {
|
||||||
var tagSuffixPair by remember { mutableStateOf<Pair<String, String?>?>(null) }
|
var tagSuffixPair by remember { mutableStateOf<Pair<String, String?>?>(null) }
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
LaunchedEffect(key1 = word) {
|
LaunchedEffect(key1 = word) {
|
||||||
scope.launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
val hashtagMatcher = hashTagsPattern.matcher(word)
|
val hashtagMatcher = hashTagsPattern.matcher(word)
|
||||||
|
|
||||||
val (myTag, mySuffix) = try {
|
val (myTag, mySuffix) = try {
|
||||||
@@ -755,10 +754,8 @@ fun TagLink(word: String, tags: List<List<String>>, canPreview: Boolean, backgro
|
|||||||
var baseUserPair by remember { mutableStateOf<Pair<User, String?>?>(null) }
|
var baseUserPair by remember { mutableStateOf<Pair<User, String?>?>(null) }
|
||||||
var baseNotePair by remember { mutableStateOf<Pair<Note, String?>?>(null) }
|
var baseNotePair by remember { mutableStateOf<Pair<Note, String?>?>(null) }
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
LaunchedEffect(key1 = word) {
|
LaunchedEffect(key1 = word) {
|
||||||
scope.launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
val matcher = tagIndex.matcher(word)
|
val matcher = tagIndex.matcher(word)
|
||||||
val (index, suffix) = try {
|
val (index, suffix) = try {
|
||||||
matcher.find()
|
matcher.find()
|
||||||
|
|||||||
@@ -262,9 +262,7 @@ fun AuthorGalleryZaps(
|
|||||||
Column(modifier = Modifier.padding(start = 10.dp)) {
|
Column(modifier = Modifier.padding(start = 10.dp)) {
|
||||||
FlowRow() {
|
FlowRow() {
|
||||||
authorNotes.forEach {
|
authorNotes.forEach {
|
||||||
Box() {
|
AuthorPictureAndComment(it.key, it.value, backgroundColor, nav, accountViewModel)
|
||||||
AuthorPictureAndComment(it.key, it.value, backgroundColor, nav, accountViewModel)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,17 +327,9 @@ private fun AuthorPictureAndComment(
|
|||||||
) {
|
) {
|
||||||
val authorPictureModifier = remember { Modifier }
|
val authorPictureModifier = remember { Modifier }
|
||||||
|
|
||||||
val modifier = remember(comment) {
|
val modifier = remember {
|
||||||
if (comment != null) {
|
Modifier.clickable {
|
||||||
Modifier
|
nav(route)
|
||||||
.fillMaxWidth()
|
|
||||||
.clickable {
|
|
||||||
nav(route)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Modifier.clickable {
|
|
||||||
nav(route)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,7 +371,7 @@ private fun AuthorPictureAndComment(
|
|||||||
content = it,
|
content = it,
|
||||||
canPreview = true,
|
canPreview = true,
|
||||||
tags = null,
|
tags = null,
|
||||||
modifier = Modifier.weight(1f),
|
modifier = remember { Modifier.fillMaxWidth() },
|
||||||
backgroundColor = backgroundColor,
|
backgroundColor = backgroundColor,
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
nav = nav
|
nav = nav
|
||||||
|
|||||||
@@ -604,7 +604,7 @@ private fun RenderPoll(
|
|||||||
nav: (String) -> Unit
|
nav: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
val noteEvent = note.event as? PollNoteEvent ?: return
|
val noteEvent = note.event as? PollNoteEvent ?: return
|
||||||
val eventContent = noteEvent.content()
|
val eventContent = remember { noteEvent.content() }
|
||||||
|
|
||||||
if (makeItShort && accountViewModel.isLoggedUser(note.author)) {
|
if (makeItShort && accountViewModel.isLoggedUser(note.author)) {
|
||||||
Text(
|
Text(
|
||||||
@@ -2249,35 +2249,42 @@ fun UserPicture(
|
|||||||
.align(Alignment.TopEnd)
|
.align(Alignment.TopEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
val myIconBackgroundModifier = remember {
|
FollowingIcon(myIconBoxModifier, size)
|
||||||
Modifier
|
|
||||||
.clip(CircleShape)
|
|
||||||
.fillMaxSize(0.6f)
|
|
||||||
.align(Alignment.Center)
|
|
||||||
}
|
|
||||||
|
|
||||||
val myIconModifier = remember {
|
|
||||||
Modifier
|
|
||||||
.width(size.div(3.5f))
|
|
||||||
.height(size.div(3.5f))
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(myIconBoxModifier, contentAlignment = Alignment.Center) {
|
|
||||||
Box(
|
|
||||||
myIconBackgroundModifier.background(MaterialTheme.colors.background)
|
|
||||||
)
|
|
||||||
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(R.drawable.ic_verified),
|
|
||||||
stringResource(id = R.string.following),
|
|
||||||
modifier = myIconModifier,
|
|
||||||
tint = Following
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun FollowingIcon(
|
||||||
|
myIconBoxModifier: Modifier,
|
||||||
|
size: Dp
|
||||||
|
) {
|
||||||
|
Box(myIconBoxModifier, contentAlignment = Alignment.Center) {
|
||||||
|
val myIconBackgroundModifier = remember {
|
||||||
|
Modifier
|
||||||
|
.clip(CircleShape)
|
||||||
|
.fillMaxSize(0.6f)
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
myIconBackgroundModifier.background(MaterialTheme.colors.background)
|
||||||
|
)
|
||||||
|
|
||||||
|
val myIconModifier = remember {
|
||||||
|
Modifier
|
||||||
|
.width(size.div(3.5f))
|
||||||
|
.height(size.div(3.5f))
|
||||||
|
}
|
||||||
|
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_verified),
|
||||||
|
stringResource(id = R.string.following),
|
||||||
|
modifier = myIconModifier,
|
||||||
|
tint = Following
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class DropDownParams(
|
data class DropDownParams(
|
||||||
val isFollowingAuthor: Boolean,
|
val isFollowingAuthor: Boolean,
|
||||||
val isPrivateBookmarkNote: Boolean,
|
val isPrivateBookmarkNote: Boolean,
|
||||||
|
|||||||
@@ -4,14 +4,12 @@ import android.widget.Toast
|
|||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Bolt
|
import androidx.compose.material.icons.filled.Bolt
|
||||||
import androidx.compose.material.icons.outlined.Bolt
|
import androidx.compose.material.icons.outlined.Bolt
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
@@ -20,23 +18,20 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.Role
|
import androidx.compose.ui.semantics.Role
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.ui.window.Dialog
|
|
||||||
import androidx.compose.ui.window.DialogProperties
|
|
||||||
import androidx.compose.ui.window.Popup
|
import androidx.compose.ui.window.Popup
|
||||||
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.service.model.LnZapEvent
|
||||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@@ -48,16 +43,11 @@ fun PollNote(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit
|
nav: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
|
||||||
val account = remember(accountState) { accountState?.account } ?: return
|
|
||||||
|
|
||||||
val pollViewModel: PollNoteViewModel = viewModel(
|
val pollViewModel: PollNoteViewModel = viewModel(
|
||||||
key = baseNote.idHex
|
key = baseNote.idHex
|
||||||
)
|
)
|
||||||
|
|
||||||
LaunchedEffect(key1 = baseNote) {
|
pollViewModel.load(accountViewModel.account, baseNote)
|
||||||
pollViewModel.load(account, baseNote)
|
|
||||||
}
|
|
||||||
|
|
||||||
PollNote(
|
PollNote(
|
||||||
baseNote = baseNote,
|
baseNote = baseNote,
|
||||||
@@ -78,15 +68,11 @@ fun PollNote(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit
|
nav: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
val zapsState by baseNote.live().zaps.observeAsState()
|
WatchZapsAndUpdateTallies(baseNote, pollViewModel)
|
||||||
|
|
||||||
LaunchedEffect(key1 = zapsState) {
|
val tallies by pollViewModel.tallies.collectAsState()
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
pollViewModel.refreshTallies()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pollViewModel.tallies.forEach { poll_op ->
|
tallies.forEach { poll_op ->
|
||||||
OptionNote(
|
OptionNote(
|
||||||
poll_op,
|
poll_op,
|
||||||
pollViewModel,
|
pollViewModel,
|
||||||
@@ -99,6 +85,21 @@ fun PollNote(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WatchZapsAndUpdateTallies(
|
||||||
|
baseNote: Note,
|
||||||
|
pollViewModel: PollNoteViewModel
|
||||||
|
) {
|
||||||
|
val zapsState by baseNote.live().zaps.observeAsState()
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = zapsState) {
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
pollViewModel.refreshTallies()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun OptionNote(
|
private fun OptionNote(
|
||||||
poolOption: PollOption,
|
poolOption: PollOption,
|
||||||
@@ -109,6 +110,8 @@ private fun OptionNote(
|
|||||||
backgroundColor: Color,
|
backgroundColor: Color,
|
||||||
nav: (String) -> Unit
|
nav: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
|
val tags = remember { baseNote.event?.tags() }
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.padding(vertical = 3.dp)
|
modifier = Modifier.padding(vertical = 3.dp)
|
||||||
@@ -126,54 +129,16 @@ private fun OptionNote(
|
|||||||
accountViewModel,
|
accountViewModel,
|
||||||
pollViewModel,
|
pollViewModel,
|
||||||
nonClickablePrepend = {
|
nonClickablePrepend = {
|
||||||
Box(
|
RenderOptionAfterVote(
|
||||||
Modifier
|
poolOption.descriptor,
|
||||||
.fillMaxWidth(0.75f)
|
poolOption.tally.toFloat(),
|
||||||
.clip(shape = RoundedCornerShape(15.dp))
|
color,
|
||||||
.border(
|
canPreview,
|
||||||
2.dp,
|
tags,
|
||||||
color,
|
backgroundColor,
|
||||||
RoundedCornerShape(15.dp)
|
accountViewModel,
|
||||||
)
|
nav
|
||||||
) {
|
)
|
||||||
LinearProgressIndicator(
|
|
||||||
modifier = Modifier.matchParentSize(),
|
|
||||||
color = color,
|
|
||||||
progress = poolOption.tally.toFloat()
|
|
||||||
)
|
|
||||||
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.End,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(horizontal = 10.dp)
|
|
||||||
.width(40.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "${(poolOption.tally.toFloat() * 100).roundToInt()}%",
|
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(15.dp)
|
|
||||||
) {
|
|
||||||
TranslatableRichTextViewer(
|
|
||||||
poolOption.descriptor,
|
|
||||||
canPreview,
|
|
||||||
Modifier,
|
|
||||||
pollViewModel.pollEvent?.tags(),
|
|
||||||
backgroundColor,
|
|
||||||
accountViewModel,
|
|
||||||
nav
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
clickablePrepend = {
|
clickablePrepend = {
|
||||||
}
|
}
|
||||||
@@ -186,32 +151,113 @@ private fun OptionNote(
|
|||||||
pollViewModel,
|
pollViewModel,
|
||||||
nonClickablePrepend = {},
|
nonClickablePrepend = {},
|
||||||
clickablePrepend = {
|
clickablePrepend = {
|
||||||
Box(
|
RenderOptionBeforeVote(poolOption.descriptor, canPreview, tags, backgroundColor, accountViewModel, nav)
|
||||||
Modifier
|
|
||||||
.fillMaxWidth(0.75f)
|
|
||||||
.clip(shape = RoundedCornerShape(15.dp))
|
|
||||||
.border(
|
|
||||||
2.dp,
|
|
||||||
MaterialTheme.colors.primary,
|
|
||||||
RoundedCornerShape(15.dp)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
TranslatableRichTextViewer(
|
|
||||||
poolOption.descriptor,
|
|
||||||
canPreview,
|
|
||||||
Modifier.padding(15.dp),
|
|
||||||
pollViewModel.pollEvent?.tags(),
|
|
||||||
backgroundColor,
|
|
||||||
accountViewModel,
|
|
||||||
nav
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RenderOptionAfterVote(
|
||||||
|
description: String,
|
||||||
|
totalRatio: Float,
|
||||||
|
color: Color,
|
||||||
|
canPreview: Boolean,
|
||||||
|
tags: List<List<String>>?,
|
||||||
|
backgroundColor: Color,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit
|
||||||
|
) {
|
||||||
|
val totalPercentage = remember(totalRatio) {
|
||||||
|
"${(totalRatio * 100).roundToInt()}%"
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth(0.75f)
|
||||||
|
.clip(shape = RoundedCornerShape(15.dp))
|
||||||
|
.border(
|
||||||
|
2.dp,
|
||||||
|
color,
|
||||||
|
RoundedCornerShape(15.dp)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
LinearProgressIndicator(
|
||||||
|
modifier = Modifier.matchParentSize(),
|
||||||
|
color = color,
|
||||||
|
progress = totalRatio
|
||||||
|
)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.End,
|
||||||
|
modifier = remember {
|
||||||
|
Modifier
|
||||||
|
.padding(horizontal = 10.dp)
|
||||||
|
.width(40.dp)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = totalPercentage,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = remember {
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(15.dp)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
TranslatableRichTextViewer(
|
||||||
|
description,
|
||||||
|
canPreview,
|
||||||
|
remember { Modifier },
|
||||||
|
tags,
|
||||||
|
backgroundColor,
|
||||||
|
accountViewModel,
|
||||||
|
nav
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RenderOptionBeforeVote(
|
||||||
|
description: String,
|
||||||
|
canPreview: Boolean,
|
||||||
|
tags: List<List<String>>?,
|
||||||
|
backgroundColor: Color,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth(0.75f)
|
||||||
|
.clip(shape = RoundedCornerShape(15.dp))
|
||||||
|
.border(
|
||||||
|
2.dp,
|
||||||
|
MaterialTheme.colors.primary,
|
||||||
|
RoundedCornerShape(15.dp)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
TranslatableRichTextViewer(
|
||||||
|
description,
|
||||||
|
canPreview,
|
||||||
|
remember { Modifier.padding(15.dp) },
|
||||||
|
tags,
|
||||||
|
backgroundColor,
|
||||||
|
accountViewModel,
|
||||||
|
nav
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
fun ZapVote(
|
fun ZapVote(
|
||||||
@@ -374,7 +420,7 @@ fun ZapVote(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only show tallies after a user has zapped note
|
// only show tallies after a user has zapped note
|
||||||
if (baseNote.author == accountViewModel.userProfile() || pollViewModel.wasZappedByLoggedInAccount) {
|
if (!pollViewModel.canZap()) {
|
||||||
Text(
|
Text(
|
||||||
showAmount(poolOption.zappedValue),
|
showAmount(poolOption.zappedValue),
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
@@ -399,23 +445,18 @@ fun FilteredZapAmountChoicePopup(
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||||
val account = accountState?.account ?: return
|
val defaultZapType by remember(accountState) {
|
||||||
|
derivedStateOf {
|
||||||
|
accountState?.account?.defaultZapType ?: LnZapEvent.ZapType.PRIVATE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val zapMessage = ""
|
val zapMessage = ""
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
val options = account.zapAmountChoices.filter { pollViewModel.isValidInputVoteAmount(it) }.toMutableList()
|
val sortedOptions = remember(accountState) {
|
||||||
if (options.isEmpty()) {
|
pollViewModel.createZapOptionsThatMatchThePollingParameters()
|
||||||
pollViewModel.valueMinimum?.let { minimum ->
|
|
||||||
pollViewModel.valueMaximum?.let { maximum ->
|
|
||||||
if (minimum != maximum) {
|
|
||||||
options.add(((minimum + maximum) / 2).toLong())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pollViewModel.valueMinimum?.let { options.add(it.toLong()) }
|
|
||||||
pollViewModel.valueMaximum?.let { options.add(it.toLong()) }
|
|
||||||
val sortedOptions = options.toSet().sorted()
|
|
||||||
|
|
||||||
Popup(
|
Popup(
|
||||||
alignment = Alignment.BottomCenter,
|
alignment = Alignment.BottomCenter,
|
||||||
@@ -424,6 +465,10 @@ fun FilteredZapAmountChoicePopup(
|
|||||||
) {
|
) {
|
||||||
FlowRow(horizontalArrangement = Arrangement.Center) {
|
FlowRow(horizontalArrangement = Arrangement.Center) {
|
||||||
sortedOptions.forEach { amountInSats ->
|
sortedOptions.forEach { amountInSats ->
|
||||||
|
val zapAmount = remember {
|
||||||
|
"⚡ ${showAmount(amountInSats.toBigDecimal().setScale(1))}"
|
||||||
|
}
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
modifier = Modifier.padding(horizontal = 3.dp),
|
modifier = Modifier.padding(horizontal = 3.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
@@ -436,7 +481,7 @@ fun FilteredZapAmountChoicePopup(
|
|||||||
context,
|
context,
|
||||||
onError,
|
onError,
|
||||||
onProgress,
|
onProgress,
|
||||||
account.defaultZapType
|
defaultZapType
|
||||||
)
|
)
|
||||||
onDismiss()
|
onDismiss()
|
||||||
}
|
}
|
||||||
@@ -448,7 +493,7 @@ fun FilteredZapAmountChoicePopup(
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
"⚡ ${showAmount(amountInSats.toBigDecimal().setScale(1))}",
|
text = zapAmount,
|
||||||
color = Color.White,
|
color = Color.White,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
modifier = Modifier.combinedClickable(
|
modifier = Modifier.combinedClickable(
|
||||||
@@ -462,7 +507,7 @@ fun FilteredZapAmountChoicePopup(
|
|||||||
context,
|
context,
|
||||||
onError,
|
onError,
|
||||||
onProgress,
|
onProgress,
|
||||||
account.defaultZapType
|
defaultZapType
|
||||||
)
|
)
|
||||||
onDismiss()
|
onDismiss()
|
||||||
}
|
}
|
||||||
@@ -477,118 +522,3 @@ fun FilteredZapAmountChoicePopup(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
|
||||||
@Composable
|
|
||||||
fun ZapVoteAmountChoicePopup(
|
|
||||||
baseNote: Note,
|
|
||||||
accountViewModel: AccountViewModel,
|
|
||||||
pollViewModel: PollNoteViewModel,
|
|
||||||
pollOption: Int,
|
|
||||||
onDismiss: () -> Unit,
|
|
||||||
onError: (text: String) -> Unit,
|
|
||||||
onProgress: (percent: Float) -> Unit
|
|
||||||
) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
var inputAmountText by rememberSaveable { mutableStateOf("") }
|
|
||||||
|
|
||||||
val colorInValid = TextFieldDefaults.outlinedTextFieldColors(
|
|
||||||
focusedBorderColor = MaterialTheme.colors.error,
|
|
||||||
unfocusedBorderColor = Color.Red
|
|
||||||
)
|
|
||||||
val colorValid = TextFieldDefaults.outlinedTextFieldColors(
|
|
||||||
focusedBorderColor = MaterialTheme.colors.primary,
|
|
||||||
unfocusedBorderColor = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
|
||||||
)
|
|
||||||
|
|
||||||
Dialog(
|
|
||||||
onDismissRequest = { onDismiss() },
|
|
||||||
properties = DialogProperties(
|
|
||||||
dismissOnClickOutside = true,
|
|
||||||
usePlatformDefaultWidth = false
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Surface {
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.Center,
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(10.dp)
|
|
||||||
) {
|
|
||||||
var amount = pollViewModel.inputVoteAmountLong(inputAmountText)
|
|
||||||
|
|
||||||
// only prompt for input amount if vote is not atomic
|
|
||||||
if (!pollViewModel.isVoteAmountAtomic()) {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = inputAmountText,
|
|
||||||
onValueChange = { inputAmountText = it },
|
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
|
||||||
modifier = Modifier.width(150.dp),
|
|
||||||
colors = if (pollViewModel.isValidInputVoteAmount(amount)) colorValid else colorInValid,
|
|
||||||
label = {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.poll_zap_amount),
|
|
||||||
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
placeholder = {
|
|
||||||
Text(
|
|
||||||
text = pollViewModel.voteAmountPlaceHolderText(context.getString(R.string.sats)),
|
|
||||||
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else { amount = pollViewModel.valueMaximum?.toLong() }
|
|
||||||
|
|
||||||
val isValidInputAmount = pollViewModel.isValidInputVoteAmount(amount)
|
|
||||||
Button(
|
|
||||||
modifier = Modifier.padding(horizontal = 3.dp),
|
|
||||||
enabled = isValidInputAmount,
|
|
||||||
onClick = {
|
|
||||||
if (amount != null && isValidInputAmount) {
|
|
||||||
accountViewModel.zap(
|
|
||||||
baseNote,
|
|
||||||
amount * 1000,
|
|
||||||
pollOption,
|
|
||||||
"",
|
|
||||||
context,
|
|
||||||
onError,
|
|
||||||
onProgress
|
|
||||||
)
|
|
||||||
onDismiss()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
shape = RoundedCornerShape(20.dp),
|
|
||||||
colors = ButtonDefaults
|
|
||||||
.buttonColors(
|
|
||||||
backgroundColor = MaterialTheme.colors.primary
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
"⚡ ${showAmount(amount?.toBigDecimal()?.setScale(1))}",
|
|
||||||
color = Color.White,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
modifier = Modifier.combinedClickable(
|
|
||||||
onClick = {
|
|
||||||
if (amount != null && isValidInputAmount) {
|
|
||||||
accountViewModel.zap(
|
|
||||||
baseNote,
|
|
||||||
amount * 1000,
|
|
||||||
pollOption,
|
|
||||||
"",
|
|
||||||
context,
|
|
||||||
onError,
|
|
||||||
onProgress
|
|
||||||
)
|
|
||||||
onDismiss()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onLongClick = {}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,17 +1,23 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.note
|
package com.vitorpamplona.amethyst.ui.note
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
|
import com.vitorpamplona.amethyst.model.RelaySetupInfo
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.service.model.*
|
import com.vitorpamplona.amethyst.service.model.*
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@Immutable
|
||||||
data class PollOption(
|
data class PollOption(
|
||||||
val option: Int,
|
val option: Int,
|
||||||
val descriptor: String,
|
val descriptor: String,
|
||||||
@@ -21,21 +27,23 @@ data class PollOption(
|
|||||||
val zappedByLoggedIn: Boolean
|
val zappedByLoggedIn: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Stable
|
||||||
class PollNoteViewModel : ViewModel() {
|
class PollNoteViewModel : ViewModel() {
|
||||||
var account: Account? = null
|
private var account: Account? = null
|
||||||
private var pollNote: Note? = null
|
private var pollNote: Note? = null
|
||||||
|
|
||||||
var pollEvent: PollNoteEvent? = null
|
private var pollEvent: PollNoteEvent? = null
|
||||||
var pollOptions: Map<Int, String>? = null
|
private var pollOptions: Map<Int, String>? = null
|
||||||
var valueMaximum: Int? = null
|
private var valueMaximum: Int? = null
|
||||||
var valueMinimum: Int? = null
|
private var valueMinimum: Int? = null
|
||||||
private var closedAt: Int? = null
|
private var closedAt: Int? = null
|
||||||
var consensusThreshold: BigDecimal? = null
|
private var consensusThreshold: BigDecimal? = null
|
||||||
|
|
||||||
var totalZapped: BigDecimal = BigDecimal.ZERO
|
private var totalZapped: BigDecimal = BigDecimal.ZERO
|
||||||
var wasZappedByLoggedInAccount: Boolean = false
|
private var wasZappedByLoggedInAccount: Boolean = false
|
||||||
|
|
||||||
var tallies by mutableStateOf<List<PollOption>>(emptyList())
|
private val _tallies = MutableStateFlow<List<PollOption>>(emptyList())
|
||||||
|
val tallies = _tallies.asStateFlow()
|
||||||
|
|
||||||
fun load(acc: Account, note: Note?) {
|
fun load(acc: Account, note: Note?) {
|
||||||
account = acc
|
account = acc
|
||||||
@@ -46,36 +54,35 @@ class PollNoteViewModel : ViewModel() {
|
|||||||
valueMinimum = pollEvent?.getTagInt(VALUE_MINIMUM)
|
valueMinimum = pollEvent?.getTagInt(VALUE_MINIMUM)
|
||||||
consensusThreshold = pollEvent?.getTagInt(CONSENSUS_THRESHOLD)?.toFloat()?.div(100)?.toBigDecimal()
|
consensusThreshold = pollEvent?.getTagInt(CONSENSUS_THRESHOLD)?.toFloat()?.div(100)?.toBigDecimal()
|
||||||
closedAt = pollEvent?.getTagInt(CLOSED_AT)
|
closedAt = pollEvent?.getTagInt(CLOSED_AT)
|
||||||
|
|
||||||
refreshTallies()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refreshTallies() {
|
suspend fun refreshTallies() {
|
||||||
totalZapped = totalZapped()
|
totalZapped = totalZapped()
|
||||||
wasZappedByLoggedInAccount = pollNote?.let { account?.calculateIfNoteWasZappedByAccount(it) } ?: false
|
wasZappedByLoggedInAccount = pollNote?.let { account?.calculateIfNoteWasZappedByAccount(it) } ?: false
|
||||||
|
|
||||||
tallies = pollOptions?.keys?.map {
|
_tallies.emit(
|
||||||
val zappedInOption = zappedPollOptionAmount(it)
|
pollOptions?.keys?.map {
|
||||||
|
val zappedInOption = zappedPollOptionAmount(it)
|
||||||
|
|
||||||
val myTally = if (totalZapped.compareTo(BigDecimal.ZERO) > 0) {
|
val myTally = if (totalZapped.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
zappedInOption.divide(totalZapped, 2, RoundingMode.HALF_UP)
|
zappedInOption.divide(totalZapped, 2, RoundingMode.HALF_UP)
|
||||||
} else {
|
} else {
|
||||||
BigDecimal.ZERO
|
BigDecimal.ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
val zappedByLoggedIn = account?.userProfile()?.let { it1 -> isPollOptionZappedBy(it, it1) } ?: false
|
val zappedByLoggedIn = account?.userProfile()?.let { it1 -> isPollOptionZappedBy(it, it1) } ?: false
|
||||||
|
|
||||||
val consensus = consensusThreshold != null && myTally >= consensusThreshold!!
|
val consensus = consensusThreshold != null && myTally >= consensusThreshold!!
|
||||||
|
|
||||||
PollOption(it, pollOptions?.get(it) ?: "", zappedInOption, myTally, consensus, zappedByLoggedIn)
|
PollOption(it, pollOptions?.get(it) ?: "", zappedInOption, myTally, consensus, zappedByLoggedIn)
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canZap(): Boolean {
|
fun canZap(): Boolean {
|
||||||
val account = account ?: return false
|
val account = account ?: return false
|
||||||
val user = account.userProfile() ?: return false
|
|
||||||
val note = pollNote ?: return false
|
val note = pollNote ?: return false
|
||||||
return user != note.author && !wasZappedByLoggedInAccount
|
return account.userProfile() != note.author && !wasZappedByLoggedInAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isVoteAmountAtomic() = valueMaximum != null && valueMinimum != null && valueMinimum == valueMaximum
|
fun isVoteAmountAtomic() = valueMaximum != null && valueMinimum != null && valueMinimum == valueMaximum
|
||||||
@@ -154,4 +161,21 @@ class PollNoteViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
} ?: BigDecimal(0)
|
} ?: BigDecimal(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createZapOptionsThatMatchThePollingParameters(): List<Long> {
|
||||||
|
val options = account?.zapAmountChoices?.filter { isValidInputVoteAmount(it) }?.toMutableList() ?: mutableListOf()
|
||||||
|
if (options.isEmpty()) {
|
||||||
|
valueMinimum?.let { minimum ->
|
||||||
|
valueMaximum?.let { maximum ->
|
||||||
|
if (minimum != maximum) {
|
||||||
|
options.add(((minimum + maximum) / 2).toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
valueMinimum?.let { options.add(it.toLong()) }
|
||||||
|
valueMaximum?.let { options.add(it.toLong()) }
|
||||||
|
|
||||||
|
return options.toSet().sorted()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,6 +153,10 @@ private fun FeedLoaded(
|
|||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
routeForLastRead: String
|
routeForLastRead: String
|
||||||
) {
|
) {
|
||||||
|
val defaultModifier = remember {
|
||||||
|
Modifier.fillMaxWidth().defaultMinSize(minHeight = 100.dp)
|
||||||
|
}
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = remember { Modifier.fillMaxSize() },
|
modifier = remember { Modifier.fillMaxSize() },
|
||||||
contentPadding = remember {
|
contentPadding = remember {
|
||||||
@@ -164,7 +168,7 @@ private fun FeedLoaded(
|
|||||||
state = listState
|
state = listState
|
||||||
) {
|
) {
|
||||||
itemsIndexed(state.feed.value, key = { _, item -> item.id() }) { _, item ->
|
itemsIndexed(state.feed.value, key = { _, item -> item.id() }) { _, item ->
|
||||||
Row(Modifier.fillMaxWidth().defaultMinSize(minHeight = 100.dp)) {
|
Row(defaultModifier) {
|
||||||
when (item) {
|
when (item) {
|
||||||
is NoteCard -> NoteCompose(
|
is NoteCard -> NoteCompose(
|
||||||
item.note,
|
item.note,
|
||||||
|
|||||||
Reference in New Issue
Block a user