Passes the video note uri so that when the user clicks in the video popup the app brings it back to the playing screen without breaking the video.

This commit is contained in:
Vitor Pamplona 2024-06-03 17:26:40 -04:00
parent 2b2ee5bbfc
commit a6519d57c6
27 changed files with 100 additions and 62 deletions

View File

@ -36,6 +36,7 @@ fun TranslatableRichTextViewer(
tags: ImmutableListOfLists<String>,
backgroundColor: MutableState<Color>,
id: String,
callbackUri: String? = null,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) = ExpandableRichTextViewer(
@ -46,6 +47,7 @@ fun TranslatableRichTextViewer(
tags,
backgroundColor,
id,
callbackUri,
accountViewModel,
nav,
)

View File

@ -31,11 +31,12 @@ object CachedRichTextParser {
fun parseText(
content: String,
tags: ImmutableListOfLists<String>,
callbackUri: String? = null,
): RichTextViewerState {
return if (richTextCache[content] != null) {
richTextCache[content]
} else {
val newUrls = RichTextParser().parseText(content, tags)
val newUrls = RichTextParser().parseText(content, tags, callbackUri)
richTextCache.put(content, newUrls)
newUrls
}

View File

@ -308,7 +308,7 @@ fun EditPostView(
accountViewModel = accountViewModel,
)
} else {
LoadUrlPreview(myUrlPreview, myUrlPreview, accountViewModel)
LoadUrlPreview(myUrlPreview, myUrlPreview, null, accountViewModel)
}
} else if (RichTextParser.startsWithNIP19Scheme(myUrlPreview)) {
val bgColor = MaterialTheme.colorScheme.background
@ -323,7 +323,7 @@ fun EditPostView(
nav = nav,
)
} else if (RichTextParser.isUrlWithoutScheme(myUrlPreview)) {
LoadUrlPreview("https://$myUrlPreview", myUrlPreview, accountViewModel)
LoadUrlPreview("https://$myUrlPreview", myUrlPreview, null, accountViewModel)
}
}
}

View File

@ -429,7 +429,7 @@ fun NewPostView(
accountViewModel = accountViewModel,
)
} else {
LoadUrlPreview(myUrlPreview, myUrlPreview, accountViewModel)
LoadUrlPreview(myUrlPreview, myUrlPreview, null, accountViewModel)
}
} else if (RichTextParser.startsWithNIP19Scheme(myUrlPreview)) {
val bgColor = MaterialTheme.colorScheme.background
@ -444,7 +444,7 @@ fun NewPostView(
nav = nav,
)
} else if (RichTextParser.isUrlWithoutScheme(myUrlPreview)) {
LoadUrlPreview("https://$myUrlPreview", myUrlPreview, accountViewModel)
LoadUrlPreview("https://$myUrlPreview", myUrlPreview, null, accountViewModel)
}
}
}

View File

@ -63,15 +63,16 @@ fun NotifyRequestDialog(
val background = remember { mutableStateOf(defaultBackground) }
TranslatableRichTextViewer(
textContent,
content = textContent,
canPreview = true,
quotesLeft = 1,
Modifier.fillMaxWidth(),
EmptyTagList,
background,
textContent,
accountViewModel,
nav,
modifier = Modifier.fillMaxWidth(),
tags = EmptyTagList,
backgroundColor = background,
id = textContent,
callbackUri = null,
accountViewModel = accountViewModel,
nav = nav,
)
},
confirmButton = {

View File

@ -64,6 +64,7 @@ fun ExpandableRichTextViewer(
tags: ImmutableListOfLists<String>,
backgroundColor: MutableState<Color>,
id: String,
callbackUri: String? = null,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
@ -99,6 +100,7 @@ fun ExpandableRichTextViewer(
modifier.align(Alignment.TopStart),
tags,
backgroundColor,
callbackUri,
accountViewModel,
nav,
)

View File

@ -37,6 +37,7 @@ import com.vitorpamplona.amethyst.ui.theme.HalfVertPadding
fun LoadUrlPreview(
url: String,
urlText: String,
callbackUri: String? = null,
accountViewModel: AccountViewModel,
) {
val automaticallyShowUrlPreview = remember { accountViewModel.settings.showUrlPreview.value }
@ -61,7 +62,7 @@ fun LoadUrlPreview(
) { state ->
when (state) {
is UrlPreviewState.Loaded -> {
RenderLoaded(state, url, accountViewModel)
RenderLoaded(state, url, callbackUri, accountViewModel)
}
else -> {
ClickableUrl(urlText, url)
@ -75,12 +76,13 @@ fun LoadUrlPreview(
fun RenderLoaded(
state: UrlPreviewState.Loaded,
url: String,
callbackUri: String? = null,
accountViewModel: AccountViewModel,
) {
if (state.previewInfo.mimeType.type == "image") {
Box(modifier = HalfVertPadding) {
ZoomableContentView(
content = MediaUrlImage(url),
content = MediaUrlImage(url, uri = callbackUri),
roundedCorner = true,
accountViewModel = accountViewModel,
)
@ -88,7 +90,7 @@ fun RenderLoaded(
} else if (state.previewInfo.mimeType.type == "video") {
Box(modifier = HalfVertPadding) {
ZoomableContentView(
content = MediaUrlVideo(url),
content = MediaUrlVideo(url, uri = callbackUri),
roundedCorner = true,
accountViewModel = accountViewModel,
)

View File

@ -121,14 +121,15 @@ fun RichTextViewer(
modifier: Modifier,
tags: ImmutableListOfLists<String>,
backgroundColor: MutableState<Color>,
callbackUri: String? = null,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
Column(modifier = modifier) {
if (remember(content) { isMarkdown(content) }) {
RenderContentAsMarkdown(content, tags, canPreview, quotesLeft, backgroundColor, accountViewModel, nav)
RenderContentAsMarkdown(content, tags, canPreview, quotesLeft, backgroundColor, callbackUri, accountViewModel, nav)
} else {
RenderRegular(content, tags, canPreview, quotesLeft, backgroundColor, accountViewModel, nav)
RenderRegular(content, tags, canPreview, quotesLeft, backgroundColor, callbackUri, accountViewModel, nav)
}
}
}
@ -267,7 +268,7 @@ fun RenderRegularPreview3() {
) { word, state ->
when (word) {
// is ImageSegment -> ZoomableContentView(word.segmentText, state, accountViewModel)
is LinkSegment -> LoadUrlPreview(word.segmentText, word.segmentText, accountViewModel)
is LinkSegment -> LoadUrlPreview(word.segmentText, word.segmentText, null, accountViewModel)
is EmojiSegment -> RenderCustomEmoji(word.segmentText, state)
is InvoiceSegment -> MayBeInvoicePreview(word.segmentText)
is WithdrawSegment -> MayBeWithdrawal(word.segmentText)
@ -291,16 +292,18 @@ private fun RenderRegular(
canPreview: Boolean,
quotesLeft: Int,
backgroundColor: MutableState<Color>,
callbackUri: String? = null,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
RenderRegular(content, tags) { word, state ->
RenderRegular(content, tags, callbackUri) { word, state ->
if (canPreview) {
RenderWordWithPreview(
word,
state,
backgroundColor,
quotesLeft,
callbackUri,
accountViewModel,
nav,
)
@ -321,9 +324,10 @@ private fun RenderRegular(
fun RenderRegular(
content: String,
tags: ImmutableListOfLists<String>,
callbackUri: String? = null,
wordRenderer: @Composable (Segment, RichTextViewerState) -> Unit,
) {
val state by remember(content, tags) { mutableStateOf(CachedRichTextParser.parseText(content, tags)) }
val state by remember(content, tags) { mutableStateOf(CachedRichTextParser.parseText(content, tags, callbackUri)) }
val spaceWidth = measureSpaceWidth(LocalTextStyle.current)
@ -412,12 +416,13 @@ private fun RenderWordWithPreview(
state: RichTextViewerState,
backgroundColor: MutableState<Color>,
quotesLeft: Int,
callbackUri: String? = null,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
when (word) {
is ImageSegment -> ZoomableContentView(word.segmentText, state, accountViewModel)
is LinkSegment -> LoadUrlPreview(word.segmentText, word.segmentText, accountViewModel)
is LinkSegment -> LoadUrlPreview(word.segmentText, word.segmentText, callbackUri, accountViewModel)
is EmojiSegment -> RenderCustomEmoji(word.segmentText, state)
is InvoiceSegment -> MayBeInvoicePreview(word.segmentText)
is WithdrawSegment -> MayBeWithdrawal(word.segmentText)

View File

@ -542,15 +542,13 @@ fun GetVideoController(
}
}
if (event == Lifecycle.Event.ON_PAUSE) {
GlobalScope.launch(Dispatchers.Main) {
if (!keepPlaying.value) {
// Stops and releases the media.
controller.value?.let {
Log.d("PlaybackService", "Releasing Video from Pause $videoUri ")
it.stop()
it.release()
controller.value = null
}
if (!keepPlaying.value) {
// Stops and releases the media.
controller.value?.let {
Log.d("PlaybackService", "Releasing Video from Pause $videoUri ")
it.stop()
it.release()
controller.value = null
}
}
}

View File

@ -57,6 +57,7 @@ class MarkdownMediaRenderer(
val canPreview: Boolean,
val quotesLeft: Int,
val backgroundColor: MutableState<Color>,
val callbackUri: String? = null,
val accountViewModel: AccountViewModel,
val nav: (String) -> Unit,
) : MediaRenderer {
@ -107,7 +108,7 @@ class MarkdownMediaRenderer(
uri: String,
richTextStringBuilder: RichTextString.Builder,
) {
val content = parser.parseMediaUrl(uri, eventTags = tags ?: EmptyTagList, startOfText)
val content = parser.parseMediaUrl(uri, eventTags = tags ?: EmptyTagList, startOfText, callbackUri)
if (canPreview) {
if (content != null) {
@ -123,7 +124,7 @@ class MarkdownMediaRenderer(
renderAsCompleteLink(title ?: uri, uri, richTextStringBuilder)
} else {
renderInlineFullWidth(richTextStringBuilder) {
LoadUrlPreview(uri, title ?: uri, accountViewModel)
LoadUrlPreview(uri, title ?: uri, callbackUri, accountViewModel)
}
}
}

View File

@ -44,6 +44,7 @@ fun RenderContentAsMarkdown(
canPreview: Boolean,
quotesLeft: Int,
backgroundColor: MutableState<Color>,
callbackUri: String? = null,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
@ -70,13 +71,14 @@ fun RenderContentAsMarkdown(
val renderer =
remember(content) {
MarkdownMediaRenderer(
content.take(100),
tags,
canPreview,
quotesLeft,
backgroundColor,
accountViewModel,
nav,
startOfText = content.take(100),
tags = tags,
canPreview = canPreview,
quotesLeft = quotesLeft,
backgroundColor = backgroundColor,
callbackUri = callbackUri,
accountViewModel = accountViewModel,
nav = nav,
)
}

View File

@ -396,9 +396,12 @@ fun AppNavigation(
LaunchedEffect(intentNextPage) {
if (actionableNextPage != null) {
actionableNextPage?.let {
navController.navigate(it) {
popUpTo(Route.Home.route)
launchSingleTop = true
val currentRoute = getRouteWithArguments(navController)
if (!isSameRoute(currentRoute, it)) {
navController.navigate(it) {
popUpTo(Route.Home.route)
launchSingleTop = true
}
}
actionableNextPage = null
}

View File

@ -642,6 +642,7 @@ private fun RenderRegularTextNote(
tags = tags,
backgroundColor = backgroundBubbleColor,
id = note.idHex,
callbackUri = note.toNostrUri(),
accountViewModel = accountViewModel,
nav = nav,
)
@ -655,6 +656,7 @@ private fun RenderRegularTextNote(
tags = EmptyTagList,
backgroundColor = backgroundBubbleColor,
id = note.idHex,
callbackUri = note.toNostrUri(),
accountViewModel = accountViewModel,
nav = nav,
)

View File

@ -179,6 +179,7 @@ private fun OptionNote(
pollViewModel = pollViewModel,
nonClickablePrepend = {
RenderOptionAfterVote(
baseNote,
poolOption,
color,
canPreview,
@ -217,6 +218,7 @@ private fun OptionNote(
@Composable
private fun RenderOptionAfterVote(
baseNote: Note,
poolOption: PollOption,
color: Color,
canPreview: Boolean,
@ -276,7 +278,8 @@ private fun RenderOptionAfterVote(
Modifier,
tags,
backgroundColor,
poolOption.descriptor,
baseNote.idHex + poolOption.descriptor,
baseNote.toNostrUri(),
accountViewModel,
nav,
)

View File

@ -236,6 +236,7 @@ fun RenderAppDefinition(
tags = tags,
backgroundColor = backgroundColor,
id = note.idHex,
callbackUri = note.toNostrUri(),
accountViewModel = accountViewModel,
nav = nav,
)

View File

@ -220,6 +220,7 @@ fun AudioHeader(
tags = tags,
backgroundColor = background,
id = note.idHex,
callbackUri = note.toNostrUri(),
accountViewModel = accountViewModel,
nav = nav,
)

View File

@ -188,6 +188,7 @@ private fun RenderGitPatchEvent(
tags = tags,
backgroundColor = backgroundColor,
id = note.idHex,
callbackUri = note.toNostrUri(),
accountViewModel = accountViewModel,
nav = nav,
)
@ -291,6 +292,7 @@ private fun RenderGitIssueEvent(
tags = tags,
backgroundColor = backgroundColor,
id = note.idHex,
callbackUri = note.toNostrUri(),
accountViewModel = accountViewModel,
nav = nav,
)

View File

@ -100,15 +100,16 @@ fun DisplayHighlight(
}
TranslatableRichTextViewer(
quote,
content = quote,
canPreview = canPreview && !makeItShort,
quotesLeft,
Modifier.fillMaxWidth(),
EmptyTagList,
backgroundColor,
quotesLeft = quotesLeft,
modifier = Modifier.fillMaxWidth(),
tags = EmptyTagList,
backgroundColor = backgroundColor,
id = quote,
accountViewModel,
nav,
callbackUri = null,
accountViewModel = accountViewModel,
nav = nav,
)
DisplayQuoteAuthor(authorHex ?: "", url, postAddress, accountViewModel, nav)

View File

@ -138,6 +138,7 @@ fun RenderNIP90ContentDiscoveryResponse(
tags = tags,
backgroundColor = backgroundColor,
id = note.idHex,
callbackUri = note.toNostrUri(),
accountViewModel = accountViewModel,
nav = nav,
)

View File

@ -41,7 +41,6 @@ import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.events.BaseTextNoteEvent
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
import com.vitorpamplona.quartz.events.EmptyTagList
import com.vitorpamplona.quartz.events.PollNoteEvent
@ -65,22 +64,18 @@ fun RenderPoll(
val showReply by
remember(note) {
derivedStateOf {
noteEvent is BaseTextNoteEvent && !makeItShort && unPackReply && (note.replyTo != null || noteEvent.hasAnyTaggedUser())
!makeItShort && unPackReply && (note.replyTo != null || noteEvent.hasAnyTaggedUser())
}
}
if (showReply) {
val replyingDirectlyTo =
remember(note) {
if (noteEvent is BaseTextNoteEvent) {
val replyingTo = noteEvent.replyingToAddressOrEvent()
if (replyingTo != null) {
val newNote = accountViewModel.getNoteIfExists(replyingTo)
if (newNote != null && newNote.channelHex() == null && newNote.event?.kind() != CommunityDefinitionEvent.KIND) {
newNote
} else {
note.replyTo?.lastOrNull { it.event?.kind() != CommunityDefinitionEvent.KIND }
}
val replyingTo = noteEvent.replyingToAddressOrEvent()
if (replyingTo != null) {
val newNote = accountViewModel.getNoteIfExists(replyingTo)
if (newNote != null && newNote.channelHex() == null && newNote.event?.kind() != CommunityDefinitionEvent.KIND) {
newNote
} else {
note.replyTo?.lastOrNull { it.event?.kind() != CommunityDefinitionEvent.KIND }
}
@ -116,6 +111,7 @@ fun RenderPoll(
tags = tags,
backgroundColor = backgroundColor,
id = note.idHex,
callbackUri = note.toNostrUri(),
accountViewModel = accountViewModel,
nav = nav,
)

View File

@ -115,6 +115,7 @@ fun RenderPrivateMessage(
tags = tags,
backgroundColor = backgroundColor,
id = note.idHex,
callbackUri = note.toNostrUri(),
accountViewModel = accountViewModel,
nav = nav,
)
@ -144,6 +145,7 @@ fun RenderPrivateMessage(
tags = EmptyTagList,
backgroundColor = backgroundColor,
id = note.idHex,
callbackUri = note.toNostrUri(),
accountViewModel = accountViewModel,
nav = nav,
)

View File

@ -76,6 +76,7 @@ fun RenderReport(
tags = EmptyTagList,
backgroundColor = backgroundColor,
id = note.idHex,
callbackUri = note.toNostrUri(),
quotesLeft = 1,
accountViewModel = accountViewModel,
nav = nav,

View File

@ -149,6 +149,7 @@ fun RenderTextEvent(
tags = tags,
backgroundColor = backgroundColor,
id = note.idHex,
callbackUri = note.toNostrUri(),
accountViewModel = accountViewModel,
nav = nav,
)

View File

@ -119,6 +119,7 @@ fun RenderTextModificationEvent(
tags = EmptyTagList,
backgroundColor = backgroundColor,
id = note.idHex,
callbackUri = note.toNostrUri(),
accountViewModel = accountViewModel,
nav = nav,
)

View File

@ -169,6 +169,7 @@ fun VideoDisplay(
tags = tags,
backgroundColor = backgroundColor,
id = note.idHex,
callbackUri = note.toNostrUri(),
accountViewModel = accountViewModel,
nav = nav,
)

View File

@ -77,6 +77,7 @@ fun TranslatableRichTextViewer(
tags: ImmutableListOfLists<String>,
backgroundColor: MutableState<Color>,
id: String,
callbackUri: String? = null,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
@ -103,6 +104,7 @@ fun TranslatableRichTextViewer(
tags = tags,
backgroundColor = backgroundColor,
id = id,
callbackUri = callbackUri,
accountViewModel = accountViewModel,
nav = nav,
)
@ -119,6 +121,7 @@ private fun RenderText(
tags: ImmutableListOfLists<String>,
backgroundColor: MutableState<Color>,
id: String,
callbackUri: String? = null,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
@ -139,6 +142,7 @@ private fun RenderText(
tags,
backgroundColor,
id,
callbackUri,
accountViewModel,
nav,
)

View File

@ -46,6 +46,7 @@ class RichTextParser() {
fullUrl: String,
eventTags: ImmutableListOfLists<String>,
description: String?,
callbackUri: String? = null,
): MediaUrlContent? {
val removedParamsFromUrl = removeQueryParamsForExtensionComparison(fullUrl)
return if (imageExtensions.any { removedParamsFromUrl.endsWith(it) }) {
@ -59,6 +60,7 @@ class RichTextParser() {
blurhash = frags[FileHeaderEvent.BLUR_HASH] ?: tags[FileHeaderEvent.BLUR_HASH],
dim = frags[FileHeaderEvent.DIMENSION] ?: tags[FileHeaderEvent.DIMENSION],
contentWarning = frags["content-warning"] ?: tags["content-warning"],
uri = callbackUri,
)
} else if (videoExtensions.any { removedParamsFromUrl.endsWith(it) }) {
val frags = Nip54InlineMetadata().parse(fullUrl)
@ -70,6 +72,7 @@ class RichTextParser() {
blurhash = frags[FileHeaderEvent.BLUR_HASH] ?: tags[FileHeaderEvent.BLUR_HASH],
dim = frags[FileHeaderEvent.DIMENSION] ?: tags[FileHeaderEvent.DIMENSION],
contentWarning = frags["content-warning"] ?: tags["content-warning"],
uri = callbackUri,
)
} else {
null
@ -103,11 +106,12 @@ class RichTextParser() {
fun parseText(
content: String,
tags: ImmutableListOfLists<String>,
callbackUri: String?,
): RichTextViewerState {
val urlSet = parseValidUrls(content)
val imagesForPager =
urlSet.mapNotNull { fullUrl -> parseMediaUrl(fullUrl, tags, content) }.associateBy { it.url }
urlSet.mapNotNull { fullUrl -> parseMediaUrl(fullUrl, tags, content, callbackUri) }.associateBy { it.url }
val imageList = imagesForPager.values.toList()
val emojiMap = Nip30CustomEmoji.createEmojiMap(tags)