Merge pull request #1961 from vitorpamplona/claude/pinned-notes-in-tab-8y3TR
Integrate pinned notes into new threads tab
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.mutual.dal.UserProfileMutualFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads.TabNotesNewThreads
|
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.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.pinnedNotes.dal.UserProfilePinnedNotesFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.relays.RelaysTabHeader
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.relays.RelaysTabHeader
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.relays.TabRelays
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.relays.TabRelays
|
||||||
@@ -408,7 +406,7 @@ private fun RenderScreen(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val pagerState = rememberPagerState { 12 }
|
val pagerState = rememberPagerState { 11 }
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
ProfileHeader(baseUser, appRecommendations, externalIdentities, nav, accountViewModel)
|
ProfileHeader(baseUser, appRecommendations, externalIdentities, nav, accountViewModel)
|
||||||
@@ -430,7 +428,6 @@ private fun RenderScreen(
|
|||||||
followersFeedViewModel,
|
followersFeedViewModel,
|
||||||
zapFeedViewModel,
|
zapFeedViewModel,
|
||||||
bookmarksFeedViewModel,
|
bookmarksFeedViewModel,
|
||||||
pinnedNotesFeedViewModel,
|
|
||||||
galleryFeedViewModel,
|
galleryFeedViewModel,
|
||||||
reportsFeedViewModel,
|
reportsFeedViewModel,
|
||||||
accountViewModel,
|
accountViewModel,
|
||||||
@@ -485,7 +482,7 @@ private fun CreateAndRenderPages(
|
|||||||
)
|
)
|
||||||
|
|
||||||
when (page) {
|
when (page) {
|
||||||
0 -> TabNotesNewThreads(threadsViewModel, accountViewModel, nav)
|
0 -> TabNotesNewThreads(threadsViewModel, pinnedNotesFeedViewModel, accountViewModel, nav)
|
||||||
1 -> TabNotesConversations(repliesViewModel, accountViewModel, nav)
|
1 -> TabNotesConversations(repliesViewModel, accountViewModel, nav)
|
||||||
2 -> TabMutualConversations(mutualViewModel, accountViewModel, nav)
|
2 -> TabMutualConversations(mutualViewModel, accountViewModel, nav)
|
||||||
3 -> TabGallery(galleryFeedViewModel, accountViewModel, nav)
|
3 -> TabGallery(galleryFeedViewModel, accountViewModel, nav)
|
||||||
@@ -493,10 +490,9 @@ private fun CreateAndRenderPages(
|
|||||||
5 -> TabFollowers(followersFeedViewModel, accountViewModel, nav)
|
5 -> TabFollowers(followersFeedViewModel, accountViewModel, nav)
|
||||||
6 -> TabReceivedZaps(baseUser, zapFeedViewModel, accountViewModel, nav)
|
6 -> TabReceivedZaps(baseUser, zapFeedViewModel, accountViewModel, nav)
|
||||||
7 -> TabBookmarks(bookmarksFeedViewModel, accountViewModel, nav)
|
7 -> TabBookmarks(bookmarksFeedViewModel, accountViewModel, nav)
|
||||||
8 -> TabPinnedNotes(pinnedNotesFeedViewModel, accountViewModel, nav)
|
8 -> TabFollowedTags(baseUser, accountViewModel, nav)
|
||||||
9 -> TabFollowedTags(baseUser, accountViewModel, nav)
|
9 -> TabReports(baseUser, reportsFeedViewModel, accountViewModel, nav)
|
||||||
10 -> TabReports(baseUser, reportsFeedViewModel, accountViewModel, nav)
|
10 -> TabRelays(baseUser, accountViewModel, nav)
|
||||||
11 -> TabRelays(baseUser, accountViewModel, nav)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,7 +522,6 @@ private fun CreateAndRenderTabs(
|
|||||||
followersFeedViewModel: UserProfileFollowersUserFeedViewModel,
|
followersFeedViewModel: UserProfileFollowersUserFeedViewModel,
|
||||||
zapFeedViewModel: UserProfileZapsViewModel,
|
zapFeedViewModel: UserProfileZapsViewModel,
|
||||||
bookmarksFeedViewModel: UserProfileBookmarksFeedViewModel,
|
bookmarksFeedViewModel: UserProfileBookmarksFeedViewModel,
|
||||||
pinnedNotesFeedViewModel: UserProfilePinnedNotesFeedViewModel,
|
|
||||||
galleryFeedViewModel: UserProfileGalleryFeedViewModel,
|
galleryFeedViewModel: UserProfileGalleryFeedViewModel,
|
||||||
reportsFeedViewModel: UserProfileReportFeedViewModel,
|
reportsFeedViewModel: UserProfileReportFeedViewModel,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
@@ -543,7 +538,6 @@ private fun CreateAndRenderTabs(
|
|||||||
{ FollowersTabHeader(baseUser, followersFeedViewModel, accountViewModel) },
|
{ FollowersTabHeader(baseUser, followersFeedViewModel, accountViewModel) },
|
||||||
{ ZapTabHeader(zapFeedViewModel, accountViewModel) },
|
{ ZapTabHeader(zapFeedViewModel, accountViewModel) },
|
||||||
{ BookmarkTabHeader(baseUser, accountViewModel) },
|
{ BookmarkTabHeader(baseUser, accountViewModel) },
|
||||||
{ PinnedNotesTabHeader(baseUser, accountViewModel) },
|
|
||||||
{ FollowedTagsTabHeader(baseUser, accountViewModel) },
|
{ FollowedTagsTabHeader(baseUser, accountViewModel) },
|
||||||
{ ReportsTabHeader(baseUser, reportsFeedViewModel, accountViewModel) },
|
{ ReportsTabHeader(baseUser, reportsFeedViewModel, accountViewModel) },
|
||||||
{ RelaysTabHeader(baseUser, accountViewModel) },
|
{ RelaysTabHeader(baseUser, accountViewModel) },
|
||||||
|
|||||||
+182
-7
@@ -20,28 +20,203 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads
|
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.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
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.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.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.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.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.newthreads.dal.UserProfileNewThreadsFeedViewModel
|
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
|
@Composable
|
||||||
fun TabNotesNewThreads(
|
fun TabNotesNewThreads(
|
||||||
feedViewModel: UserProfileNewThreadsFeedViewModel,
|
feedViewModel: UserProfileNewThreadsFeedViewModel,
|
||||||
|
pinnedNotesFeedViewModel: UserProfilePinnedNotesFeedViewModel,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
|
LaunchedEffect(Unit) { pinnedNotesFeedViewModel.invalidateData() }
|
||||||
|
|
||||||
Column(Modifier.fillMaxHeight()) {
|
Column(Modifier.fillMaxHeight()) {
|
||||||
RefresheableFeedView(
|
RefresheableBox(feedViewModel, enablePullRefresh = false) {
|
||||||
feedViewModel,
|
val listState =
|
||||||
null,
|
androidx.compose.foundation.lazy
|
||||||
enablePullRefresh = false,
|
.rememberLazyListState()
|
||||||
accountViewModel = accountViewModel,
|
|
||||||
nav = nav,
|
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