Merge pull request #609 from barrydeen/fix/profile-notes-feed-pagination
fix(profile): keep notes feed paginating and render inline gallery media
This commit is contained in:
@@ -545,17 +545,25 @@ fun UserProfileScreen(
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
// Auto-load more media when scrolling near the bottom of the grid
|
||||
if (selectedTabId == 3 && mediaItems.isNotEmpty()) {
|
||||
LaunchedEffect(listState) {
|
||||
// Auto-load more when scrolling near the bottom of the notes/replies/media feeds
|
||||
val autoPaginates = selectedTabId == 0 || selectedTabId == 1 ||
|
||||
(selectedTabId == 3 && mediaItems.isNotEmpty())
|
||||
if (autoPaginates) {
|
||||
LaunchedEffect(listState, selectedTabId) {
|
||||
snapshotFlow {
|
||||
val lastVisible = listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
|
||||
val totalItems = listState.layoutInfo.totalItemsCount
|
||||
lastVisible to totalItems
|
||||
}.collect { (lastVisible, totalItems) ->
|
||||
if (totalItems > 0 && lastVisible >= totalItems - 3) {
|
||||
viewModel.loadMoreNotes()
|
||||
viewModel.loadMoreReplies()
|
||||
when (selectedTabId) {
|
||||
0 -> if (notesSortMode == ProfileSortMode.RECENCY) viewModel.loadMoreNotes()
|
||||
1 -> if (repliesSortMode == ProfileSortMode.RECENCY) viewModel.loadMoreReplies()
|
||||
3 -> {
|
||||
viewModel.loadMoreNotes()
|
||||
viewModel.loadMoreReplies()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -854,6 +862,52 @@ fun UserProfileScreen(
|
||||
val rootUserZapPollVote = remember(pollVoteVersion, event.id) {
|
||||
if (event.kind == 6969) eventRepo?.getUserZapPollVote(event.id) else null
|
||||
}
|
||||
if (isGalleryEvent(event)) {
|
||||
GalleryCard(
|
||||
event = event,
|
||||
profile = if (repostPubkey != null) eventRepo?.getProfileData(event.pubkey) else profile,
|
||||
onReply = { onReply(event) },
|
||||
onProfileClick = { onNavigateToProfile?.invoke(event.pubkey) },
|
||||
onNavigateToProfile = onNavigateToProfile,
|
||||
onNoteClick = { onNoteClick(event) },
|
||||
onQuotedNoteClick = onQuotedNoteClick,
|
||||
onReact = { emoji -> onReact(event, emoji) },
|
||||
onRepost = { onRepost(event) },
|
||||
onQuote = { onQuote(event) },
|
||||
userReactionEmojis = userEmojis,
|
||||
hasUserReposted = hasUserReposted,
|
||||
onZap = { zapTargetEvent = event },
|
||||
hasUserZapped = hasUserZapped,
|
||||
likeCount = likeCount,
|
||||
replyCount = replyCount,
|
||||
repostCount = repostCount,
|
||||
zapSats = zapSats,
|
||||
isZapAnimating = event.id in zapAnimatingIds,
|
||||
isZapInProgress = event.id in zapInProgressIds,
|
||||
eventRepo = eventRepo,
|
||||
repostPubkeys = profileRepostPubkeys,
|
||||
reactionDetails = reactionDetails,
|
||||
zapDetails = zapDetails,
|
||||
reactionEmojiUrls = eventReactionEmojiUrls,
|
||||
relayIcons = relayIcons,
|
||||
onNavigateToProfileFromDetails = onNavigateToProfile,
|
||||
onFollowAuthor = { onToggleFollow?.invoke(event.pubkey) },
|
||||
onBlockAuthor = { onBlockUser?.invoke() },
|
||||
isFollowingAuthor = contactRepo.isFollowing(event.pubkey),
|
||||
isOwnEvent = event.pubkey == userPubkey,
|
||||
nip05Repo = nip05Repo,
|
||||
onAddToList = { onAddNoteToList(event.id) },
|
||||
isInList = event.id in listedIds,
|
||||
onPin = { onTogglePin(event.id) },
|
||||
isPinned = event.id in pinnedIds,
|
||||
onDelete = { onDeleteEvent(event.id, event.kind) },
|
||||
resolvedEmojis = resolvedEmojis,
|
||||
unicodeEmojis = unicodeEmojis,
|
||||
onOpenEmojiLibrary = onOpenEmojiLibrary,
|
||||
noteActions = invoiceNoteActions
|
||||
)
|
||||
return@items
|
||||
}
|
||||
PostCard(
|
||||
event = event,
|
||||
profile = if (repostPubkey != null) eventRepo?.getProfileData(event.pubkey) else profile,
|
||||
|
||||
@@ -340,20 +340,27 @@ class UserProfileViewModel(app: Application) : AndroidViewModel(app) {
|
||||
}
|
||||
20, 21, 22 -> {
|
||||
eventRepo.cacheEvent(event)
|
||||
if (event.created_at < oldestNoteTimestamp) oldestNoteTimestamp = event.created_at
|
||||
// Add to gallery posts
|
||||
// Add to gallery posts (fed by both the notes stream and the
|
||||
// dedicated, unbounded usergallery sub)
|
||||
val gallery = _galleryPosts.value.toMutableList()
|
||||
if (gallery.none { it.id == event.id }) {
|
||||
gallery.add(event)
|
||||
gallery.sortByDescending { it.created_at }
|
||||
_galleryPosts.value = gallery
|
||||
}
|
||||
// Also show in root notes feed
|
||||
val current = _rootNotes.value.toMutableList()
|
||||
if (current.none { it.id == event.id }) {
|
||||
current.add(event)
|
||||
current.sortByDescending { _repostSortTime[it.id] ?: it.created_at }
|
||||
_rootNotes.value = current
|
||||
// Only surface gallery kinds in the chronological notes feed when
|
||||
// they arrive via the time-bounded, paginated posts stream.
|
||||
// The usergallery sub has no time bound and reaches arbitrarily far
|
||||
// back — letting it into rootNotes both collapses the feed into
|
||||
// gallery-only content and poisons oldestNoteTimestamp pagination.
|
||||
if (subscriptionId == "userposts" || subscriptionId == "userposts-more") {
|
||||
if (event.created_at < oldestNoteTimestamp) oldestNoteTimestamp = event.created_at
|
||||
val current = _rootNotes.value.toMutableList()
|
||||
if (current.none { it.id == event.id }) {
|
||||
current.add(event)
|
||||
current.sortByDescending { _repostSortTime[it.id] ?: it.created_at }
|
||||
_rootNotes.value = current
|
||||
}
|
||||
}
|
||||
}
|
||||
6 -> {
|
||||
|
||||
Reference in New Issue
Block a user