Makes the new Video events non replaceable, while keeping the old ones in the replaceable

This commit is contained in:
Vitor Pamplona
2025-09-03 17:44:40 -04:00
parent 8131f3db39
commit 8d73bf2cc4
11 changed files with 186 additions and 63 deletions

View File

@@ -1044,13 +1044,13 @@ object LocalCache : ILocalCache {
event: VideoNormalEvent,
relay: NormalizedRelayUrl?,
wasVerified: Boolean,
) = consumeBaseReplaceable(event, relay, wasVerified)
) = consumeRegularEvent(event, relay, wasVerified)
private fun consume(
event: VideoShortEvent,
relay: NormalizedRelayUrl?,
wasVerified: Boolean,
) = consumeBaseReplaceable(event, relay, wasVerified)
) = consumeRegularEvent(event, relay, wasVerified)
fun consume(
event: StatusEvent,

View File

@@ -59,6 +59,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Size5dp
import com.vitorpamplona.amethyst.ui.theme.imageModifier
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
import com.vitorpamplona.quartz.nip02FollowList.toImmutableListOfLists
@@ -75,18 +76,20 @@ fun VideoDisplay(
accountViewModel: AccountViewModel,
nav: INav,
) {
val event = (note.event as? VideoEvent) ?: return
val imeta = event.imetaTags().firstOrNull() ?: return
val videoEvent = (note.event as? VideoEvent) ?: return
val event = (videoEvent as? Event) ?: return
val title = event.title()
val summary = event.content.ifBlank { null }?.takeIf { title != it }
val imeta = videoEvent.imetaTags().firstOrNull() ?: return
val title = videoEvent.title()
val summary = videoEvent.content.ifBlank { null }?.takeIf { title != it }
val image = imeta.image.firstOrNull()
val isYouTube = imeta.url.contains("youtube.com") || imeta.url.contains("youtu.be")
val tags = remember(note) { note.event?.tags?.toImmutableListOfLists() ?: EmptyTagList }
val content by
remember(note) {
val description = event.content.ifBlank { null } ?: event.alt()
val description = videoEvent.content.ifBlank { null } ?: event.alt()
val isImage = imeta.mimeType?.startsWith("image/") == true || RichTextParser.isImageUrl(imeta.url)
val uri = note.toNostrUri()
@@ -187,11 +190,11 @@ fun VideoDisplay(
nav = nav,
)
if (event.hasHashtags()) {
if (videoEvent.hasHashtags()) {
Row(
Modifier.fillMaxWidth(),
) {
DisplayUncitedHashtags(event, summary, callbackUri, accountViewModel, nav)
DisplayUncitedHashtags(videoEvent, summary, callbackUri, accountViewModel, nav)
}
}
}

View File

@@ -33,6 +33,7 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip71Video.VideoEvent
@@ -43,8 +44,10 @@ fun JustVideoDisplay(
contentScale: ContentScale,
accountViewModel: AccountViewModel,
) {
val event = (note.event as? VideoEvent) ?: return
val imeta = event.imetaTags().getOrNull(0) ?: return
val videoEvent = (note.event as? VideoEvent) ?: return
val event = (videoEvent as? Event) ?: return
val imeta = videoEvent.imetaTags().getOrNull(0) ?: return
val content by
remember(note) {

View File

@@ -237,6 +237,9 @@ open class BasicRelayClient(
text: String,
onConnected: () -> Unit,
) {
if (text.contains("2e9244f75b36d47116b8c8bd5c4ea4d29fb1a3d64688d2524156af34c6124dc3")) {
Log.d(logTag, "AABBCC Receiving: $text")
}
// Log.d(logTag, "Receiving: $text")
stats.addBytesReceived(text.bytesUsedInMemory())

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2025 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.quartz.nip71Video
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.publishedAt.PublishedAtProvider
import com.vitorpamplona.quartz.nip22Comments.RootScope
import com.vitorpamplona.quartz.nip23LongContent.tags.PublishedAtTag
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
import com.vitorpamplona.quartz.nip71Video.tags.DurationTag
import com.vitorpamplona.quartz.nip71Video.tags.SegmentTag
import com.vitorpamplona.quartz.nip92IMeta.imetas
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@Immutable
abstract class RegularVideoEvent(
id: HexKey,
pubKey: HexKey,
createdAt: Long,
kind: Int,
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, kind, tags, content, sig),
PublishedAtProvider,
VideoEvent,
RootScope {
@Transient var iMetas: List<VideoMeta>? = null
override fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
override fun publishedAt() = tags.firstNotNullOfOrNull(PublishedAtTag::parse)
override fun duration() = tags.firstNotNullOfOrNull(DurationTag::parse)
override fun textTrack() = tags.mapNotNull(ETag::parse)
override fun segments() = tags.mapNotNull(SegmentTag::parse)
override fun participants() = tags.mapNotNull(PTag::parse)
override fun hashtags() = tags.hashtags()
override fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse)
override fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse)
override fun imetaTags() = iMetas ?: imetas().map { VideoMeta.parse(it) }.also { iMetas = it }
}

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2025 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.quartz.nip71Video
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.publishedAt.PublishedAtProvider
import com.vitorpamplona.quartz.nip22Comments.RootScope
import com.vitorpamplona.quartz.nip23LongContent.tags.PublishedAtTag
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
import com.vitorpamplona.quartz.nip71Video.tags.DurationTag
import com.vitorpamplona.quartz.nip71Video.tags.SegmentTag
import com.vitorpamplona.quartz.nip92IMeta.imetas
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@Immutable
abstract class ReplaceableVideoEvent(
id: HexKey,
pubKey: HexKey,
createdAt: Long,
kind: Int,
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseReplaceableEvent(id, pubKey, createdAt, kind, tags, content, sig),
PublishedAtProvider,
VideoEvent,
RootScope {
@Transient var iMetas: List<VideoMeta>? = null
override fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
override fun publishedAt() = tags.firstNotNullOfOrNull(PublishedAtTag::parse)
override fun duration() = tags.firstNotNullOfOrNull(DurationTag::parse)
override fun textTrack() = tags.mapNotNull(ETag::parse)
override fun segments() = tags.mapNotNull(SegmentTag::parse)
override fun participants() = tags.mapNotNull(PTag::parse)
override fun hashtags() = tags.hashtags()
override fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse)
override fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse)
override fun imetaTags() = iMetas ?: imetas().map { VideoMeta.parse(it) }.also { iMetas = it }
}

View File

@@ -21,52 +21,30 @@
package com.vitorpamplona.quartz.nip71Video
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.IEvent
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.publishedAt.PublishedAtProvider
import com.vitorpamplona.quartz.nip22Comments.RootScope
import com.vitorpamplona.quartz.nip23LongContent.tags.PublishedAtTag
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
import com.vitorpamplona.quartz.nip71Video.tags.DurationTag
import com.vitorpamplona.quartz.nip71Video.tags.SegmentTag
import com.vitorpamplona.quartz.nip92IMeta.imetas
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
import com.vitorpamplona.quartz.nip94FileMetadata.tags.MimeTypeTag
@Immutable
abstract class VideoEvent(
id: HexKey,
pubKey: HexKey,
createdAt: Long,
kind: Int,
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig),
PublishedAtProvider,
RootScope {
@Transient var iMetas: List<VideoMeta>? = null
interface VideoEvent : IEvent {
fun title(): String?
fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
fun publishedAt(): Long?
override fun publishedAt() = tags.firstNotNullOfOrNull(PublishedAtTag::parse)
fun duration(): Int?
fun duration() = tags.firstNotNullOfOrNull(DurationTag::parse)
fun textTrack(): List<ETag>
fun textTrack() = tags.mapNotNull(ETag::parse)
fun segments(): List<SegmentTag>
fun segments() = tags.mapNotNull(SegmentTag::parse)
fun participants(): List<PTag>
fun participants() = tags.mapNotNull(PTag::parse)
fun hashtags(): List<String>
fun hashtags() = tags.hashtags()
fun mimeType(): String?
private fun mimeType() = tags.firstNotNullOfOrNull(MimeTypeTag::parse)
fun hash(): String?
private fun hash() = tags.firstNotNullOfOrNull(HashSha256Tag::parse)
fun imetaTags() = iMetas ?: imetas().map { VideoMeta.parse(it) }.also { iMetas = it }
fun imetaTags(): List<VideoMeta>
}

View File

@@ -27,6 +27,7 @@ import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
import com.vitorpamplona.quartz.nip22Comments.RootScope
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip71Video.videoIMetas
import com.vitorpamplona.quartz.utils.TimeUtils
import java.util.UUID
@@ -38,7 +39,7 @@ class VideoHorizontalEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : VideoEvent(id, pubKey, createdAt, KIND, tags, content, sig),
) : ReplaceableVideoEvent(id, pubKey, createdAt, KIND, tags, content, sig),
RootScope {
companion object {
const val KIND = 34235

View File

@@ -24,11 +24,9 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
import com.vitorpamplona.quartz.nip22Comments.RootScope
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.utils.TimeUtils
import java.util.UUID
@Immutable
class VideoNormalEvent(
@@ -38,7 +36,7 @@ class VideoNormalEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : VideoEvent(id, pubKey, createdAt, KIND, tags, content, sig),
) : RegularVideoEvent(id, pubKey, createdAt, KIND, tags, content, sig),
RootScope {
companion object {
const val KIND = 21
@@ -47,10 +45,9 @@ class VideoNormalEvent(
fun build(
video: VideoMeta,
description: String,
dTag: String = UUID.randomUUID().toString(),
createdAt: Long = TimeUtils.now(),
initializer: TagArrayBuilder<VideoNormalEvent>.() -> Unit = {},
) = build(description, dTag, createdAt) {
) = build(description, createdAt) {
videoIMeta(video)
initializer()
}
@@ -58,21 +55,18 @@ class VideoNormalEvent(
fun build(
video: List<VideoMeta>,
description: String,
dTag: String = UUID.randomUUID().toString(),
createdAt: Long = TimeUtils.now(),
initializer: TagArrayBuilder<VideoNormalEvent>.() -> Unit = {},
) = build(description, dTag, createdAt) {
) = build(description, createdAt) {
videoIMetas(video)
initializer()
}
fun build(
description: String,
dTag: String = UUID.randomUUID().toString(),
createdAt: Long = TimeUtils.now(),
initializer: TagArrayBuilder<VideoNormalEvent>.() -> Unit = {},
) = eventTemplate(KIND, description, createdAt) {
dTag(dTag)
alt(ALT_DESCRIPTION)
initializer()
}

View File

@@ -24,11 +24,9 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
import com.vitorpamplona.quartz.nip22Comments.RootScope
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.utils.TimeUtils
import java.util.UUID
@Immutable
class VideoShortEvent(
@@ -38,7 +36,7 @@ class VideoShortEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : VideoEvent(id, pubKey, createdAt, KIND, tags, content, sig),
) : RegularVideoEvent(id, pubKey, createdAt, KIND, tags, content, sig),
RootScope {
companion object {
const val KIND = 22
@@ -47,21 +45,18 @@ class VideoShortEvent(
fun build(
video: VideoMeta,
description: String,
dTag: String = UUID.randomUUID().toString(),
createdAt: Long = TimeUtils.now(),
initializer: TagArrayBuilder<VideoShortEvent>.() -> Unit = {},
) = build(description, dTag, createdAt) {
) = build(description, createdAt) {
videoIMeta(video)
initializer()
}
fun build(
description: String,
dTag: String = UUID.randomUUID().toString(),
createdAt: Long = TimeUtils.now(),
initializer: TagArrayBuilder<VideoShortEvent>.() -> Unit = {},
) = eventTemplate(KIND, description, createdAt) {
dTag(dTag)
alt(ALT_DESCRIPTION)
initializer()
}

View File

@@ -38,7 +38,7 @@ class VideoVerticalEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : VideoEvent(id, pubKey, createdAt, KIND, tags, content, sig),
) : ReplaceableVideoEvent(id, pubKey, createdAt, KIND, tags, content, sig),
RootScope {
companion object {
const val KIND = 34236