Merge pull request #1001 from believethehype/better_gallery_view

Gallery: use FileHeader Info for rendering / faster loading
This commit is contained in:
Vitor Pamplona 2024-07-31 10:41:19 -04:00 committed by GitHub
commit 4bae04ad31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 150 additions and 68 deletions

View File

@ -139,8 +139,6 @@ open class Note(
var relays = listOf<RelayBriefInfoCache.RelayBriefInfo>()
private set
var associatedNote: Note? = null
var lastReactionsDownloadTime: Map<String, EOSETime> = emptyMap()
fun id() = Hex.decode(idHex)

View File

@ -176,6 +176,69 @@ fun ZoomableContentView(
}
}
@Composable
fun GalleryContentView(
content: BaseMediaContent,
roundedCorner: Boolean,
isFiniteHeight: Boolean,
accountViewModel: AccountViewModel,
) {
when (content) {
is MediaUrlImage ->
SensitivityWarning(content.contentWarning != null, accountViewModel) {
TwoSecondController(content) { controllerVisible ->
val mainImageModifier = Modifier.fillMaxWidth()
val loadedImageModifier = if (roundedCorner) MaterialTheme.colorScheme.imageModifier else Modifier.fillMaxWidth()
UrlImageView(content, mainImageModifier, loadedImageModifier, isFiniteHeight, controllerVisible, accountViewModel = accountViewModel, gallery = true)
}
}
is MediaUrlVideo ->
SensitivityWarning(content.contentWarning != null, accountViewModel) {
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
VideoView(
videoUri = content.url,
mimeType = content.mimeType,
title = content.description,
artworkUri = content.artworkUri,
gallery = true,
authorName = content.authorName,
dimensions = content.dim,
blurhash = content.blurhash,
roundedCorner = roundedCorner,
isFiniteHeight = isFiniteHeight,
nostrUriCallback = content.uri,
accountViewModel = accountViewModel,
)
}
}
is MediaLocalImage ->
TwoSecondController(content) { controllerVisible ->
val mainImageModifier = Modifier.fillMaxWidth()
val loadedImageModifier = if (roundedCorner) MaterialTheme.colorScheme.imageModifier else Modifier.fillMaxWidth()
LocalImageView(content, mainImageModifier, loadedImageModifier, isFiniteHeight, controllerVisible, accountViewModel = accountViewModel)
}
is MediaLocalVideo ->
content.localFile?.let {
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
VideoView(
videoUri = it.toUri().toString(),
mimeType = content.mimeType,
title = content.description,
artworkUri = content.artworkUri,
authorName = content.authorName,
gallery = true,
roundedCorner = roundedCorner,
isFiniteHeight = isFiniteHeight,
nostrUriCallback = content.uri,
accountViewModel = accountViewModel,
)
}
}
}
}
@Composable
fun TwoSecondController(
content: BaseMediaContent,
@ -303,6 +366,7 @@ fun UrlImageView(
isFiniteHeight: Boolean,
controllerVisible: MutableState<Boolean>,
accountViewModel: AccountViewModel,
gallery: Boolean = false,
alwayShowImage: Boolean = false,
) {
Box(contentAlignment = Alignment.Center) {
@ -319,7 +383,14 @@ fun UrlImageView(
SubcomposeAsyncImage(
model = content.url,
contentDescription = content.description,
contentScale = if (isFiniteHeight) ContentScale.Fit else ContentScale.FillWidth,
contentScale =
if (gallery) {
ContentScale.Crop
} else if (isFiniteHeight) {
ContentScale.Fit
} else {
ContentScale.FillWidth
},
modifier = mainImageModifier,
) {
when (painter.state) {

View File

@ -47,16 +47,7 @@ class UserProfileGalleryFeedFilter(
}
var sorted = sort(notes)
var finalnotes = setOf<Note>()
for (item in sorted) {
val note = (item.event as ProfileGalleryEntryEvent).event()?.let { LocalCache.checkGetOrCreateNote(it) }
if (note != null) {
note.associatedNote = item
finalnotes = finalnotes + note
}
}
return finalnotes.toList()
return sorted.toList()
}
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)

View File

@ -45,18 +45,20 @@ import androidx.compose.ui.Alignment.Companion.BottomStart
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
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.commons.richtext.BaseMediaContent
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser.Companion.isVideoUrl
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.GalleryContentView
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
import com.vitorpamplona.amethyst.ui.components.VideoView
import com.vitorpamplona.amethyst.ui.note.CheckHiddenFeedWatchBlockAndReport
import com.vitorpamplona.amethyst.ui.note.ClickableNote
import com.vitorpamplona.amethyst.ui.note.LongPressToQuickActionGallery
@ -79,7 +81,6 @@ import com.vitorpamplona.quartz.events.ProfileGalleryEntryEvent
fun RenderGalleryFeed(
viewModel: FeedViewModel,
routeForLastRead: String?,
forceEventKind: Int?,
listState: LazyGridState,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
@ -103,7 +104,6 @@ fun RenderGalleryFeed(
state,
routeForLastRead,
listState,
forceEventKind,
accountViewModel,
nav,
)
@ -121,7 +121,6 @@ private fun GalleryFeedLoaded(
state: FeedState.Loaded,
routeForLastRead: String?,
listState: LazyGridState,
forceEventKind: Int?,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
@ -138,7 +137,6 @@ private fun GalleryFeedLoaded(
baseNote = item,
routeForLastRead = routeForLastRead,
modifier = Modifier,
forceEventKind = forceEventKind,
accountViewModel = accountViewModel,
nav = nav,
)
@ -157,7 +155,6 @@ fun GalleryCardCompose(
routeForLastRead: String? = null,
modifier: Modifier = Modifier,
parentBackgroundColor: MutableState<Color>? = null,
forceEventKind: Int?,
isHiddenFeed: Boolean = false,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
@ -172,20 +169,29 @@ fun GalleryCardCompose(
nav = nav,
) { canPreview ->
if (baseNote.associatedNote != null) {
if (baseNote.associatedNote!!.event != null) {
val image = (baseNote.associatedNote!!.event as ProfileGalleryEntryEvent).url()
if (image != null) {
GalleryCard(
galleryNote = baseNote.associatedNote!!,
baseNote = baseNote,
image = image,
modifier = modifier,
parentBackgroundColor = parentBackgroundColor,
accountViewModel = accountViewModel,
nav = nav,
)
}
// TODO Vitor, this works, but maybe you know of a better way to run this here in the background
// as LocalCache.checkGetOrCreateNote(it) can not run on the main thread
var note: Note? = null
val thread =
Thread {
note = (baseNote.event as ProfileGalleryEntryEvent).event()?.let { LocalCache.checkGetOrCreateNote(it) }
}
thread.start()
thread.join()
// TODO End
val image = (baseNote.event as ProfileGalleryEntryEvent).url()
if (image != null) {
note?.let {
GalleryCard(
galleryNote = baseNote,
baseNote = it,
image = image,
modifier = modifier,
parentBackgroundColor = parentBackgroundColor,
accountViewModel = accountViewModel,
nav = nav,
)
}
}
}
@ -206,6 +212,7 @@ fun GalleryCard(
LongPressToQuickActionGallery(baseNote = galleryNote, accountViewModel = accountViewModel) { showPopup ->
CheckNewAndRenderChannelCard(
baseNote,
galleryNote,
image,
modifier,
parentBackgroundColor,
@ -219,6 +226,7 @@ fun GalleryCard(
@Composable
private fun CheckNewAndRenderChannelCard(
baseNote: Note,
galleryNote: Note,
image: String,
modifier: Modifier = Modifier,
parentBackgroundColor: MutableState<Color>? = null,
@ -241,7 +249,7 @@ private fun CheckNewAndRenderChannelCard(
showPopup = showPopup,
nav = nav,
) {
InnerGalleryCardBox(baseNote, image, accountViewModel, nav)
InnerGalleryCardBox(galleryNote, image, accountViewModel, nav)
}
}
@ -267,7 +275,6 @@ data class GalleryThumb(
val id: String?,
val image: String?,
val title: String?,
// val price: Price?,
)
@Composable
@ -287,7 +294,6 @@ fun RenderGalleryThumb(
image = image,
title = "",
// noteEvent?.title(),
// price = noteEvent?.price(),
)
}.distinctUntilChanged()
.observeAsState(
@ -298,7 +304,7 @@ fun RenderGalleryThumb(
),
)
InnerRenderGalleryThumb(card as GalleryThumb, baseNote, accountViewModel)
InnerRenderGalleryThumb(card, baseNote, accountViewModel)
}
@Preview
@ -311,7 +317,6 @@ fun RenderGalleryThumbPreview(accountViewModel: AccountViewModel) {
id = "",
image = null,
title = "Like New",
// price = Price("800000", "SATS", null),
),
note = Note("hex"),
accountViewModel = accountViewModel,
@ -332,27 +337,45 @@ fun InnerRenderGalleryThumb(
contentAlignment = BottomStart,
) {
card.image?.let {
var blurHash = (note.event as ProfileGalleryEntryEvent).blurhash()
var description = (note.event as ProfileGalleryEntryEvent).content
// var hash = (note.event as ProfileGalleryEntryEvent).hash()
var dimensions = (note.event as ProfileGalleryEntryEvent).dimensions()
var mimeType = (note.event as ProfileGalleryEntryEvent).mimeType()
var content: BaseMediaContent? = null
if (isVideoUrl(it)) {
VideoView(
videoUri = it,
mimeType = null,
title = "",
authorName = note.author?.toBestDisplayName(),
roundedCorner = false,
gallery = true,
isFiniteHeight = false,
alwaysShowVideo = true,
accountViewModel = accountViewModel,
)
content =
MediaUrlVideo(
url = it,
description = description,
hash = null,
blurhash = blurHash,
dim = dimensions,
uri = null,
mimeType = mimeType,
)
} else {
AsyncImage(
model = it,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(),
)
content =
MediaUrlImage(
url = it,
description = description,
hash = null, // We don't want to show the hash banner here
blurhash = blurHash,
dim = dimensions,
uri = null,
mimeType = mimeType,
)
}
GalleryContentView(
content = content,
roundedCorner = false,
isFiniteHeight = false,
accountViewModel = accountViewModel,
)
}
// }
?: run { DisplayGalleryAuthorBanner(note) }
}
}

View File

@ -240,6 +240,16 @@ fun PrepareViewModels(
),
)
val galleryFeedViewModel: NostrUserProfileGalleryFeedViewModel =
viewModel(
key = baseUser.pubkeyHex + "UserGalleryFeedViewModel",
factory =
NostrUserProfileGalleryFeedViewModel.Factory(
baseUser,
accountViewModel.account,
),
)
val followersFeedViewModel: NostrUserProfileFollowersUserFeedViewModel =
viewModel(
key = baseUser.pubkeyHex + "UserProfileFollowersUserFeedViewModel",
@ -298,16 +308,6 @@ fun PrepareViewModels(
),
)
val galleryFeedViewModel: NostrUserProfileGalleryFeedViewModel =
viewModel(
key = baseUser.pubkeyHex + "UserGalleryFeedViewModel",
factory =
NostrUserProfileGalleryFeedViewModel.Factory(
baseUser,
accountViewModel.account,
),
)
val reportsFeedViewModel: NostrUserProfileReportFeedViewModel =
viewModel(
key = baseUser.pubkeyHex + "UserProfileReportFeedViewModel",
@ -1574,7 +1574,6 @@ fun TabGallery(
RenderGalleryFeed(
feedViewModel,
null,
0,
listState,
accountViewModel = accountViewModel,
nav = nav,