alternative attempt..

pull/957/head
Believethehype 2024-06-28 19:14:36 +02:00
rodzic e073e58241
commit a42762de53
4 zmienionych plików z 239 dodań i 5 usunięć

Wyświetl plik

@ -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)
}
}

Wyświetl plik

@ -57,6 +57,7 @@ import com.vitorpamplona.amethyst.ui.dal.ThreadFeedFilter
import com.vitorpamplona.amethyst.ui.dal.UserProfileAppRecommendationsFeedFilter
import com.vitorpamplona.amethyst.ui.dal.UserProfileBookmarksFeedFilter
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.UserProfileReportsFeedFilter
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) :
FeedViewModel(UserProfileBookmarksFeedFilter(user, account)) {
class Factory(val user: User, val account: Account) : ViewModelProvider.Factory {

Wyświetl plik

@ -20,6 +20,8 @@
*/
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.layout.Arrangement
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.padding
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.Text
import androidx.compose.runtime.Composable
@ -37,6 +43,7 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment.Companion.BottomStart
import androidx.compose.ui.Modifier
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.map
import coil.compose.AsyncImage
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.note.CheckHiddenFeedWatchBlockAndReport
import com.vitorpamplona.amethyst.ui.note.ClickableNote
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.WatchNoteEvent
import com.vitorpamplona.amethyst.ui.note.calculateBackgroundColor
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.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.Size5dp
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
// Why does it not be in a grid, like the marketplace
/*@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ProfileGallery(
baseNotes: List<GalleryThumb>,
baseNote: List<GalleryThumb>,
modifier: Modifier = Modifier,
parentBackgroundColor: MutableState<Color>? = null,
isHiddenFeed: Boolean = false,
@ -103,7 +231,7 @@ fun ProfileGallery(
}
}
}
*/
@Composable
fun GalleryCard(
baseNote: Note,

Wyświetl plik

@ -148,6 +148,7 @@ import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileBookmarksFeedViewMod
import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileConversationsFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileFollowersUserFeedViewModel
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.NostrUserProfileReportFeedViewModel
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 =
viewModel(
key = baseUser.pubkeyHex + "UserProfileReportFeedViewModel",
@ -312,6 +323,7 @@ fun PrepareViewModels(
appRecommendations,
zapFeedViewModel,
bookmarksFeedViewModel,
galleryFeedViewModel,
reportsFeedViewModel,
accountViewModel = accountViewModel,
nav = nav,
@ -328,6 +340,7 @@ fun ProfileScreen(
appRecommendations: NostrUserAppRecommendationsFeedViewModel,
zapFeedViewModel: NostrUserProfileZapsFeedViewModel,
bookmarksFeedViewModel: NostrUserProfileBookmarksFeedViewModel,
galleryFeedViewModel: NostrUserProfileGalleryFeedViewModel,
reportsFeedViewModel: NostrUserProfileReportFeedViewModel,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
@ -372,6 +385,7 @@ fun ProfileScreen(
followersFeedViewModel,
zapFeedViewModel,
bookmarksFeedViewModel,
galleryFeedViewModel,
reportsFeedViewModel,
accountViewModel,
nav,
@ -388,6 +402,7 @@ private fun RenderSurface(
followersFeedViewModel: NostrUserProfileFollowersUserFeedViewModel,
zapFeedViewModel: NostrUserProfileZapsFeedViewModel,
bookmarksFeedViewModel: NostrUserProfileBookmarksFeedViewModel,
galleryFeedViewModel: NostrUserProfileGalleryFeedViewModel,
reportsFeedViewModel: NostrUserProfileReportFeedViewModel,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
@ -448,6 +463,7 @@ private fun RenderSurface(
followersFeedViewModel,
zapFeedViewModel,
bookmarksFeedViewModel,
galleryFeedViewModel,
reportsFeedViewModel,
accountViewModel,
nav,
@ -470,6 +486,7 @@ private fun RenderScreen(
followersFeedViewModel: NostrUserProfileFollowersUserFeedViewModel,
zapFeedViewModel: NostrUserProfileZapsFeedViewModel,
bookmarksFeedViewModel: NostrUserProfileBookmarksFeedViewModel,
galleryFeedViewModel: NostrUserProfileGalleryFeedViewModel,
reportsFeedViewModel: NostrUserProfileReportFeedViewModel,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
@ -501,6 +518,7 @@ private fun RenderScreen(
followersFeedViewModel,
zapFeedViewModel,
bookmarksFeedViewModel,
galleryFeedViewModel,
reportsFeedViewModel,
accountViewModel,
nav,
@ -519,6 +537,7 @@ private fun CreateAndRenderPages(
followersFeedViewModel: NostrUserProfileFollowersUserFeedViewModel,
zapFeedViewModel: NostrUserProfileZapsFeedViewModel,
bookmarksFeedViewModel: NostrUserProfileBookmarksFeedViewModel,
galleryFeedViewModel: NostrUserProfileGalleryFeedViewModel,
reportsFeedViewModel: NostrUserProfileReportFeedViewModel,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
@ -533,7 +552,7 @@ private fun CreateAndRenderPages(
when (page) {
0 -> TabNotesNewThreads(threadsViewModel, accountViewModel, nav)
1 -> TabNotesConversations(repliesViewModel, accountViewModel, nav)
2 -> Gallery(baseUser, followsFeedViewModel, accountViewModel, nav)
2 -> TabGallery(galleryFeedViewModel, accountViewModel, nav)
3 -> TabFollows(baseUser, followsFeedViewModel, accountViewModel, nav)
4 -> TabFollowers(baseUser, followersFeedViewModel, accountViewModel, nav)
5 -> TabReceivedZaps(baseUser, zapFeedViewModel, accountViewModel, nav)
@ -1537,7 +1556,31 @@ fun TabNotesConversations(
}
}
@OptIn(ExperimentalFoundationApi::class)
@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(
baseUser: User,
feedViewModel: UserFeedViewModel,
@ -1579,7 +1622,7 @@ fun Gallery(
}
}
}
}
} */
@Composable
fun TabFollowedTags(