alternative attempt..

This commit is contained in:
Believethehype
2024-06-28 19:14:36 +02:00
parent e073e58241
commit a42762de53
4 changed files with 239 additions and 5 deletions
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2024 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.dal
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
class UserProfileGalleryFeedFilter(val user: User, val account: Account) : FeedFilter<Note>() {
override fun feedKey(): String {
return account.userProfile().pubkeyHex + "-" + user.pubkeyHex
}
override fun feed(): List<Note> {
val notes =
user.latestGalleryList
?.taggedEvents()
?.mapNotNull { LocalCache.checkGetOrCreateNote(it) }
?.toSet()
?: emptySet()
val addresses =
user.latestGalleryList
?.taggedAddresses()
?.map { LocalCache.getOrCreateAddressableNote(it) }
?.toSet()
?: emptySet()
return (notes + addresses)
.filter { account.isAcceptable(it) }
.sortedWith(DefaultFeedOrder)
}
}
@@ -57,6 +57,7 @@ import com.vitorpamplona.amethyst.ui.dal.ThreadFeedFilter
import com.vitorpamplona.amethyst.ui.dal.UserProfileAppRecommendationsFeedFilter import com.vitorpamplona.amethyst.ui.dal.UserProfileAppRecommendationsFeedFilter
import com.vitorpamplona.amethyst.ui.dal.UserProfileBookmarksFeedFilter import com.vitorpamplona.amethyst.ui.dal.UserProfileBookmarksFeedFilter
import com.vitorpamplona.amethyst.ui.dal.UserProfileConversationsFeedFilter import com.vitorpamplona.amethyst.ui.dal.UserProfileConversationsFeedFilter
import com.vitorpamplona.amethyst.ui.dal.UserProfileGalleryFeedFilter
import com.vitorpamplona.amethyst.ui.dal.UserProfileNewThreadFeedFilter import com.vitorpamplona.amethyst.ui.dal.UserProfileNewThreadFeedFilter
import com.vitorpamplona.amethyst.ui.dal.UserProfileReportsFeedFilter import com.vitorpamplona.amethyst.ui.dal.UserProfileReportsFeedFilter
import com.vitorpamplona.amethyst.ui.dal.VideoFeedFilter import com.vitorpamplona.amethyst.ui.dal.VideoFeedFilter
@@ -214,6 +215,16 @@ class NostrUserProfileReportFeedViewModel(val user: User) :
} }
} }
class NostrUserProfileGalleryFeedViewModel(val user: User, val account: Account) :
FeedViewModel(UserProfileGalleryFeedFilter(user, account)) {
class Factory(val user: User, val account: Account) : ViewModelProvider.Factory {
override fun <NostrUserProfileGalleryFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileGalleryFeedViewModel>): NostrUserProfileGalleryFeedViewModel {
return NostrUserProfileGalleryFeedViewModel(user, account)
as NostrUserProfileGalleryFeedViewModel
}
}
}
class NostrUserProfileBookmarksFeedViewModel(val user: User, val account: Account) : class NostrUserProfileBookmarksFeedViewModel(val user: User, val account: Account) :
FeedViewModel(UserProfileBookmarksFeedFilter(user, account)) { FeedViewModel(UserProfileBookmarksFeedFilter(user, account)) {
class Factory(val user: User, val account: Account) : ViewModelProvider.Factory { class Factory(val user: User, val account: Account) : ViewModelProvider.Factory {
@@ -20,6 +20,8 @@
*/ */
package com.vitorpamplona.amethyst.ui.screen.loggedIn package com.vitorpamplona.amethyst.ui.screen.loggedIn
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -30,6 +32,10 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -37,6 +43,7 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment.Companion.BottomStart import androidx.compose.ui.Alignment.Companion.BottomStart
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
@@ -46,28 +53,149 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.map import androidx.lifecycle.map
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
import com.vitorpamplona.amethyst.ui.note.CheckHiddenFeedWatchBlockAndReport import com.vitorpamplona.amethyst.ui.note.CheckHiddenFeedWatchBlockAndReport
import com.vitorpamplona.amethyst.ui.note.ClickableNote import com.vitorpamplona.amethyst.ui.note.ClickableNote
import com.vitorpamplona.amethyst.ui.note.LongPressToQuickAction import com.vitorpamplona.amethyst.ui.note.LongPressToQuickAction
import com.vitorpamplona.amethyst.ui.note.NormalChannelCard
import com.vitorpamplona.amethyst.ui.note.WatchAuthor import com.vitorpamplona.amethyst.ui.note.WatchAuthor
import com.vitorpamplona.amethyst.ui.note.WatchNoteEvent import com.vitorpamplona.amethyst.ui.note.WatchNoteEvent
import com.vitorpamplona.amethyst.ui.note.calculateBackgroundColor import com.vitorpamplona.amethyst.ui.note.calculateBackgroundColor
import com.vitorpamplona.amethyst.ui.note.elements.BannerImage import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
import com.vitorpamplona.amethyst.ui.screen.FeedEmpty
import com.vitorpamplona.amethyst.ui.screen.FeedError
import com.vitorpamplona.amethyst.ui.screen.FeedState
import com.vitorpamplona.amethyst.ui.screen.FeedViewModel
import com.vitorpamplona.amethyst.ui.screen.LoadingFeed
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.amethyst.ui.theme.HalfPadding import com.vitorpamplona.amethyst.ui.theme.HalfPadding
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.Size5dp import com.vitorpamplona.amethyst.ui.theme.Size5dp
import com.vitorpamplona.quartz.events.GalleryListEvent import com.vitorpamplona.quartz.events.GalleryListEvent
@Composable
private fun RenderGalleryFeed(
viewModel: FeedViewModel,
routeForLastRead: String?,
forceEventKind: Int?,
listState: LazyListState,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val feedState by viewModel.feedContent.collectAsStateWithLifecycle()
CrossfadeIfEnabled(
targetState = feedState,
animationSpec = tween(durationMillis = 100),
label = "RenderDiscoverFeed",
accountViewModel = accountViewModel,
) { state ->
when (state) {
is FeedState.Empty -> {
FeedEmpty { viewModel.invalidateData() }
}
is FeedState.FeedError -> {
FeedError(state.errorMessage) { viewModel.invalidateData() }
}
is FeedState.Loaded -> {
GalleryFeedLoaded(
state,
routeForLastRead,
listState,
forceEventKind,
accountViewModel,
nav,
)
}
is FeedState.Loading -> {
LoadingFeed()
}
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun GalleryFeedLoaded(
state: FeedState.Loaded,
routeForLastRead: String?,
listState: LazyListState,
forceEventKind: Int?,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
LazyColumn(
contentPadding = FeedPadding,
state = listState,
) {
itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { _, item ->
val defaultModifier = remember { Modifier.fillMaxWidth().animateItemPlacement() }
Row(defaultModifier) {
GalleryCardCompose(
baseNote = item,
routeForLastRead = routeForLastRead,
modifier = Modifier.fillMaxWidth(),
forceEventKind = forceEventKind,
accountViewModel = accountViewModel,
nav = nav,
)
}
HorizontalDivider(
thickness = DividerThickness,
)
}
}
}
@Composable
fun GalleryCardCompose(
baseNote: Note,
routeForLastRead: String? = null,
modifier: Modifier = Modifier,
parentBackgroundColor: MutableState<Color>? = null,
forceEventKind: Int?,
isHiddenFeed: Boolean = false,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
WatchNoteEvent(baseNote = baseNote, accountViewModel = accountViewModel) {
if (forceEventKind == null || baseNote.event?.kind() == forceEventKind) {
CheckHiddenFeedWatchBlockAndReport(
note = baseNote,
modifier = modifier,
ignoreAllBlocksAndReports = isHiddenFeed,
showHiddenWarning = false,
accountViewModel = accountViewModel,
nav = nav,
) { canPreview ->
NormalChannelCard(
baseNote = baseNote,
routeForLastRead = routeForLastRead,
modifier = modifier,
parentBackgroundColor = parentBackgroundColor,
accountViewModel = accountViewModel,
nav = nav,
)
}
}
}
}
// TODO This is to large parts from the ChannelCardCompose // TODO This is to large parts from the ChannelCardCompose
// Why does it not be in a grid, like the marketplace
/*@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun ProfileGallery( fun ProfileGallery(
baseNotes: List<GalleryThumb>, baseNote: List<GalleryThumb>,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
parentBackgroundColor: MutableState<Color>? = null, parentBackgroundColor: MutableState<Color>? = null,
isHiddenFeed: Boolean = false, isHiddenFeed: Boolean = false,
@@ -103,7 +231,7 @@ fun ProfileGallery(
} }
} }
} }
*/
@Composable @Composable
fun GalleryCard( fun GalleryCard(
baseNote: Note, baseNote: Note,
@@ -148,6 +148,7 @@ import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileBookmarksFeedViewMod
import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileConversationsFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileConversationsFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileFollowersUserFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileFollowersUserFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileFollowsUserFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileFollowsUserFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileGalleryFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileNewThreadsFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileNewThreadsFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileReportFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileReportFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileZapsFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileZapsFeedViewModel
@@ -294,6 +295,16 @@ fun PrepareViewModels(
), ),
) )
val galleryFeedViewModel: NostrUserProfileGalleryFeedViewModel =
viewModel(
key = baseUser.pubkeyHex + "UserGalleryFeedViewModel",
factory =
NostrUserProfileGalleryFeedViewModel.Factory(
baseUser,
accountViewModel.account,
),
)
val reportsFeedViewModel: NostrUserProfileReportFeedViewModel = val reportsFeedViewModel: NostrUserProfileReportFeedViewModel =
viewModel( viewModel(
key = baseUser.pubkeyHex + "UserProfileReportFeedViewModel", key = baseUser.pubkeyHex + "UserProfileReportFeedViewModel",
@@ -312,6 +323,7 @@ fun PrepareViewModels(
appRecommendations, appRecommendations,
zapFeedViewModel, zapFeedViewModel,
bookmarksFeedViewModel, bookmarksFeedViewModel,
galleryFeedViewModel,
reportsFeedViewModel, reportsFeedViewModel,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
@@ -328,6 +340,7 @@ fun ProfileScreen(
appRecommendations: NostrUserAppRecommendationsFeedViewModel, appRecommendations: NostrUserAppRecommendationsFeedViewModel,
zapFeedViewModel: NostrUserProfileZapsFeedViewModel, zapFeedViewModel: NostrUserProfileZapsFeedViewModel,
bookmarksFeedViewModel: NostrUserProfileBookmarksFeedViewModel, bookmarksFeedViewModel: NostrUserProfileBookmarksFeedViewModel,
galleryFeedViewModel: NostrUserProfileGalleryFeedViewModel,
reportsFeedViewModel: NostrUserProfileReportFeedViewModel, reportsFeedViewModel: NostrUserProfileReportFeedViewModel,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
@@ -372,6 +385,7 @@ fun ProfileScreen(
followersFeedViewModel, followersFeedViewModel,
zapFeedViewModel, zapFeedViewModel,
bookmarksFeedViewModel, bookmarksFeedViewModel,
galleryFeedViewModel,
reportsFeedViewModel, reportsFeedViewModel,
accountViewModel, accountViewModel,
nav, nav,
@@ -388,6 +402,7 @@ private fun RenderSurface(
followersFeedViewModel: NostrUserProfileFollowersUserFeedViewModel, followersFeedViewModel: NostrUserProfileFollowersUserFeedViewModel,
zapFeedViewModel: NostrUserProfileZapsFeedViewModel, zapFeedViewModel: NostrUserProfileZapsFeedViewModel,
bookmarksFeedViewModel: NostrUserProfileBookmarksFeedViewModel, bookmarksFeedViewModel: NostrUserProfileBookmarksFeedViewModel,
galleryFeedViewModel: NostrUserProfileGalleryFeedViewModel,
reportsFeedViewModel: NostrUserProfileReportFeedViewModel, reportsFeedViewModel: NostrUserProfileReportFeedViewModel,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
@@ -448,6 +463,7 @@ private fun RenderSurface(
followersFeedViewModel, followersFeedViewModel,
zapFeedViewModel, zapFeedViewModel,
bookmarksFeedViewModel, bookmarksFeedViewModel,
galleryFeedViewModel,
reportsFeedViewModel, reportsFeedViewModel,
accountViewModel, accountViewModel,
nav, nav,
@@ -470,6 +486,7 @@ private fun RenderScreen(
followersFeedViewModel: NostrUserProfileFollowersUserFeedViewModel, followersFeedViewModel: NostrUserProfileFollowersUserFeedViewModel,
zapFeedViewModel: NostrUserProfileZapsFeedViewModel, zapFeedViewModel: NostrUserProfileZapsFeedViewModel,
bookmarksFeedViewModel: NostrUserProfileBookmarksFeedViewModel, bookmarksFeedViewModel: NostrUserProfileBookmarksFeedViewModel,
galleryFeedViewModel: NostrUserProfileGalleryFeedViewModel,
reportsFeedViewModel: NostrUserProfileReportFeedViewModel, reportsFeedViewModel: NostrUserProfileReportFeedViewModel,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
@@ -501,6 +518,7 @@ private fun RenderScreen(
followersFeedViewModel, followersFeedViewModel,
zapFeedViewModel, zapFeedViewModel,
bookmarksFeedViewModel, bookmarksFeedViewModel,
galleryFeedViewModel,
reportsFeedViewModel, reportsFeedViewModel,
accountViewModel, accountViewModel,
nav, nav,
@@ -519,6 +537,7 @@ private fun CreateAndRenderPages(
followersFeedViewModel: NostrUserProfileFollowersUserFeedViewModel, followersFeedViewModel: NostrUserProfileFollowersUserFeedViewModel,
zapFeedViewModel: NostrUserProfileZapsFeedViewModel, zapFeedViewModel: NostrUserProfileZapsFeedViewModel,
bookmarksFeedViewModel: NostrUserProfileBookmarksFeedViewModel, bookmarksFeedViewModel: NostrUserProfileBookmarksFeedViewModel,
galleryFeedViewModel: NostrUserProfileGalleryFeedViewModel,
reportsFeedViewModel: NostrUserProfileReportFeedViewModel, reportsFeedViewModel: NostrUserProfileReportFeedViewModel,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
@@ -533,7 +552,7 @@ private fun CreateAndRenderPages(
when (page) { when (page) {
0 -> TabNotesNewThreads(threadsViewModel, accountViewModel, nav) 0 -> TabNotesNewThreads(threadsViewModel, accountViewModel, nav)
1 -> TabNotesConversations(repliesViewModel, accountViewModel, nav) 1 -> TabNotesConversations(repliesViewModel, accountViewModel, nav)
2 -> Gallery(baseUser, followsFeedViewModel, accountViewModel, nav) 2 -> TabGallery(galleryFeedViewModel, accountViewModel, nav)
3 -> TabFollows(baseUser, followsFeedViewModel, accountViewModel, nav) 3 -> TabFollows(baseUser, followsFeedViewModel, accountViewModel, nav)
4 -> TabFollowers(baseUser, followersFeedViewModel, accountViewModel, nav) 4 -> TabFollowers(baseUser, followersFeedViewModel, accountViewModel, nav)
5 -> TabReceivedZaps(baseUser, zapFeedViewModel, accountViewModel, nav) 5 -> TabReceivedZaps(baseUser, zapFeedViewModel, accountViewModel, nav)
@@ -1537,7 +1556,31 @@ fun TabNotesConversations(
} }
} }
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun TabGallery(
feedViewModel: NostrUserProfileGalleryFeedViewModel,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
LaunchedEffect(Unit) { feedViewModel.invalidateData() }
Column(Modifier.fillMaxHeight()) {
Column(
modifier = Modifier.padding(vertical = 0.dp),
) {
RefresheableFeedView(
feedViewModel,
null,
enablePullRefresh = false,
accountViewModel = accountViewModel,
nav = nav,
)
}
}
}
/*@Composable
fun Gallery( fun Gallery(
baseUser: User, baseUser: User,
feedViewModel: UserFeedViewModel, feedViewModel: UserFeedViewModel,
@@ -1579,7 +1622,7 @@ fun Gallery(
} }
} }
} }
} } */
@Composable @Composable
fun TabFollowedTags( fun TabFollowedTags(