Skip to content

Commit

Permalink
Adds support for displaying video events.
Browse files Browse the repository at this point in the history
  • Loading branch information
vitorpamplona committed Dec 29, 2023
1 parent 57430c4 commit 2de3d19
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class Account(
var translateTo: String = Locale.getDefault().language,
var zapAmountChoices: List<Long> = DefaultZapAmounts,
var reactionChoices: List<String> = DefaultReactions,
var defaultZapType: LnZapEvent.ZapType = LnZapEvent.ZapType.PRIVATE,
var defaultZapType: LnZapEvent.ZapType = LnZapEvent.ZapType.PUBLIC,
var defaultFileServer: Nip96MediaServers.ServerName = Nip96MediaServers.DEFAULT[0],
var defaultHomeFollowList: MutableStateFlow<String> = MutableStateFlow(KIND3_FOLLOWS),
var defaultStoriesFollowList: MutableStateFlow<String> = MutableStateFlow(GLOBAL_FOLLOWS),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ import com.vitorpamplona.quartz.events.RepostEvent
import com.vitorpamplona.quartz.events.SealedGossipEvent
import com.vitorpamplona.quartz.events.StatusEvent
import com.vitorpamplona.quartz.events.TextNoteEvent
import com.vitorpamplona.quartz.events.VideoHorizontalEvent
import com.vitorpamplona.quartz.events.VideoVerticalEvent
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.toImmutableList
Expand Down Expand Up @@ -438,6 +440,9 @@ object LocalCache {
private fun consume(event: PinListEvent, relay: Relay?) { consumeBaseReplaceable(event, relay) }
private fun consume(event: RelaySetEvent, relay: Relay?) { consumeBaseReplaceable(event, relay) }
private fun consume(event: AudioTrackEvent, relay: Relay?) { consumeBaseReplaceable(event, relay) }
private fun consume(event: VideoVerticalEvent, relay: Relay?) { consumeBaseReplaceable(event, relay) }
private fun consume(event: VideoHorizontalEvent, relay: Relay?) { consumeBaseReplaceable(event, relay) }

fun consume(event: StatusEvent, relay: Relay?) {
val version = getOrCreateNote(event.id)
val note = getOrCreateAddressableNote(event.address())
Expand Down Expand Up @@ -1593,7 +1598,8 @@ object LocalCache {
}
is StatusEvent -> consume(event, relay)
is TextNoteEvent -> consume(event, relay)

is VideoHorizontalEvent -> consume(event, relay)
is VideoVerticalEvent -> consume(event, relay)
else -> {
Log.w("Event Not Supported", event.toJson())
}
Expand Down
153 changes: 151 additions & 2 deletions app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ import com.vitorpamplona.amethyst.ui.theme.WidthAuthorPictureModifier
import com.vitorpamplona.amethyst.ui.theme.boostedNoteModifier
import com.vitorpamplona.amethyst.ui.theme.channelNotePictureModifier
import com.vitorpamplona.amethyst.ui.theme.grayText
import com.vitorpamplona.amethyst.ui.theme.imageModifier
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor
import com.vitorpamplona.amethyst.ui.theme.normalNoteModifier
Expand Down Expand Up @@ -203,6 +204,9 @@ import com.vitorpamplona.quartz.events.ReportEvent
import com.vitorpamplona.quartz.events.RepostEvent
import com.vitorpamplona.quartz.events.TextNoteEvent
import com.vitorpamplona.quartz.events.UserMetadata
import com.vitorpamplona.quartz.events.VideoEvent
import com.vitorpamplona.quartz.events.VideoHorizontalEvent
import com.vitorpamplona.quartz.events.VideoVerticalEvent
import com.vitorpamplona.quartz.events.toImmutableListOfLists
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
Expand Down Expand Up @@ -636,7 +640,7 @@ fun LongCommunityHeader(
)
}

if (noteEvent.hasHashtags()) {
if (summary != null && noteEvent.hasHashtags()) {
DisplayUncitedHashtags(
remember(noteEvent) { noteEvent.hashtags().toImmutableList() },
summary ?: "",
Expand Down Expand Up @@ -1171,6 +1175,14 @@ private fun RenderNoteRow(
FileHeaderDisplay(baseNote, true, accountViewModel)
}

is VideoHorizontalEvent -> {
VideoDisplay(baseNote, makeItShort, canPreview, backgroundColor, accountViewModel, nav)
}

is VideoVerticalEvent -> {
VideoDisplay(baseNote, makeItShort, canPreview, backgroundColor, accountViewModel, nav)
}

is FileStorageHeaderEvent -> {
FileStorageHeaderDisplay(baseNote, true, accountViewModel)
}
Expand Down Expand Up @@ -2384,7 +2396,21 @@ private fun ReplyRow(
}

if (showReply) {
val replyingDirectlyTo = remember { note.replyTo?.lastOrNull { it.event?.kind() != CommunityDefinitionEvent.kind } }
val replyingDirectlyTo = remember(note) {
if (noteEvent is BaseTextNoteEvent) {
val replyingTo = noteEvent.replyingTo()
if (replyingTo != null) {
note.replyTo?.firstOrNull() {
// important to test both ids in case it's a replaceable event.
it.idHex == replyingTo || it.event?.id() == replyingTo
}
} else {
note.replyTo?.lastOrNull { it.event?.kind() != CommunityDefinitionEvent.kind }
}
} else {
note.replyTo?.lastOrNull { it.event?.kind() != CommunityDefinitionEvent.kind }
}
}
if (replyingDirectlyTo != null && unPackReply) {
ReplyNoteComposition(replyingDirectlyTo, backgroundColor, accountViewModel, nav)
Spacer(modifier = StdVertSpacer)
Expand Down Expand Up @@ -3028,6 +3054,129 @@ fun FileHeaderDisplay(note: Note, roundedCorner: Boolean, accountViewModel: Acco
}
}

@Composable
fun VideoDisplay(
note: Note,
makeItShort: Boolean,
canPreview: Boolean,
backgroundColor: MutableState<Color>,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
val event = (note.event as? VideoEvent) ?: return
val fullUrl = event.url() ?: return

val title = event.title()
val summary = event.content.ifBlank { null }?.takeIf { title != it }
val image = event.thumb() ?: event.image()
val isYouTube = fullUrl.contains("youtube.com") || fullUrl.contains("youtu.be")
val tags = remember(note) { note.event?.tags()?.toImmutableListOfLists() ?: EmptyTagList }

val content by remember(note) {
val blurHash = event.blurhash()
val hash = event.hash()
val dimensions = event.dimensions()
val description = event.alt() ?: event.content
val isImage = imageExtensions.any {
removeQueryParamsForExtensionComparison(fullUrl).lowercase().endsWith(it)
}
val uri = note.toNostrUri()

mutableStateOf<ZoomableContent>(
if (isImage) {
ZoomableUrlImage(
url = fullUrl,
description = description,
hash = hash,
blurhash = blurHash,
dim = dimensions,
uri = uri
)
} else {
ZoomableUrlVideo(
url = fullUrl,
description = description,
hash = hash,
dim = dimensions,
uri = uri,
authorName = note.author?.toBestDisplayName(),
artworkUri = event.thumb() ?: event.image()
)
}
)
}

SensitivityWarning(note = note, accountViewModel = accountViewModel) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(top = 5.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
if (isYouTube) {
val uri = LocalUriHandler.current
Row(
modifier = Modifier.clickable { runCatching { uri.openUri(fullUrl) } }
) {
image?.let {
AsyncImage(
model = it,
contentDescription = stringResource(
R.string.preview_card_image_for,
it
),
contentScale = ContentScale.FillWidth,
modifier = MaterialTheme.colorScheme.imageModifier
)
} ?: CreateImageHeader(note, accountViewModel)
}
} else {
ZoomableContentView(
content = content,
roundedCorner = true,
accountViewModel = accountViewModel
)
}

title?.let {
Text(
text = it,
fontWeight = FontWeight.Bold,
maxLines = 3,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
.fillMaxWidth()
.padding(top = 5.dp)
)
}

summary?.let {
TranslatableRichTextViewer(
content = it,
canPreview = canPreview && !makeItShort,
modifier = Modifier.fillMaxWidth(),
tags = tags,
backgroundColor = backgroundColor,
accountViewModel = accountViewModel,
nav = nav
)
}

if (event.hasHashtags()) {
Row(
Modifier.fillMaxWidth()
) {
DisplayUncitedHashtags(
remember(event) { event.hashtags().toImmutableList() },
summary ?: "",
nav
)
}
}
}
}
}

@Composable
fun FileStorageHeaderDisplay(baseNote: Note, roundedCorner: Boolean, accountViewModel: AccountViewModel) {
val eventHeader = (baseNote.event as? FileStorageHeaderEvent) ?: return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ import com.vitorpamplona.amethyst.ui.note.RenderPoll
import com.vitorpamplona.amethyst.ui.note.RenderPostApproval
import com.vitorpamplona.amethyst.ui.note.RenderRepost
import com.vitorpamplona.amethyst.ui.note.RenderTextEvent
import com.vitorpamplona.amethyst.ui.note.VideoDisplay
import com.vitorpamplona.amethyst.ui.note.showAmount
import com.vitorpamplona.amethyst.ui.note.timeAgo
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
Expand Down Expand Up @@ -130,6 +131,7 @@ import com.vitorpamplona.quartz.events.PinListEvent
import com.vitorpamplona.quartz.events.PollNoteEvent
import com.vitorpamplona.quartz.events.RelaySetEvent
import com.vitorpamplona.quartz.events.RepostEvent
import com.vitorpamplona.quartz.events.VideoEvent
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableSet
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -423,6 +425,8 @@ fun NoteMaster(
accountViewModel = accountViewModel,
nav = nav
)
} else if (noteEvent is VideoEvent) {
VideoDisplay(baseNote, false, true, backgroundColor, accountViewModel, nav)
} else if (noteEvent is FileHeaderEvent) {
FileHeaderDisplay(baseNote, true, accountViewModel)
} else if (noteEvent is FileStorageHeaderEvent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ open class BaseTextNoteEvent(
fun mentions() = taggedUsers()
open fun replyTos() = taggedEvents()

fun replyingTo(): HexKey? {
val oldStylePositional = tags.lastOrNull() { it.size > 1 && it[0] == "e" }?.get(1)
val newStyle = tags.lastOrNull { it.size > 3 && it[0] == "e" && it[3] == "reply" }?.get(1)

return newStyle ?: oldStylePositional
}

@Transient
private var citedUsersCache: Set<HexKey>? = null

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class GoalEvent(
createdAt: Long = TimeUtils.now(),
onReady: (GoalEvent) -> Unit
) {
var tags = mutableListOf(
val tags = mutableListOf(
arrayOf(AMOUNT, amount.toString()),
arrayOf("relays") + relays,
arrayOf("alt", alt)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ abstract class VideoEvent(
fun torrentInfoHash() = tags.firstOrNull { it.size > 1 && it[0] == TORRENT_INFOHASH }?.get(1)
fun blurhash() = tags.firstOrNull { it.size > 1 && it[0] == BLUR_HASH }?.get(1)

fun title() = tags.firstOrNull { it.size > 1 && it[0] == TITLE }?.get(1)
fun summary() = tags.firstOrNull { it.size > 1 && it[0] == SUMMARY }?.get(1)
fun image() = tags.firstOrNull { it.size > 1 && it[0] == IMAGE }?.get(1)
fun thumb() = tags.firstOrNull { it.size > 1 && it[0] == THUMB }?.get(1)

fun hasUrl() = tags.any { it.size > 1 && it[0] == URL }

companion object {
Expand Down

0 comments on commit 2de3d19

Please sign in to comment.