Merge pull request #1764 from greenart7c3/claude/reactions-settings-HhrO0
Add customizable reactions row settings screen
This commit is contained in:
@@ -468,6 +468,12 @@ class Account(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun changeReactionRowItems(items: List<ReactionRowItem>) {
|
||||
if (settings.changeReactionRowItems(items)) {
|
||||
sendNewAppSpecificData()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateZapAmounts(
|
||||
amountSet: List<Long>,
|
||||
selectedZapType: LnZapEvent.ZapType,
|
||||
|
||||
@@ -235,6 +235,15 @@ class AccountSettings(
|
||||
return false
|
||||
}
|
||||
|
||||
fun changeReactionRowItems(newItems: List<ReactionRowItem>): Boolean {
|
||||
if (syncedSettings.reactions.reactionRowItems.value != newItems) {
|
||||
syncedSettings.reactions.reactionRowItems.tryEmit(newItems.toImmutableList())
|
||||
saveAccountSettings()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun changeZapPaymentRequest(newServer: Nip47WalletConnect.Nip47URINorm?): Boolean {
|
||||
if (zapPaymentRequest.value != newServer) {
|
||||
zapPaymentRequest.tryEmit(newServer)
|
||||
|
||||
@@ -33,7 +33,11 @@ import java.util.Locale
|
||||
class AccountSyncedSettings(
|
||||
internalSettings: AccountSyncedSettingsInternal,
|
||||
) {
|
||||
val reactions = AccountReactionPreferences(MutableStateFlow(internalSettings.reactions.reactionChoices.toImmutableList()))
|
||||
val reactions =
|
||||
AccountReactionPreferences(
|
||||
MutableStateFlow(internalSettings.reactions.reactionChoices.toImmutableList()),
|
||||
MutableStateFlow(internalSettings.reactions.reactionRowItems.toImmutableList()),
|
||||
)
|
||||
val zaps =
|
||||
AccountZapPreferences(
|
||||
MutableStateFlow(internalSettings.zaps.zapAmountChoices.toImmutableList()),
|
||||
@@ -54,7 +58,7 @@ class AccountSyncedSettings(
|
||||
|
||||
fun toInternal(): AccountSyncedSettingsInternal =
|
||||
AccountSyncedSettingsInternal(
|
||||
reactions = AccountReactionPreferencesInternal(reactions.reactionChoices.value),
|
||||
reactions = AccountReactionPreferencesInternal(reactions.reactionChoices.value, reactions.reactionRowItems.value),
|
||||
zaps =
|
||||
AccountZapPreferencesInternal(
|
||||
zaps.zapAmountChoices.value,
|
||||
@@ -80,6 +84,11 @@ class AccountSyncedSettings(
|
||||
reactions.reactionChoices.tryEmit(newReactionChoices)
|
||||
}
|
||||
|
||||
val newReactionRowItems = syncedSettingsInternal.reactions.reactionRowItems.toImmutableList()
|
||||
if (!equalImmutableLists(reactions.reactionRowItems.value, newReactionRowItems)) {
|
||||
reactions.reactionRowItems.tryEmit(newReactionRowItems)
|
||||
}
|
||||
|
||||
val newZapChoices = syncedSettingsInternal.zaps.zapAmountChoices.toImmutableList()
|
||||
if (!equalImmutableLists(zaps.zapAmountChoices.value, newZapChoices)) {
|
||||
zaps.zapAmountChoices.tryEmit(newZapChoices)
|
||||
@@ -120,6 +129,7 @@ class AccountSyncedSettings(
|
||||
@Stable
|
||||
class AccountReactionPreferences(
|
||||
var reactionChoices: MutableStateFlow<ImmutableList<String>>,
|
||||
var reactionRowItems: MutableStateFlow<ImmutableList<ReactionRowItem>>,
|
||||
)
|
||||
|
||||
@Stable
|
||||
|
||||
+26
@@ -39,6 +39,31 @@ val DefaultReactions =
|
||||
|
||||
val DefaultZapAmounts = listOf(100L, 500L, 1000L)
|
||||
|
||||
@Serializable
|
||||
enum class ReactionRowAction {
|
||||
Reply,
|
||||
Boost,
|
||||
Like,
|
||||
Zap,
|
||||
Share,
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ReactionRowItem(
|
||||
val action: ReactionRowAction,
|
||||
val enabled: Boolean = true,
|
||||
val showCounter: Boolean = true,
|
||||
)
|
||||
|
||||
val DefaultReactionRowItems =
|
||||
listOf(
|
||||
ReactionRowItem(ReactionRowAction.Reply),
|
||||
ReactionRowItem(ReactionRowAction.Boost),
|
||||
ReactionRowItem(ReactionRowAction.Like),
|
||||
ReactionRowItem(ReactionRowAction.Zap),
|
||||
ReactionRowItem(ReactionRowAction.Share, showCounter = false),
|
||||
)
|
||||
|
||||
fun getLanguagesSpokenByUser(): Set<String> {
|
||||
val languageList = ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration())
|
||||
val codedList = mutableSetOf<String>()
|
||||
@@ -59,6 +84,7 @@ class AccountSyncedSettingsInternal(
|
||||
@Serializable
|
||||
class AccountReactionPreferencesInternal(
|
||||
var reactionChoices: List<String> = DefaultReactions,
|
||||
var reactionRowItems: List<ReactionRowItem> = DefaultReactionRowItems,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
||||
@@ -109,6 +109,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationScre
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.search.SearchScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.AllSettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.NIP47SetupScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.ReactionsSettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SecurityFiltersScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.UpdateZapAmountScreen
|
||||
@@ -175,6 +176,7 @@ fun AppNavigation(
|
||||
composableFromEnd<Route.Drafts> { DraftListScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.Settings> { SettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.UserSettings> { UserSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.ReactionsSettings> { ReactionsSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Nip47NWCSetup> { NIP47SetupScreen(accountViewModel, nav, it.nip47) }
|
||||
composableFromEndArgs<Route.UpdateZapAmount> { UpdateZapAmountScreen(accountViewModel, nav, it.nip47) }
|
||||
composableFromEndArgs<Route.EditRelays> { AllRelayListScreen(accountViewModel, nav) }
|
||||
|
||||
@@ -88,6 +88,8 @@ sealed class Route {
|
||||
|
||||
@Serializable object UserSettings : Route()
|
||||
|
||||
@Serializable object ReactionsSettings : Route()
|
||||
|
||||
@Serializable object Lists : Route()
|
||||
|
||||
@Serializable data class MyPeopleListView(
|
||||
|
||||
@@ -100,6 +100,8 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.emojicoder.EmojiCoder
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.ReactionRowAction
|
||||
import com.vitorpamplona.amethyst.model.ReactionRowItem
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.ZapPaymentHandler
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteReactionCount
|
||||
@@ -212,6 +214,7 @@ private fun InnerReactionRow(
|
||||
nav: INav,
|
||||
) {
|
||||
val voiceRecordingState = remember(baseNote.idHex) { mutableStateOf(false) }
|
||||
val reactionRowItems by accountViewModel.reactionRowItemsFlow().collectAsStateWithLifecycle()
|
||||
|
||||
GenericInnerReactionRow(
|
||||
showReactionDetail = showReactionDetail,
|
||||
@@ -222,16 +225,21 @@ private fun InnerReactionRow(
|
||||
RenderShowIndividualReactionsButton(wantsToSeeReactions, accountViewModel)
|
||||
}
|
||||
},
|
||||
two = {
|
||||
reactions = reactionRowItems,
|
||||
renderReaction = { item ->
|
||||
when (item.action) {
|
||||
ReactionRowAction.Reply -> {
|
||||
ReplyReactionWithDialog(
|
||||
baseNote,
|
||||
MaterialTheme.colorScheme.placeholderText,
|
||||
accountViewModel,
|
||||
nav,
|
||||
showCounter = item.showCounter,
|
||||
voiceRecordingState = voiceRecordingState,
|
||||
)
|
||||
},
|
||||
three = {
|
||||
}
|
||||
|
||||
ReactionRowAction.Boost -> {
|
||||
val isDM = baseNote.event is ChatroomKeyable
|
||||
if (!isDM) {
|
||||
BoostWithDialog(
|
||||
@@ -240,20 +248,38 @@ private fun InnerReactionRow(
|
||||
MaterialTheme.colorScheme.placeholderText,
|
||||
accountViewModel,
|
||||
nav,
|
||||
showCounter = item.showCounter,
|
||||
)
|
||||
}
|
||||
},
|
||||
four = {
|
||||
LikeReaction(baseNote, MaterialTheme.colorScheme.placeholderText, accountViewModel, nav)
|
||||
},
|
||||
five = {
|
||||
ZapReaction(baseNote, MaterialTheme.colorScheme.placeholderText, accountViewModel, nav = nav)
|
||||
},
|
||||
six = {
|
||||
}
|
||||
|
||||
ReactionRowAction.Like -> {
|
||||
LikeReaction(
|
||||
baseNote,
|
||||
MaterialTheme.colorScheme.placeholderText,
|
||||
accountViewModel,
|
||||
nav,
|
||||
showCounter = item.showCounter,
|
||||
)
|
||||
}
|
||||
|
||||
ReactionRowAction.Zap -> {
|
||||
ZapReaction(
|
||||
baseNote,
|
||||
MaterialTheme.colorScheme.placeholderText,
|
||||
accountViewModel,
|
||||
nav = nav,
|
||||
showCounter = item.showCounter,
|
||||
)
|
||||
}
|
||||
|
||||
ReactionRowAction.Share -> {
|
||||
ShareReaction(
|
||||
note = baseNote,
|
||||
grayTint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -301,12 +327,12 @@ private fun GenericInnerReactionRow(
|
||||
addPadding: Boolean,
|
||||
weightTwo: Float = 1f,
|
||||
one: @Composable () -> Unit,
|
||||
two: @Composable () -> Unit,
|
||||
three: @Composable () -> Unit,
|
||||
four: @Composable () -> Unit,
|
||||
five: @Composable () -> Unit,
|
||||
six: @Composable () -> Unit,
|
||||
reactions: ImmutableList<ReactionRowItem>,
|
||||
renderReaction: @Composable (ReactionRowItem) -> Unit,
|
||||
) {
|
||||
val enabledReactions = remember(reactions) { reactions.filter { it.enabled } }
|
||||
val lastIndex = enabledReactions.lastIndex
|
||||
|
||||
Row(
|
||||
verticalAlignment = CenterVertically,
|
||||
horizontalArrangement = RowColSpacing,
|
||||
@@ -321,15 +347,18 @@ private fun GenericInnerReactionRow(
|
||||
}
|
||||
}
|
||||
|
||||
Row(verticalAlignment = CenterVertically, horizontalArrangement = RowColSpacing, modifier = Modifier.weight(weightTwo)) { two() }
|
||||
|
||||
Row(verticalAlignment = CenterVertically, horizontalArrangement = RowColSpacing, modifier = Modifier.weight(1f)) { three() }
|
||||
|
||||
Row(verticalAlignment = CenterVertically, horizontalArrangement = RowColSpacing, modifier = Modifier.weight(1f)) { four() }
|
||||
|
||||
Row(verticalAlignment = CenterVertically, modifier = Modifier.weight(1f)) { five() }
|
||||
|
||||
Row(verticalAlignment = CenterVertically, modifier = Modifier) { six() }
|
||||
enabledReactions.forEachIndexed { index, item ->
|
||||
val isLast = index == lastIndex
|
||||
val itemWeight = if (item.action == ReactionRowAction.Reply) weightTwo else 1f
|
||||
val mod = if (isLast) Modifier else Modifier.weight(itemWeight)
|
||||
Row(
|
||||
verticalAlignment = CenterVertically,
|
||||
horizontalArrangement = RowColSpacing,
|
||||
modifier = mod,
|
||||
) {
|
||||
renderReaction(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,11 +577,13 @@ private fun BoostWithDialog(
|
||||
grayTint: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
showCounter: Boolean = true,
|
||||
) {
|
||||
BoostReaction(
|
||||
baseNote,
|
||||
grayTint,
|
||||
accountViewModel,
|
||||
showCounter = showCounter,
|
||||
onQuotePress = {
|
||||
nav.nav {
|
||||
Route.NewShortNote(
|
||||
@@ -597,6 +628,7 @@ private fun ReplyReactionWithDialog(
|
||||
grayTint: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
showCounter: Boolean = true,
|
||||
voiceRecordingState: MutableState<Boolean>? = null,
|
||||
) {
|
||||
if (baseNote.event is BaseVoiceEvent) {
|
||||
@@ -605,10 +637,11 @@ private fun ReplyReactionWithDialog(
|
||||
grayTint,
|
||||
accountViewModel,
|
||||
nav,
|
||||
showCounter = showCounter,
|
||||
voiceRecordingState = voiceRecordingState,
|
||||
)
|
||||
} else {
|
||||
ReplyReaction(baseNote, grayTint, accountViewModel) {
|
||||
ReplyReaction(baseNote, grayTint, accountViewModel, showCounter = showCounter) {
|
||||
nav.nav { routeReplyTo(baseNote, accountViewModel.account) }
|
||||
}
|
||||
}
|
||||
@@ -800,6 +833,7 @@ fun BoostReaction(
|
||||
accountViewModel: AccountViewModel,
|
||||
iconSizeModifier: Modifier = Size19Modifier,
|
||||
iconSize: Dp = Size20dp,
|
||||
showCounter: Boolean = true,
|
||||
onQuotePress: () -> Unit,
|
||||
onForkPress: () -> Unit,
|
||||
) {
|
||||
@@ -836,7 +870,9 @@ fun BoostReaction(
|
||||
}
|
||||
}
|
||||
|
||||
if (showCounter) {
|
||||
BoostText(baseNote, grayTint, accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -870,6 +906,7 @@ fun LikeReaction(
|
||||
iconSize: Dp = Size18dp,
|
||||
heartSizeModifier: Modifier = Size18Modifier,
|
||||
iconFontSize: TextUnit = Font14SP,
|
||||
showCounter: Boolean = true,
|
||||
) {
|
||||
var wantsToReact by remember { mutableStateOf(false) }
|
||||
|
||||
@@ -908,7 +945,9 @@ fun LikeReaction(
|
||||
}
|
||||
}
|
||||
|
||||
if (showCounter) {
|
||||
ObserveLikeText(baseNote, accountViewModel) { reactionCount -> SlidingAnimationCount(reactionCount, grayTint, accountViewModel) }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -1027,6 +1066,7 @@ fun ZapReaction(
|
||||
iconSize: Dp = Size20dp,
|
||||
iconSizeModifier: Modifier = Size20Modifier,
|
||||
animationModifier: Modifier = Size14Modifier,
|
||||
showCounter: Boolean = true,
|
||||
nav: INav,
|
||||
) {
|
||||
var wantsToZap by remember { mutableStateOf(false) }
|
||||
@@ -1194,9 +1234,11 @@ fun ZapReaction(
|
||||
}
|
||||
}
|
||||
|
||||
if (showCounter) {
|
||||
ObserveZapAmountText(baseNote, accountViewModel) { zapAmountTxt ->
|
||||
SlidingAnimationAmount(zapAmountTxt, grayTint, accountViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun zapClick(
|
||||
|
||||
+7
@@ -986,6 +986,13 @@ class AccountViewModel(
|
||||
onDone()
|
||||
}
|
||||
|
||||
fun reactionRowItemsFlow() = account.settings.syncedSettings.reactions.reactionRowItems
|
||||
|
||||
fun changeReactionRowItems(items: List<com.vitorpamplona.amethyst.model.ReactionRowItem>) =
|
||||
launchSigner {
|
||||
account.changeReactionRowItems(items)
|
||||
}
|
||||
|
||||
fun updateZapAmounts(
|
||||
amountSet: List<Long>,
|
||||
selectedZapType: LnZapEvent.ZapType,
|
||||
|
||||
+8
@@ -33,6 +33,7 @@ import androidx.compose.material.icons.outlined.FavoriteBorder
|
||||
import androidx.compose.material.icons.outlined.Key
|
||||
import androidx.compose.material.icons.outlined.Security
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material.icons.outlined.ThumbUp
|
||||
import androidx.compose.material.icons.outlined.Translate
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -153,6 +154,13 @@ fun AllSettingsScreen(
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.Settings) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.reactions_settings,
|
||||
icon = Icons.Outlined.ThumbUp,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.ReactionsSettings) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* 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.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
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.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.ReactionRowAction
|
||||
import com.vitorpamplona.amethyst.model.ReactionRowItem
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20dp
|
||||
|
||||
@Composable
|
||||
fun ReactionsSettingsScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBarWithBackButton(stringRes(id = R.string.reactions_settings), nav::popBack)
|
||||
},
|
||||
) { padding ->
|
||||
Column(Modifier.padding(padding)) {
|
||||
ReactionsSettingsContent(accountViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ReactionsSettingsContent(accountViewModel: AccountViewModel) {
|
||||
val reactionRowItems by accountViewModel.reactionRowItemsFlow().collectAsStateWithLifecycle()
|
||||
var items by remember(reactionRowItems) { mutableStateOf(reactionRowItems.toMutableList()) }
|
||||
|
||||
fun save(newItems: List<ReactionRowItem>) {
|
||||
items = newItems.toMutableList()
|
||||
accountViewModel.changeReactionRowItems(newItems)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = Size20dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = stringRes(R.string.reactions_settings_description),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = Color.Gray,
|
||||
modifier = Modifier.padding(bottom = 16.dp),
|
||||
)
|
||||
|
||||
items.forEachIndexed { index, item ->
|
||||
ReactionRowItemCard(
|
||||
item = item,
|
||||
canMoveUp = index > 0,
|
||||
canMoveDown = index < items.lastIndex,
|
||||
onToggleEnabled = {
|
||||
val newItems = items.toMutableList()
|
||||
newItems[index] = item.copy(enabled = !item.enabled)
|
||||
save(newItems)
|
||||
},
|
||||
onToggleCounter = {
|
||||
val newItems = items.toMutableList()
|
||||
newItems[index] = item.copy(showCounter = !item.showCounter)
|
||||
save(newItems)
|
||||
},
|
||||
onMoveUp = {
|
||||
if (index > 0) {
|
||||
val newItems = items.toMutableList()
|
||||
val temp = newItems[index - 1]
|
||||
newItems[index - 1] = newItems[index]
|
||||
newItems[index] = temp
|
||||
save(newItems)
|
||||
}
|
||||
},
|
||||
onMoveDown = {
|
||||
if (index < items.lastIndex) {
|
||||
val newItems = items.toMutableList()
|
||||
val temp = newItems[index + 1]
|
||||
newItems[index + 1] = newItems[index]
|
||||
newItems[index] = temp
|
||||
save(newItems)
|
||||
}
|
||||
},
|
||||
)
|
||||
if (index < items.lastIndex) {
|
||||
HorizontalDivider()
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReactionRowItemCard(
|
||||
item: ReactionRowItem,
|
||||
canMoveUp: Boolean,
|
||||
canMoveDown: Boolean,
|
||||
onToggleEnabled: () -> Unit,
|
||||
onToggleCounter: () -> Unit,
|
||||
onMoveUp: () -> Unit,
|
||||
onMoveDown: () -> Unit,
|
||||
) {
|
||||
val actionName = reactionActionName(item.action)
|
||||
val actionDescription = reactionActionDescription(item.action)
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = actionName,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(
|
||||
text = actionDescription,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = Color.Gray,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
IconButton(
|
||||
onClick = onMoveUp,
|
||||
enabled = canMoveUp,
|
||||
modifier = Modifier.size(32.dp),
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.KeyboardArrowUp,
|
||||
contentDescription = stringRes(R.string.reactions_settings_move_up),
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = if (canMoveUp) MaterialTheme.colorScheme.onSurface else Color.Gray,
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = onMoveDown,
|
||||
enabled = canMoveDown,
|
||||
modifier = Modifier.size(32.dp),
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.KeyboardArrowDown,
|
||||
contentDescription = stringRes(R.string.reactions_settings_move_down),
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = if (canMoveDown) MaterialTheme.colorScheme.onSurface else Color.Gray,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Switch(
|
||||
checked = item.enabled,
|
||||
onCheckedChange = { onToggleEnabled() },
|
||||
)
|
||||
Text(
|
||||
text = stringRes(R.string.reactions_settings_enabled),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
|
||||
if (item.action != ReactionRowAction.Share) {
|
||||
Row(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Switch(
|
||||
checked = item.showCounter,
|
||||
onCheckedChange = { onToggleCounter() },
|
||||
enabled = item.enabled,
|
||||
)
|
||||
Text(
|
||||
text = stringRes(R.string.reactions_settings_show_counter),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = if (item.enabled) MaterialTheme.colorScheme.onSurface else Color.Gray,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun reactionActionName(action: ReactionRowAction): String =
|
||||
when (action) {
|
||||
ReactionRowAction.Reply -> stringRes(R.string.reactions_settings_reply)
|
||||
ReactionRowAction.Boost -> stringRes(R.string.reactions_settings_boost)
|
||||
ReactionRowAction.Like -> stringRes(R.string.reactions_settings_like)
|
||||
ReactionRowAction.Zap -> stringRes(R.string.reactions_settings_zap)
|
||||
ReactionRowAction.Share -> stringRes(R.string.reactions_settings_share)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun reactionActionDescription(action: ReactionRowAction): String =
|
||||
when (action) {
|
||||
ReactionRowAction.Reply -> stringRes(R.string.reactions_settings_reply_description)
|
||||
ReactionRowAction.Boost -> stringRes(R.string.reactions_settings_boost_description)
|
||||
ReactionRowAction.Like -> stringRes(R.string.reactions_settings_like_description)
|
||||
ReactionRowAction.Zap -> stringRes(R.string.reactions_settings_zap_description)
|
||||
ReactionRowAction.Share -> stringRes(R.string.reactions_settings_share_description)
|
||||
}
|
||||
@@ -1234,6 +1234,23 @@
|
||||
|
||||
<string name="change_reaction">Change Quick Reactions</string>
|
||||
|
||||
<string name="reactions_settings">Reactions Settings</string>
|
||||
<string name="reactions_settings_description">Configure which reaction buttons are shown, their order, and whether to display counters.</string>
|
||||
<string name="reactions_settings_enabled">Enabled</string>
|
||||
<string name="reactions_settings_show_counter">Show Count</string>
|
||||
<string name="reactions_settings_move_up">Move up</string>
|
||||
<string name="reactions_settings_move_down">Move down</string>
|
||||
<string name="reactions_settings_reply">Reply</string>
|
||||
<string name="reactions_settings_reply_description">Reply to this note</string>
|
||||
<string name="reactions_settings_boost">Boost</string>
|
||||
<string name="reactions_settings_boost_description">Repost or quote this note</string>
|
||||
<string name="reactions_settings_like">Like</string>
|
||||
<string name="reactions_settings_like_description">React to this note with an emoji</string>
|
||||
<string name="reactions_settings_zap">Zap</string>
|
||||
<string name="reactions_settings_zap_description">Send a lightning payment to the author</string>
|
||||
<string name="reactions_settings_share">Share</string>
|
||||
<string name="reactions_settings_share_description">Share this note externally</string>
|
||||
|
||||
<string name="profile_image_of_user">Profile Picture of %1$s</string>
|
||||
<string name="relay_info">Relay %1$s</string>
|
||||
<string name="expand_relay_list">Expand relay list</string>
|
||||
|
||||
Reference in New Issue
Block a user