feat: show pinned notes at top of Notes tab instead of separate tab
Remove the dedicated Pinned Notes tab from the profile and display pinned notes at the beginning of the Notes tab with a pin icon marker. https://claude.ai/code/session_01ChamaudsEwwQ8mNtqnx4ze
This commit is contained in:
+5
-11
@@ -87,8 +87,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.mutual.TabMutualCon
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.mutual.dal.UserProfileMutualFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads.TabNotesNewThreads
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads.dal.UserProfileNewThreadsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.pinnedNotes.PinnedNotesTabHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.pinnedNotes.TabPinnedNotes
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.pinnedNotes.dal.UserProfilePinnedNotesFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.relays.RelaysTabHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.relays.TabRelays
|
||||
@@ -408,7 +406,7 @@ private fun RenderScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val pagerState = rememberPagerState { 12 }
|
||||
val pagerState = rememberPagerState { 11 }
|
||||
|
||||
Column {
|
||||
ProfileHeader(baseUser, appRecommendations, externalIdentities, nav, accountViewModel)
|
||||
@@ -430,7 +428,6 @@ private fun RenderScreen(
|
||||
followersFeedViewModel,
|
||||
zapFeedViewModel,
|
||||
bookmarksFeedViewModel,
|
||||
pinnedNotesFeedViewModel,
|
||||
galleryFeedViewModel,
|
||||
reportsFeedViewModel,
|
||||
accountViewModel,
|
||||
@@ -485,7 +482,7 @@ private fun CreateAndRenderPages(
|
||||
)
|
||||
|
||||
when (page) {
|
||||
0 -> TabNotesNewThreads(threadsViewModel, accountViewModel, nav)
|
||||
0 -> TabNotesNewThreads(threadsViewModel, pinnedNotesFeedViewModel, accountViewModel, nav)
|
||||
1 -> TabNotesConversations(repliesViewModel, accountViewModel, nav)
|
||||
2 -> TabMutualConversations(mutualViewModel, accountViewModel, nav)
|
||||
3 -> TabGallery(galleryFeedViewModel, accountViewModel, nav)
|
||||
@@ -493,10 +490,9 @@ private fun CreateAndRenderPages(
|
||||
5 -> TabFollowers(followersFeedViewModel, accountViewModel, nav)
|
||||
6 -> TabReceivedZaps(baseUser, zapFeedViewModel, accountViewModel, nav)
|
||||
7 -> TabBookmarks(bookmarksFeedViewModel, accountViewModel, nav)
|
||||
8 -> TabPinnedNotes(pinnedNotesFeedViewModel, accountViewModel, nav)
|
||||
9 -> TabFollowedTags(baseUser, accountViewModel, nav)
|
||||
10 -> TabReports(baseUser, reportsFeedViewModel, accountViewModel, nav)
|
||||
11 -> TabRelays(baseUser, accountViewModel, nav)
|
||||
8 -> TabFollowedTags(baseUser, accountViewModel, nav)
|
||||
9 -> TabReports(baseUser, reportsFeedViewModel, accountViewModel, nav)
|
||||
10 -> TabRelays(baseUser, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,7 +522,6 @@ private fun CreateAndRenderTabs(
|
||||
followersFeedViewModel: UserProfileFollowersUserFeedViewModel,
|
||||
zapFeedViewModel: UserProfileZapsViewModel,
|
||||
bookmarksFeedViewModel: UserProfileBookmarksFeedViewModel,
|
||||
pinnedNotesFeedViewModel: UserProfilePinnedNotesFeedViewModel,
|
||||
galleryFeedViewModel: UserProfileGalleryFeedViewModel,
|
||||
reportsFeedViewModel: UserProfileReportFeedViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
@@ -543,7 +538,6 @@ private fun CreateAndRenderTabs(
|
||||
{ FollowersTabHeader(baseUser, followersFeedViewModel, accountViewModel) },
|
||||
{ ZapTabHeader(zapFeedViewModel, accountViewModel) },
|
||||
{ BookmarkTabHeader(baseUser, accountViewModel) },
|
||||
{ PinnedNotesTabHeader(baseUser, accountViewModel) },
|
||||
{ FollowedTagsTabHeader(baseUser, accountViewModel) },
|
||||
{ ReportsTabHeader(baseUser, reportsFeedViewModel, accountViewModel) },
|
||||
{ RelaysTabHeader(baseUser, accountViewModel) },
|
||||
|
||||
+182
-7
@@ -20,28 +20,203 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.PushPin
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
|
||||
import com.vitorpamplona.amethyst.ui.feeds.FeedEmpty
|
||||
import com.vitorpamplona.amethyst.ui.feeds.FeedError
|
||||
import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed
|
||||
import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.RefresheableFeedView
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads.dal.UserProfileNewThreadsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.pinnedNotes.dal.UserProfilePinnedNotesFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
|
||||
@Composable
|
||||
fun TabNotesNewThreads(
|
||||
feedViewModel: UserProfileNewThreadsFeedViewModel,
|
||||
pinnedNotesFeedViewModel: UserProfilePinnedNotesFeedViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
LaunchedEffect(Unit) { pinnedNotesFeedViewModel.invalidateData() }
|
||||
|
||||
Column(Modifier.fillMaxHeight()) {
|
||||
RefresheableFeedView(
|
||||
feedViewModel,
|
||||
null,
|
||||
enablePullRefresh = false,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
RefresheableBox(feedViewModel, enablePullRefresh = false) {
|
||||
val listState =
|
||||
androidx.compose.foundation.lazy
|
||||
.rememberLazyListState()
|
||||
|
||||
WatchScrollToTop(feedViewModel.feedState, listState)
|
||||
|
||||
val feedState by feedViewModel.feedState.feedContent.collectAsStateWithLifecycle()
|
||||
val pinnedFeedState by pinnedNotesFeedViewModel.feedState.feedContent.collectAsStateWithLifecycle()
|
||||
|
||||
when (val state = feedState) {
|
||||
is FeedState.Empty -> {
|
||||
val pinnedLoaded = pinnedFeedState as? FeedState.Loaded
|
||||
if (pinnedLoaded != null) {
|
||||
val pinnedItems by pinnedLoaded.feed.collectAsStateWithLifecycle()
|
||||
if (pinnedItems.list.isNotEmpty()) {
|
||||
FeedLoadedWithPinnedNotes(
|
||||
pinnedFeedState = pinnedLoaded,
|
||||
loaded = null,
|
||||
listState = listState,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
} else {
|
||||
FeedEmpty { feedViewModel.invalidateData() }
|
||||
}
|
||||
} else {
|
||||
FeedEmpty { feedViewModel.invalidateData() }
|
||||
}
|
||||
}
|
||||
|
||||
is FeedState.FeedError -> {
|
||||
FeedError(state.errorMessage) { feedViewModel.invalidateData() }
|
||||
}
|
||||
|
||||
is FeedState.Loaded -> {
|
||||
FeedLoadedWithPinnedNotes(
|
||||
pinnedFeedState = pinnedFeedState as? FeedState.Loaded,
|
||||
loaded = state,
|
||||
listState = listState,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
is FeedState.Loading -> {
|
||||
LoadingFeed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun FeedLoadedWithPinnedNotes(
|
||||
pinnedFeedState: FeedState.Loaded?,
|
||||
loaded: FeedState.Loaded?,
|
||||
listState: LazyListState,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val pinnedItems =
|
||||
pinnedFeedState?.let {
|
||||
val state by it.feed.collectAsStateWithLifecycle()
|
||||
state
|
||||
}
|
||||
|
||||
val feedItems =
|
||||
loaded?.let {
|
||||
val state by it.feed.collectAsStateWithLifecycle()
|
||||
state
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
contentPadding = FeedPadding,
|
||||
state = listState,
|
||||
) {
|
||||
if (pinnedItems != null && pinnedItems.list.isNotEmpty()) {
|
||||
items(
|
||||
pinnedItems.list,
|
||||
key = { "pinned-${it.idHex}" },
|
||||
contentType = { it.event?.kind ?: -1 },
|
||||
) { item ->
|
||||
Column(Modifier.fillMaxWidth().animateItem()) {
|
||||
PinnedNoteLabel()
|
||||
NoteCompose(
|
||||
item,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
routeForLastRead = null,
|
||||
isBoostedNote = false,
|
||||
isHiddenFeed = pinnedItems.showHidden,
|
||||
quotesLeft = 3,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
}
|
||||
}
|
||||
|
||||
if (feedItems != null) {
|
||||
itemsIndexed(
|
||||
feedItems.list,
|
||||
key = { _, item -> item.idHex },
|
||||
contentType = { _, item -> item.event?.kind ?: -1 },
|
||||
) { _, item ->
|
||||
Row(Modifier.fillMaxWidth().animateItem()) {
|
||||
NoteCompose(
|
||||
item,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
routeForLastRead = null,
|
||||
isBoostedNote = false,
|
||||
isHiddenFeed = feedItems.showHidden,
|
||||
quotesLeft = 3,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PinnedNoteLabel() {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 0.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.PushPin,
|
||||
contentDescription = stringRes(R.string.pinned_notes),
|
||||
modifier = Modifier.size(14.dp),
|
||||
tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(
|
||||
text = stringRes(R.string.pinned_notes),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user