diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettings.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettings.kt index 17f756030..79a9af579 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettings.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettings.kt @@ -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( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettingsFlow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettingsFlow.kt index a6a00acbb..824142495 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettingsFlow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettingsFlow.kt @@ -48,6 +48,10 @@ class UiSettingsFlow( val showHomeNewThreadsTab: MutableStateFlow = MutableStateFlow(true), val showHomeConversationsTab: MutableStateFlow = MutableStateFlow(true), val showHomeEverythingTab: MutableStateFlow = MutableStateFlow(false), + val showProfileBadges: MutableStateFlow = MutableStateFlow(true), + val showProfileAppRecommendations: MutableStateFlow = MutableStateFlow(true), + val showProfileZapReceivedFeed: MutableStateFlow = MutableStateFlow(true), + val showProfileFollowersFeed: MutableStateFlow = MutableStateFlow(true), ) { val listOfFlows: List> = listOf>( @@ -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), ) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/preferences/UISharedPreferences.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/preferences/UISharedPreferences.kt index 36dbe9286..ee4561b00 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/preferences/UISharedPreferences.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/preferences/UISharedPreferences.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt index 44e61bcfd..68f3f630a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt @@ -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 { ReactionsSettingsScreen(accountViewModel, nav) } composableFromEnd { BottomBarSettingsScreen(accountViewModel, nav) } composableFromEnd { HomeTabsSettingsScreen(accountViewModel, nav) } + composableFromEnd { ProfileUiSettingsScreen(accountViewModel, nav) } composableFromEnd { VideoPlayerSettingsScreen(accountViewModel, nav) } composableFromEnd { CallSettingsScreen(accountViewModel, nav) } composableFromEnd { ImportFollowListSelectUserScreen(accountViewModel, nav) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/routes/Routes.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/routes/Routes.kt index 7607abd2f..e8f5d506f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/routes/Routes.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/routes/Routes.kt @@ -225,6 +225,8 @@ sealed class Route { @Serializable object HomeTabsSettings : Route() + @Serializable object ProfileUiSettings : Route() + @Serializable object VideoPlayerSettings : Route() @Serializable object CallSettings : Route() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/ProfileScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/ProfileScreen.kt index 03229139a..3d19ac70d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/ProfileScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/ProfileScreen.kt @@ -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, 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) + } + }, ) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DrawAdditionalInfo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DrawAdditionalInfo.kt index ea12f1bf0..c9726e524 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DrawAdditionalInfo.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DrawAdditionalInfo.kt @@ -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) + } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/AllSettingsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/AllSettingsScreen.kt index a24133ff4..4c3e8160c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/AllSettingsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/AllSettingsScreen.kt @@ -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) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/ProfileUiSettingsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/ProfileUiSettingsScreen.kt new file mode 100644 index 000000000..5b6ffb4fe --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/ProfileUiSettingsScreen.kt @@ -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, + ) + } +} diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index b31283c26..d9a7ed94e 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -1881,6 +1881,12 @@ Home Tabs Pick which tabs appear on the Home screen. When only one tab is active the tab bar is hidden. Everything + Profile UI + Pick which sections and feeds appear on user profile screens. All options are enabled by default. + Profile Badges + App Recommendations + Zap Received Feed + Followers Feed Reaction Row Configure which reaction buttons are shown, their order, and whether to display counters. Enabled