feat: add Profile UI settings page
New "Profile UI" settings page lets users toggle which profile-screen sections are loaded and displayed. All toggles default to on. Toggles: - Profile Badges (NIP-58 badges row) - App Recommendations (apps row in profile header) - Zap Received Feed (received zaps tab) - Followers Feed (followers tab) Tabs are filtered from the profile pager so disabled feeds no longer allocate a page or fire their subscriptions.
This commit is contained in:
@@ -48,6 +48,10 @@ data class UiSettings(
|
||||
val showHomeNewThreadsTab: Boolean = true,
|
||||
val showHomeConversationsTab: Boolean = true,
|
||||
val showHomeEverythingTab: Boolean = false,
|
||||
val showProfileBadges: Boolean = true,
|
||||
val showProfileAppRecommendations: Boolean = true,
|
||||
val showProfileZapReceivedFeed: Boolean = true,
|
||||
val showProfileFollowersFeed: Boolean = true,
|
||||
)
|
||||
|
||||
enum class ThemeType(
|
||||
|
||||
@@ -48,6 +48,10 @@ class UiSettingsFlow(
|
||||
val showHomeNewThreadsTab: MutableStateFlow<Boolean> = MutableStateFlow(true),
|
||||
val showHomeConversationsTab: MutableStateFlow<Boolean> = MutableStateFlow(true),
|
||||
val showHomeEverythingTab: MutableStateFlow<Boolean> = MutableStateFlow(false),
|
||||
val showProfileBadges: MutableStateFlow<Boolean> = MutableStateFlow(true),
|
||||
val showProfileAppRecommendations: MutableStateFlow<Boolean> = MutableStateFlow(true),
|
||||
val showProfileZapReceivedFeed: MutableStateFlow<Boolean> = MutableStateFlow(true),
|
||||
val showProfileFollowersFeed: MutableStateFlow<Boolean> = MutableStateFlow(true),
|
||||
) {
|
||||
val listOfFlows: List<Flow<Any?>> =
|
||||
listOf<Flow<Any?>>(
|
||||
@@ -70,6 +74,10 @@ class UiSettingsFlow(
|
||||
showHomeNewThreadsTab,
|
||||
showHomeConversationsTab,
|
||||
showHomeEverythingTab,
|
||||
showProfileBadges,
|
||||
showProfileAppRecommendations,
|
||||
showProfileZapReceivedFeed,
|
||||
showProfileFollowersFeed,
|
||||
)
|
||||
|
||||
// emits at every change in any of the propertyes.
|
||||
@@ -96,6 +104,10 @@ class UiSettingsFlow(
|
||||
flows[16] as Boolean,
|
||||
flows[17] as Boolean,
|
||||
flows[18] as Boolean,
|
||||
flows[19] as Boolean,
|
||||
flows[20] as Boolean,
|
||||
flows[21] as Boolean,
|
||||
flows[22] as Boolean,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -120,6 +132,10 @@ class UiSettingsFlow(
|
||||
showHomeNewThreadsTab.value,
|
||||
showHomeConversationsTab.value,
|
||||
showHomeEverythingTab.value,
|
||||
showProfileBadges.value,
|
||||
showProfileAppRecommendations.value,
|
||||
showProfileZapReceivedFeed.value,
|
||||
showProfileFollowersFeed.value,
|
||||
)
|
||||
|
||||
fun update(torSettings: UiSettings): Boolean {
|
||||
@@ -201,6 +217,22 @@ class UiSettingsFlow(
|
||||
showHomeEverythingTab.tryEmit(torSettings.showHomeEverythingTab)
|
||||
any = true
|
||||
}
|
||||
if (showProfileBadges.value != torSettings.showProfileBadges) {
|
||||
showProfileBadges.tryEmit(torSettings.showProfileBadges)
|
||||
any = true
|
||||
}
|
||||
if (showProfileAppRecommendations.value != torSettings.showProfileAppRecommendations) {
|
||||
showProfileAppRecommendations.tryEmit(torSettings.showProfileAppRecommendations)
|
||||
any = true
|
||||
}
|
||||
if (showProfileZapReceivedFeed.value != torSettings.showProfileZapReceivedFeed) {
|
||||
showProfileZapReceivedFeed.tryEmit(torSettings.showProfileZapReceivedFeed)
|
||||
any = true
|
||||
}
|
||||
if (showProfileFollowersFeed.value != torSettings.showProfileFollowersFeed) {
|
||||
showProfileFollowersFeed.tryEmit(torSettings.showProfileFollowersFeed)
|
||||
any = true
|
||||
}
|
||||
|
||||
return any
|
||||
}
|
||||
@@ -239,6 +271,10 @@ class UiSettingsFlow(
|
||||
MutableStateFlow(uiSettings.showHomeNewThreadsTab),
|
||||
MutableStateFlow(uiSettings.showHomeConversationsTab),
|
||||
MutableStateFlow(uiSettings.showHomeEverythingTab),
|
||||
MutableStateFlow(uiSettings.showProfileBadges),
|
||||
MutableStateFlow(uiSettings.showProfileAppRecommendations),
|
||||
MutableStateFlow(uiSettings.showProfileZapReceivedFeed),
|
||||
MutableStateFlow(uiSettings.showProfileFollowersFeed),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -112,6 +112,10 @@ class UiSharedPreferences(
|
||||
val UI_SHOW_HOME_NEW_THREADS_TAB = booleanPreferencesKey("ui.show_home_new_threads_tab")
|
||||
val UI_SHOW_HOME_CONVERSATIONS_TAB = booleanPreferencesKey("ui.show_home_conversations_tab")
|
||||
val UI_SHOW_HOME_EVERYTHING_TAB = booleanPreferencesKey("ui.show_home_everything_tab")
|
||||
val UI_SHOW_PROFILE_BADGES = booleanPreferencesKey("ui.show_profile_badges")
|
||||
val UI_SHOW_PROFILE_APP_RECOMMENDATIONS = booleanPreferencesKey("ui.show_profile_app_recommendations")
|
||||
val UI_SHOW_PROFILE_ZAP_RECEIVED_FEED = booleanPreferencesKey("ui.show_profile_zap_received_feed")
|
||||
val UI_SHOW_PROFILE_FOLLOWERS_FEED = booleanPreferencesKey("ui.show_profile_followers_feed")
|
||||
|
||||
suspend fun uiPreferences(context: Context): UiSettings? =
|
||||
try {
|
||||
@@ -142,6 +146,10 @@ class UiSharedPreferences(
|
||||
showHomeNewThreadsTab = preferences[UI_SHOW_HOME_NEW_THREADS_TAB] ?: true,
|
||||
showHomeConversationsTab = preferences[UI_SHOW_HOME_CONVERSATIONS_TAB] ?: true,
|
||||
showHomeEverythingTab = preferences[UI_SHOW_HOME_EVERYTHING_TAB] ?: false,
|
||||
showProfileBadges = preferences[UI_SHOW_PROFILE_BADGES] ?: true,
|
||||
showProfileAppRecommendations = preferences[UI_SHOW_PROFILE_APP_RECOMMENDATIONS] ?: true,
|
||||
showProfileZapReceivedFeed = preferences[UI_SHOW_PROFILE_ZAP_RECEIVED_FEED] ?: true,
|
||||
showProfileFollowersFeed = preferences[UI_SHOW_PROFILE_FOLLOWERS_FEED] ?: true,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
@@ -185,6 +193,10 @@ class UiSharedPreferences(
|
||||
preferences[UI_SHOW_HOME_NEW_THREADS_TAB] = sharedSettings.showHomeNewThreadsTab
|
||||
preferences[UI_SHOW_HOME_CONVERSATIONS_TAB] = sharedSettings.showHomeConversationsTab
|
||||
preferences[UI_SHOW_HOME_EVERYTHING_TAB] = sharedSettings.showHomeEverythingTab
|
||||
preferences[UI_SHOW_PROFILE_BADGES] = sharedSettings.showProfileBadges
|
||||
preferences[UI_SHOW_PROFILE_APP_RECOMMENDATIONS] = sharedSettings.showProfileAppRecommendations
|
||||
preferences[UI_SHOW_PROFILE_ZAP_RECEIVED_FEED] = sharedSettings.showProfileZapReceivedFeed
|
||||
preferences[UI_SHOW_PROFILE_FOLLOWERS_FEED] = sharedSettings.showProfileFollowersFeed
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
|
||||
@@ -161,6 +161,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.HomeTabsSettingsSc
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.NIP47SetupScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.NamecoinSettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.OtsSettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.ProfileUiSettingsScreen
|
||||
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
|
||||
@@ -313,6 +314,7 @@ fun BuildNavigation(
|
||||
composableFromEnd<Route.ReactionsSettings> { ReactionsSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.BottomBarSettings> { BottomBarSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.HomeTabsSettings> { HomeTabsSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.ProfileUiSettings> { ProfileUiSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.VideoPlayerSettings> { VideoPlayerSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.CallSettings> { CallSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.ImportFollowsSelectUser> { ImportFollowListSelectUserScreen(accountViewModel, nav) }
|
||||
|
||||
@@ -225,6 +225,8 @@ sealed class Route {
|
||||
|
||||
@Serializable object HomeTabsSettings : Route()
|
||||
|
||||
@Serializable object ProfileUiSettings : Route()
|
||||
|
||||
@Serializable object VideoPlayerSettings : Route()
|
||||
|
||||
@Serializable object CallSettings : Route()
|
||||
|
||||
+69
-32
@@ -59,6 +59,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
@@ -439,7 +440,28 @@ private fun RenderScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val pagerState = rememberPagerState { 11 }
|
||||
val ui = accountViewModel.settings.uiSettingsFlow
|
||||
val showFollowersTab by ui.showProfileFollowersFeed.collectAsStateWithLifecycle()
|
||||
val showZapsTab by ui.showProfileZapReceivedFeed.collectAsStateWithLifecycle()
|
||||
|
||||
val visibleTabs =
|
||||
remember(showFollowersTab, showZapsTab) {
|
||||
buildList {
|
||||
add(ProfileTab.Notes)
|
||||
add(ProfileTab.Replies)
|
||||
add(ProfileTab.Mutual)
|
||||
add(ProfileTab.Gallery)
|
||||
add(ProfileTab.Follows)
|
||||
if (showFollowersTab) add(ProfileTab.Followers)
|
||||
if (showZapsTab) add(ProfileTab.Zaps)
|
||||
add(ProfileTab.Bookmarks)
|
||||
add(ProfileTab.FollowedTags)
|
||||
add(ProfileTab.Reports)
|
||||
add(ProfileTab.Relays)
|
||||
}
|
||||
}
|
||||
|
||||
val pagerState = rememberPagerState(pageCount = { visibleTabs.size })
|
||||
|
||||
Column {
|
||||
ProfileHeader(baseUser, appRecommendations, externalIdentities, nav, accountViewModel)
|
||||
@@ -454,6 +476,7 @@ private fun RenderScreen(
|
||||
CreateAndRenderTabs(
|
||||
baseUser,
|
||||
pagerState,
|
||||
visibleTabs,
|
||||
threadsViewModel,
|
||||
repliesViewModel,
|
||||
mutualViewModel,
|
||||
@@ -471,7 +494,7 @@ private fun RenderScreen(
|
||||
modifier = pagerModifier,
|
||||
) { page ->
|
||||
CreateAndRenderPages(
|
||||
page,
|
||||
visibleTabs[page],
|
||||
baseUser,
|
||||
threadsViewModel,
|
||||
repliesViewModel,
|
||||
@@ -490,9 +513,23 @@ private fun RenderScreen(
|
||||
}
|
||||
}
|
||||
|
||||
private enum class ProfileTab {
|
||||
Notes,
|
||||
Replies,
|
||||
Mutual,
|
||||
Gallery,
|
||||
Follows,
|
||||
Followers,
|
||||
Zaps,
|
||||
Bookmarks,
|
||||
FollowedTags,
|
||||
Reports,
|
||||
Relays,
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CreateAndRenderPages(
|
||||
page: Int,
|
||||
tab: ProfileTab,
|
||||
baseUser: User,
|
||||
threadsViewModel: UserProfileNewThreadsFeedViewModel,
|
||||
repliesViewModel: UserProfileConversationsFeedViewModel,
|
||||
@@ -514,18 +551,18 @@ private fun CreateAndRenderPages(
|
||||
accountViewModel,
|
||||
)
|
||||
|
||||
when (page) {
|
||||
0 -> TabNotesNewThreads(threadsViewModel, pinnedNotesFeedViewModel, accountViewModel, nav)
|
||||
1 -> TabNotesConversations(repliesViewModel, accountViewModel, nav)
|
||||
2 -> TabMutualConversations(mutualViewModel, accountViewModel, nav)
|
||||
3 -> TabGallery(galleryFeedViewModel, accountViewModel, nav)
|
||||
4 -> TabFollows(followsFeedViewModel, accountViewModel, nav)
|
||||
5 -> TabFollowers(followersFeedViewModel, accountViewModel, nav)
|
||||
6 -> TabReceivedZaps(baseUser, zapFeedViewModel, accountViewModel, nav)
|
||||
7 -> TabBookmarks(bookmarksFeedViewModel, accountViewModel, nav)
|
||||
8 -> TabFollowedTags(baseUser, accountViewModel, nav)
|
||||
9 -> TabReports(baseUser, reportsFeedViewModel, accountViewModel, nav)
|
||||
10 -> TabRelays(baseUser, accountViewModel, nav)
|
||||
when (tab) {
|
||||
ProfileTab.Notes -> TabNotesNewThreads(threadsViewModel, pinnedNotesFeedViewModel, accountViewModel, nav)
|
||||
ProfileTab.Replies -> TabNotesConversations(repliesViewModel, accountViewModel, nav)
|
||||
ProfileTab.Mutual -> TabMutualConversations(mutualViewModel, accountViewModel, nav)
|
||||
ProfileTab.Gallery -> TabGallery(galleryFeedViewModel, accountViewModel, nav)
|
||||
ProfileTab.Follows -> TabFollows(followsFeedViewModel, accountViewModel, nav)
|
||||
ProfileTab.Followers -> TabFollowers(followersFeedViewModel, accountViewModel, nav)
|
||||
ProfileTab.Zaps -> TabReceivedZaps(baseUser, zapFeedViewModel, accountViewModel, nav)
|
||||
ProfileTab.Bookmarks -> TabBookmarks(bookmarksFeedViewModel, accountViewModel, nav)
|
||||
ProfileTab.FollowedTags -> TabFollowedTags(baseUser, accountViewModel, nav)
|
||||
ProfileTab.Reports -> TabReports(baseUser, reportsFeedViewModel, accountViewModel, nav)
|
||||
ProfileTab.Relays -> TabRelays(baseUser, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,6 +585,7 @@ fun UpdateThreadsAndRepliesWhenBlockUnblock(
|
||||
private fun CreateAndRenderTabs(
|
||||
baseUser: User,
|
||||
pagerState: PagerState,
|
||||
visibleTabs: List<ProfileTab>,
|
||||
threadsViewModel: UserProfileNewThreadsFeedViewModel,
|
||||
repliesViewModel: UserProfileConversationsFeedViewModel,
|
||||
mutualViewModel: UserProfileMutualFeedViewModel,
|
||||
@@ -561,26 +599,25 @@ private fun CreateAndRenderTabs(
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
val tabs =
|
||||
listOf<@Composable (() -> Unit)?>(
|
||||
{ Text(text = stringRes(R.string.notes)) },
|
||||
{ Text(text = stringRes(R.string.replies)) },
|
||||
{ Text(text = stringRes(R.string.mutual)) },
|
||||
{ Text(text = stringRes(R.string.gallery)) },
|
||||
{ FollowTabHeader(followsFeedViewModel, accountViewModel) },
|
||||
{ FollowersTabHeader(baseUser, followersFeedViewModel, accountViewModel) },
|
||||
{ ZapTabHeader(zapFeedViewModel, accountViewModel) },
|
||||
{ BookmarkTabHeader(baseUser, accountViewModel) },
|
||||
{ FollowedTagsTabHeader(baseUser, accountViewModel) },
|
||||
{ ReportsTabHeader(baseUser, reportsFeedViewModel, accountViewModel) },
|
||||
{ RelaysTabHeader(baseUser, accountViewModel) },
|
||||
)
|
||||
|
||||
tabs.forEachIndexed { index, function ->
|
||||
visibleTabs.forEachIndexed { index, tab ->
|
||||
Tab(
|
||||
selected = pagerState.currentPage == index,
|
||||
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(index) } },
|
||||
text = function,
|
||||
text = {
|
||||
when (tab) {
|
||||
ProfileTab.Notes -> Text(text = stringRes(R.string.notes))
|
||||
ProfileTab.Replies -> Text(text = stringRes(R.string.replies))
|
||||
ProfileTab.Mutual -> Text(text = stringRes(R.string.mutual))
|
||||
ProfileTab.Gallery -> Text(text = stringRes(R.string.gallery))
|
||||
ProfileTab.Follows -> FollowTabHeader(followsFeedViewModel, accountViewModel)
|
||||
ProfileTab.Followers -> FollowersTabHeader(baseUser, followersFeedViewModel, accountViewModel)
|
||||
ProfileTab.Zaps -> ZapTabHeader(zapFeedViewModel, accountViewModel)
|
||||
ProfileTab.Bookmarks -> BookmarkTabHeader(baseUser, accountViewModel)
|
||||
ProfileTab.FollowedTags -> FollowedTagsTabHeader(baseUser, accountViewModel)
|
||||
ProfileTab.Reports -> ReportsTabHeader(baseUser, reportsFeedViewModel, accountViewModel)
|
||||
ProfileTab.Relays -> RelaysTabHeader(baseUser, accountViewModel)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -113,6 +113,10 @@ fun DrawAdditionalInfo(
|
||||
|
||||
val displayName = user.info.bestName()
|
||||
|
||||
val ui = accountViewModel.settings.uiSettingsFlow
|
||||
val showBadges by ui.showProfileBadges.collectAsStateWithLifecycle()
|
||||
val showAppRecommendations by ui.showProfileAppRecommendations.collectAsStateWithLifecycle()
|
||||
|
||||
Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = SpacedBy3dp) {
|
||||
if (displayName != null) {
|
||||
Row(
|
||||
@@ -267,7 +271,9 @@ fun DrawAdditionalInfo(
|
||||
}
|
||||
}
|
||||
|
||||
DisplayBadges(baseUser, accountViewModel, nav)
|
||||
if (showBadges) {
|
||||
DisplayBadges(baseUser, accountViewModel, nav)
|
||||
}
|
||||
|
||||
user.info.about?.let {
|
||||
Row(
|
||||
@@ -289,7 +295,9 @@ fun DrawAdditionalInfo(
|
||||
}
|
||||
}
|
||||
|
||||
DisplayAppRecommendations(appRecommendations, accountViewModel, nav)
|
||||
if (showAppRecommendations) {
|
||||
DisplayAppRecommendations(appRecommendations, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -259,6 +259,12 @@ fun AllSettingsScreen(
|
||||
icon = MaterialSymbols.Home,
|
||||
onClick = { nav.nav(Route.HomeTabsSettings) },
|
||||
)
|
||||
SettingsDivider()
|
||||
SettingsItem(
|
||||
title = R.string.profile_ui_settings,
|
||||
icon = MaterialSymbols.AccountCircle,
|
||||
onClick = { nav.nav(Route.ProfileUiSettings) },
|
||||
)
|
||||
}
|
||||
|
||||
SettingsSection(R.string.danger_zone, isDanger = true) {
|
||||
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
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.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav
|
||||
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.screen.loggedIn.mockAccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonRow
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun ProfileUiSettingsScreenPreview() {
|
||||
ThemeComparisonRow {
|
||||
ProfileUiSettingsScreen(
|
||||
mockAccountViewModel(),
|
||||
EmptyNav(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ProfileUiSettingsScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBarWithBackButton(stringRes(id = R.string.profile_ui_settings), nav)
|
||||
},
|
||||
) { padding ->
|
||||
Column(Modifier.padding(padding)) {
|
||||
ProfileUiSettingsContent(accountViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ProfileUiSettingsContent(accountViewModel: AccountViewModel) {
|
||||
val ui = accountViewModel.settings.uiSettingsFlow
|
||||
|
||||
val showBadges by ui.showProfileBadges.collectAsStateWithLifecycle()
|
||||
val showAppRecommendations by ui.showProfileAppRecommendations.collectAsStateWithLifecycle()
|
||||
val showZapReceived by ui.showProfileZapReceivedFeed.collectAsStateWithLifecycle()
|
||||
val showFollowers by ui.showProfileFollowersFeed.collectAsStateWithLifecycle()
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = stringRes(R.string.profile_ui_settings_description),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = Color.Gray,
|
||||
modifier = Modifier.padding(bottom = 16.dp, start = Size20dp, end = Size20dp),
|
||||
)
|
||||
|
||||
ProfileUiSwitchRow(
|
||||
title = stringRes(R.string.profile_ui_setting_badges),
|
||||
checked = showBadges,
|
||||
onCheckedChange = { ui.showProfileBadges.tryEmit(it) },
|
||||
)
|
||||
HorizontalDivider(modifier = Modifier.padding(horizontal = Size20dp))
|
||||
|
||||
ProfileUiSwitchRow(
|
||||
title = stringRes(R.string.profile_ui_setting_app_recommendations),
|
||||
checked = showAppRecommendations,
|
||||
onCheckedChange = { ui.showProfileAppRecommendations.tryEmit(it) },
|
||||
)
|
||||
HorizontalDivider(modifier = Modifier.padding(horizontal = Size20dp))
|
||||
|
||||
ProfileUiSwitchRow(
|
||||
title = stringRes(R.string.profile_ui_setting_zap_received_feed),
|
||||
checked = showZapReceived,
|
||||
onCheckedChange = { ui.showProfileZapReceivedFeed.tryEmit(it) },
|
||||
)
|
||||
HorizontalDivider(modifier = Modifier.padding(horizontal = Size20dp))
|
||||
|
||||
ProfileUiSwitchRow(
|
||||
title = stringRes(R.string.profile_ui_setting_followers_feed),
|
||||
checked = showFollowers,
|
||||
onCheckedChange = { ui.showProfileFollowersFeed.tryEmit(it) },
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProfileUiSwitchRow(
|
||||
title: String,
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 12.dp, horizontal = Size20dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Switch(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1881,6 +1881,12 @@
|
||||
<string name="home_tabs_settings">Home Tabs</string>
|
||||
<string name="home_tabs_settings_description">Pick which tabs appear on the Home screen. When only one tab is active the tab bar is hidden.</string>
|
||||
<string name="home_tab_everything">Everything</string>
|
||||
<string name="profile_ui_settings">Profile UI</string>
|
||||
<string name="profile_ui_settings_description">Pick which sections and feeds appear on user profile screens. All options are enabled by default.</string>
|
||||
<string name="profile_ui_setting_badges">Profile Badges</string>
|
||||
<string name="profile_ui_setting_app_recommendations">App Recommendations</string>
|
||||
<string name="profile_ui_setting_zap_received_feed">Zap Received Feed</string>
|
||||
<string name="profile_ui_setting_followers_feed">Followers Feed</string>
|
||||
<string name="reactions_settings">Reaction Row</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>
|
||||
|
||||
Reference in New Issue
Block a user