Adds view support for YakBak
This commit is contained in:
@@ -179,6 +179,8 @@ import com.vitorpamplona.quartz.nip90Dvms.NIP90UserDiscoveryResponseEvent
|
|||||||
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
|
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
|
||||||
import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent
|
import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent
|
||||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
|
||||||
import com.vitorpamplona.quartz.nipB7Blossom.BlossomServersEvent
|
import com.vitorpamplona.quartz.nipB7Blossom.BlossomServersEvent
|
||||||
import com.vitorpamplona.quartz.utils.Hex
|
import com.vitorpamplona.quartz.utils.Hex
|
||||||
import com.vitorpamplona.quartz.utils.LargeCache
|
import com.vitorpamplona.quartz.utils.LargeCache
|
||||||
@@ -599,6 +601,18 @@ object LocalCache : ILocalCache {
|
|||||||
wasVerified: Boolean,
|
wasVerified: Boolean,
|
||||||
) = consumeRegularEvent(event, relay, wasVerified)
|
) = consumeRegularEvent(event, relay, wasVerified)
|
||||||
|
|
||||||
|
fun consume(
|
||||||
|
event: VoiceEvent,
|
||||||
|
relay: NormalizedRelayUrl?,
|
||||||
|
wasVerified: Boolean,
|
||||||
|
) = consumeRegularEvent(event, relay, wasVerified)
|
||||||
|
|
||||||
|
fun consume(
|
||||||
|
event: VoiceReplyEvent,
|
||||||
|
relay: NormalizedRelayUrl?,
|
||||||
|
wasVerified: Boolean,
|
||||||
|
) = consumeRegularEvent(event, relay, wasVerified)
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
fun consume(
|
fun consume(
|
||||||
event: TorrentCommentEvent,
|
event: TorrentCommentEvent,
|
||||||
@@ -2816,6 +2830,8 @@ object LocalCache : ILocalCache {
|
|||||||
is TrustedRelayListEvent -> consume(event, relay, wasVerified)
|
is TrustedRelayListEvent -> consume(event, relay, wasVerified)
|
||||||
is VideoHorizontalEvent -> consume(event, relay, wasVerified)
|
is VideoHorizontalEvent -> consume(event, relay, wasVerified)
|
||||||
is VideoVerticalEvent -> consume(event, relay, wasVerified)
|
is VideoVerticalEvent -> consume(event, relay, wasVerified)
|
||||||
|
is VoiceEvent -> consume(event, relay, wasVerified)
|
||||||
|
is VoiceReplyEvent -> consume(event, relay, wasVerified)
|
||||||
is WikiNoteEvent -> consume(event, relay, wasVerified)
|
is WikiNoteEvent -> consume(event, relay, wasVerified)
|
||||||
else -> {
|
else -> {
|
||||||
Log.w("Event Not Supported", event.toJson())
|
Log.w("Event Not Supported", event.toJson())
|
||||||
|
|||||||
+1
-2
@@ -40,7 +40,6 @@ import com.vitorpamplona.amethyst.service.playback.composable.controls.RenderCon
|
|||||||
import com.vitorpamplona.amethyst.service.playback.composable.mediaitem.LoadedMediaItem
|
import com.vitorpamplona.amethyst.service.playback.composable.mediaitem.LoadedMediaItem
|
||||||
import com.vitorpamplona.amethyst.service.playback.composable.wavefront.Waveform
|
import com.vitorpamplona.amethyst.service.playback.composable.wavefront.Waveform
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.quartz.experimental.audio.header.tags.WaveformTag
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
@@ -50,7 +49,7 @@ fun RenderVideoPlayer(
|
|||||||
thumbData: VideoThumb?,
|
thumbData: VideoThumb?,
|
||||||
showControls: Boolean = true,
|
showControls: Boolean = true,
|
||||||
contentScale: ContentScale,
|
contentScale: ContentScale,
|
||||||
waveform: WaveformTag? = null,
|
waveform: WaveformData? = null,
|
||||||
borderModifier: Modifier,
|
borderModifier: Modifier,
|
||||||
videoModifier: Modifier,
|
videoModifier: Modifier,
|
||||||
onControllerVisibilityChanged: ((Boolean) -> Unit)? = null,
|
onControllerVisibilityChanged: ((Boolean) -> Unit)? = null,
|
||||||
|
|||||||
+8
-3
@@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -42,9 +43,13 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
|||||||
import com.vitorpamplona.amethyst.ui.theme.Size75dp
|
import com.vitorpamplona.amethyst.ui.theme.Size75dp
|
||||||
import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
||||||
import com.vitorpamplona.amethyst.ui.theme.videoGalleryModifier
|
import com.vitorpamplona.amethyst.ui.theme.videoGalleryModifier
|
||||||
import com.vitorpamplona.quartz.experimental.audio.header.tags.WaveformTag
|
|
||||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
|
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class WaveformData(
|
||||||
|
val wave: List<Int>,
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun VideoView(
|
fun VideoView(
|
||||||
videoUri: String,
|
videoUri: String,
|
||||||
@@ -54,7 +59,7 @@ fun VideoView(
|
|||||||
roundedCorner: Boolean,
|
roundedCorner: Boolean,
|
||||||
gallery: Boolean = false,
|
gallery: Boolean = false,
|
||||||
contentScale: ContentScale,
|
contentScale: ContentScale,
|
||||||
waveform: WaveformTag? = null,
|
waveform: WaveformData? = null,
|
||||||
artworkUri: String? = null,
|
artworkUri: String? = null,
|
||||||
authorName: String? = null,
|
authorName: String? = null,
|
||||||
dimensions: DimensionTag? = null,
|
dimensions: DimensionTag? = null,
|
||||||
@@ -85,7 +90,7 @@ fun VideoView(
|
|||||||
thumb: VideoThumb? = null,
|
thumb: VideoThumb? = null,
|
||||||
borderModifier: Modifier,
|
borderModifier: Modifier,
|
||||||
contentScale: ContentScale,
|
contentScale: ContentScale,
|
||||||
waveform: WaveformTag? = null,
|
waveform: WaveformData? = null,
|
||||||
artworkUri: String? = null,
|
artworkUri: String? = null,
|
||||||
authorName: String? = null,
|
authorName: String? = null,
|
||||||
dimensions: DimensionTag? = null,
|
dimensions: DimensionTag? = null,
|
||||||
|
|||||||
+1
-2
@@ -29,7 +29,6 @@ import androidx.compose.ui.layout.ContentScale
|
|||||||
import com.vitorpamplona.amethyst.service.playback.composable.mainVideo.VideoPlayerActiveMutex
|
import com.vitorpamplona.amethyst.service.playback.composable.mainVideo.VideoPlayerActiveMutex
|
||||||
import com.vitorpamplona.amethyst.service.playback.composable.mediaitem.GetMediaItem
|
import com.vitorpamplona.amethyst.service.playback.composable.mediaitem.GetMediaItem
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.quartz.experimental.audio.header.tags.WaveformTag
|
|
||||||
|
|
||||||
public val DEFAULT_MUTED_SETTING = mutableStateOf(true)
|
public val DEFAULT_MUTED_SETTING = mutableStateOf(true)
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ fun VideoViewInner(
|
|||||||
showControls: Boolean = true,
|
showControls: Boolean = true,
|
||||||
contentScale: ContentScale,
|
contentScale: ContentScale,
|
||||||
borderModifier: Modifier,
|
borderModifier: Modifier,
|
||||||
waveform: WaveformTag? = null,
|
waveform: WaveformData? = null,
|
||||||
artworkUri: String? = null,
|
artworkUri: String? = null,
|
||||||
authorName: String? = null,
|
authorName: String? = null,
|
||||||
nostrUriCallback: String? = null,
|
nostrUriCallback: String? = null,
|
||||||
|
|||||||
+3
-3
@@ -38,15 +38,15 @@ import androidx.media3.common.Player
|
|||||||
import androidx.media3.session.MediaController
|
import androidx.media3.session.MediaController
|
||||||
import com.linc.audiowaveform.infiniteLinearGradient
|
import com.linc.audiowaveform.infiniteLinearGradient
|
||||||
import com.vitorpamplona.amethyst.service.playback.composable.MediaControllerState
|
import com.vitorpamplona.amethyst.service.playback.composable.MediaControllerState
|
||||||
|
import com.vitorpamplona.amethyst.service.playback.composable.WaveformData
|
||||||
import com.vitorpamplona.amethyst.ui.components.AudioWaveformReadOnly
|
import com.vitorpamplona.amethyst.ui.components.AudioWaveformReadOnly
|
||||||
import com.vitorpamplona.quartz.experimental.audio.header.tags.WaveformTag
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.conflate
|
import kotlinx.coroutines.flow.conflate
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Waveform(
|
fun Waveform(
|
||||||
waveform: WaveformTag,
|
waveform: WaveformData,
|
||||||
mediaControllerState: MediaControllerState,
|
mediaControllerState: MediaControllerState,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
) {
|
) {
|
||||||
@@ -94,7 +94,7 @@ private fun pollCurrentDuration(controller: MediaController) =
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DrawWaveform(
|
fun DrawWaveform(
|
||||||
waveform: WaveformTag,
|
waveform: WaveformData,
|
||||||
waveformProgress: MutableFloatState,
|
waveformProgress: MutableFloatState,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* 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.amethyst.ui.note.types
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
|
import com.vitorpamplona.amethyst.service.playback.composable.VideoView
|
||||||
|
import com.vitorpamplona.amethyst.service.playback.composable.WaveformData
|
||||||
|
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags
|
||||||
|
import com.vitorpamplona.quartz.nip14Subject.subject
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.BaseVoiceEvent
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RenderVoiceTrack(
|
||||||
|
note: Note,
|
||||||
|
contentScale: ContentScale,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: INav,
|
||||||
|
) {
|
||||||
|
val noteEvent = note.event as? BaseVoiceEvent ?: return
|
||||||
|
|
||||||
|
AudioVoiceHeader(noteEvent, note, contentScale, accountViewModel, nav)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AudioVoiceHeader(
|
||||||
|
noteEvent: BaseVoiceEvent,
|
||||||
|
note: Note,
|
||||||
|
contentScale: ContentScale,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: INav,
|
||||||
|
) {
|
||||||
|
val waveform =
|
||||||
|
remember(noteEvent) {
|
||||||
|
noteEvent
|
||||||
|
.iMetaTags()
|
||||||
|
.firstOrNull()
|
||||||
|
?.waveform
|
||||||
|
?.let { WaveformData(it) }
|
||||||
|
}
|
||||||
|
val media =
|
||||||
|
remember(noteEvent) {
|
||||||
|
noteEvent.content.ifBlank { null } ?: noteEvent.iMetaTags().firstOrNull()?.url
|
||||||
|
}
|
||||||
|
|
||||||
|
if (media == null) return
|
||||||
|
|
||||||
|
Column(modifier = Modifier.fillMaxWidth().padding(top = 5.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
VideoView(
|
||||||
|
videoUri = media,
|
||||||
|
mimeType = null,
|
||||||
|
waveform = waveform,
|
||||||
|
title = noteEvent.subject(),
|
||||||
|
authorName = note.author?.toBestDisplayName(),
|
||||||
|
roundedCorner = true,
|
||||||
|
contentScale = contentScale,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nostrUriCallback = note.toNostrUri(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val callbackUri = remember(note) { note.toNostrUri() }
|
||||||
|
|
||||||
|
if (noteEvent.hasHashtags()) {
|
||||||
|
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
DisplayUncitedHashtags(noteEvent, callbackUri, accountViewModel, nav)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
@@ -34,6 +34,7 @@ import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
|||||||
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
|
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
||||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
|
||||||
|
|
||||||
class HomeConversationsFeedFilter(
|
class HomeConversationsFeedFilter(
|
||||||
val account: Account,
|
val account: Account,
|
||||||
@@ -79,6 +80,7 @@ class HomeConversationsFeedFilter(
|
|||||||
event is PollNoteEvent ||
|
event is PollNoteEvent ||
|
||||||
event is ChannelMessageEvent ||
|
event is ChannelMessageEvent ||
|
||||||
event is CommentEvent ||
|
event is CommentEvent ||
|
||||||
|
event is VoiceReplyEvent ||
|
||||||
event is LiveActivitiesChatMessageEvent
|
event is LiveActivitiesChatMessageEvent
|
||||||
) &&
|
) &&
|
||||||
filterParams.match(event)
|
filterParams.match(event)
|
||||||
|
|||||||
+2
@@ -40,6 +40,7 @@ import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
|||||||
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
||||||
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
|
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
|
||||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
|
||||||
|
|
||||||
class HomeNewThreadFeedFilter(
|
class HomeNewThreadFeedFilter(
|
||||||
val account: Account,
|
val account: Account,
|
||||||
@@ -100,6 +101,7 @@ class HomeNewThreadFeedFilter(
|
|||||||
noteEvent is InteractiveStoryPrologueEvent ||
|
noteEvent is InteractiveStoryPrologueEvent ||
|
||||||
noteEvent is CommentEvent ||
|
noteEvent is CommentEvent ||
|
||||||
noteEvent is AudioTrackEvent ||
|
noteEvent is AudioTrackEvent ||
|
||||||
|
noteEvent is VoiceEvent ||
|
||||||
noteEvent is AudioHeaderEvent
|
noteEvent is AudioHeaderEvent
|
||||||
) &&
|
) &&
|
||||||
filterParams.match(noteEvent, it.relays) &&
|
filterParams.match(noteEvent, it.relays) &&
|
||||||
|
|||||||
+4
@@ -35,6 +35,8 @@ import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
|||||||
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
||||||
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
|
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
|
||||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
|
||||||
import kotlin.collections.flatten
|
import kotlin.collections.flatten
|
||||||
|
|
||||||
val HomePostsByGeohashKinds =
|
val HomePostsByGeohashKinds =
|
||||||
@@ -48,6 +50,8 @@ val HomePostsByGeohashKinds =
|
|||||||
InteractiveStoryPrologueEvent.KIND,
|
InteractiveStoryPrologueEvent.KIND,
|
||||||
WikiNoteEvent.KIND,
|
WikiNoteEvent.KIND,
|
||||||
CommentEvent.KIND,
|
CommentEvent.KIND,
|
||||||
|
VoiceEvent.KIND,
|
||||||
|
VoiceReplyEvent.KIND,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun filterHomePostsByGeohashes(
|
fun filterHomePostsByGeohashes(
|
||||||
|
|||||||
+4
@@ -37,6 +37,8 @@ import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
|||||||
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
||||||
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
|
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
|
||||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
|
||||||
|
|
||||||
val HomePostsBuHashtagsKinds =
|
val HomePostsBuHashtagsKinds =
|
||||||
listOf(
|
listOf(
|
||||||
@@ -50,6 +52,8 @@ val HomePostsBuHashtagsKinds =
|
|||||||
InteractiveStoryPrologueEvent.KIND,
|
InteractiveStoryPrologueEvent.KIND,
|
||||||
CommentEvent.KIND,
|
CommentEvent.KIND,
|
||||||
WikiNoteEvent.KIND,
|
WikiNoteEvent.KIND,
|
||||||
|
VoiceEvent.KIND,
|
||||||
|
VoiceReplyEvent.KIND,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun filterHomePostsByHashtags(
|
fun filterHomePostsByHashtags(
|
||||||
|
|||||||
+4
@@ -40,6 +40,8 @@ import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEven
|
|||||||
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
||||||
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
|
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
|
||||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
val HomePostsNewThreadKinds =
|
val HomePostsNewThreadKinds =
|
||||||
@@ -61,6 +63,8 @@ val HomePostsConversationKinds =
|
|||||||
CommentEvent.KIND,
|
CommentEvent.KIND,
|
||||||
LiveActivitiesEvent.KIND,
|
LiveActivitiesEvent.KIND,
|
||||||
EphemeralChatEvent.KIND,
|
EphemeralChatEvent.KIND,
|
||||||
|
VoiceEvent.KIND,
|
||||||
|
VoiceReplyEvent.KIND,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun filterNewHomePostsByAuthors(
|
fun filterNewHomePostsByAuthors(
|
||||||
|
|||||||
+3
@@ -229,6 +229,7 @@ import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
|
|||||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||||
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
|
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
|
||||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.BaseVoiceEvent
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
@@ -547,6 +548,8 @@ private fun FullBleedNoteCompose(
|
|||||||
VideoDisplay(baseNote, makeItShort = false, canPreview = true, backgroundColor = backgroundColor, ContentScale.FillWidth, accountViewModel = accountViewModel, nav = nav)
|
VideoDisplay(baseNote, makeItShort = false, canPreview = true, backgroundColor = backgroundColor, ContentScale.FillWidth, accountViewModel = accountViewModel, nav = nav)
|
||||||
} else if (noteEvent is PictureEvent) {
|
} else if (noteEvent is PictureEvent) {
|
||||||
PictureDisplay(baseNote, roundedCorner = true, ContentScale.FillWidth, PaddingValues(vertical = Size5dp), backgroundColor, accountViewModel = accountViewModel, nav)
|
PictureDisplay(baseNote, roundedCorner = true, ContentScale.FillWidth, PaddingValues(vertical = Size5dp), backgroundColor, accountViewModel = accountViewModel, nav)
|
||||||
|
} else if (noteEvent is BaseVoiceEvent) {
|
||||||
|
VoiceEventDisplay(baseNote, makeItShort = false, canPreview = true, backgroundColor = backgroundColor, ContentScale.FillWidth, accountViewModel = accountViewModel, nav = nav)
|
||||||
} else if (noteEvent is FileHeaderEvent) {
|
} else if (noteEvent is FileHeaderEvent) {
|
||||||
FileHeaderDisplay(baseNote, roundedCorner = true, ContentScale.FillWidth, accountViewModel = accountViewModel)
|
FileHeaderDisplay(baseNote, roundedCorner = true, ContentScale.FillWidth, accountViewModel = accountViewModel)
|
||||||
} else if (noteEvent is FileStorageHeaderEvent) {
|
} else if (noteEvent is FileStorageHeaderEvent) {
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
|
|||||||
import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent
|
import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent
|
||||||
import com.vitorpamplona.quartz.nip98HttpAuth.HTTPAuthorizationEvent
|
import com.vitorpamplona.quartz.nip98HttpAuth.HTTPAuthorizationEvent
|
||||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
|
||||||
import com.vitorpamplona.quartz.nipB7Blossom.BlossomAuthorizationEvent
|
import com.vitorpamplona.quartz.nipB7Blossom.BlossomAuthorizationEvent
|
||||||
import com.vitorpamplona.quartz.nipB7Blossom.BlossomServersEvent
|
import com.vitorpamplona.quartz.nipB7Blossom.BlossomServersEvent
|
||||||
|
|
||||||
@@ -282,6 +284,8 @@ class EventFactory {
|
|||||||
TrustedRelayListEvent.KIND -> TrustedRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
TrustedRelayListEvent.KIND -> TrustedRelayListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
VideoHorizontalEvent.KIND -> VideoHorizontalEvent(id, pubKey, createdAt, tags, content, sig)
|
VideoHorizontalEvent.KIND -> VideoHorizontalEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
VideoVerticalEvent.KIND -> VideoVerticalEvent(id, pubKey, createdAt, tags, content, sig)
|
VideoVerticalEvent.KIND -> VideoVerticalEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
VoiceEvent.KIND -> VoiceEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
VoiceReplyEvent.KIND -> VoiceReplyEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
WikiNoteEvent.KIND -> WikiNoteEvent(id, pubKey, createdAt, tags, content, sig)
|
WikiNoteEvent.KIND -> WikiNoteEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
else -> {
|
else -> {
|
||||||
factories[kind]?.build(id, pubKey, createdAt, tags, content, sig)
|
factories[kind]?.build(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* 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.nipA0VoiceMessages
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip92IMeta.IMetaTag
|
||||||
|
import com.vitorpamplona.quartz.nip92IMeta.IMetaTagBuilder
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.tags.WaveformTag
|
||||||
|
|
||||||
|
data class AudioMeta(
|
||||||
|
val url: String,
|
||||||
|
val hash: String? = null,
|
||||||
|
val duration: Int? = null,
|
||||||
|
val waveform: List<Int>? = null,
|
||||||
|
) {
|
||||||
|
fun toIMetaArray(): Array<String> =
|
||||||
|
IMetaTagBuilder(url)
|
||||||
|
.apply {
|
||||||
|
hash?.let { hash(it) }
|
||||||
|
duration?.let { duration(it) }
|
||||||
|
waveform?.let { waveform(it) }
|
||||||
|
}.build()
|
||||||
|
.toTagArray()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun parse(iMeta: IMetaTag): AudioMeta =
|
||||||
|
AudioMeta(
|
||||||
|
iMeta.url,
|
||||||
|
iMeta.hash()?.firstOrNull(),
|
||||||
|
iMeta.duration()?.firstOrNull()?.toIntOrNull(),
|
||||||
|
iMeta.waveform()?.firstOrNull()?.let { WaveformTag.parseWave(it) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* 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.nipA0VoiceMessages
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
|
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.nip31Alts.alt
|
||||||
|
import com.vitorpamplona.quartz.nip92IMeta.imetas
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
open class BaseVoiceEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
kind: Int,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||||
|
fun iMetaTags() = imetas().map { AudioMeta.parse(it) }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun <T : BaseVoiceEvent> build(
|
||||||
|
voiceMessage: AudioMeta,
|
||||||
|
kind: Int,
|
||||||
|
alt: String,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
initializer: TagArrayBuilder<T>.() -> Unit = {},
|
||||||
|
) = eventTemplate(kind, voiceMessage.url, createdAt) {
|
||||||
|
alt(alt)
|
||||||
|
initializer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* 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.nipA0VoiceMessages
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip92IMeta.IMetaTagBuilder
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.tags.DurationTag
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.tags.HashSha256Tag
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.tags.WaveformTag
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the IMeta tags that are used by Picture events.
|
||||||
|
*/
|
||||||
|
fun IMetaTagBuilder.hash(hash: HexKey) = add(HashSha256Tag.TAG_NAME, hash)
|
||||||
|
|
||||||
|
fun IMetaTagBuilder.duration(size: Int) = add(DurationTag.TAG_NAME, size.toString())
|
||||||
|
|
||||||
|
fun IMetaTagBuilder.waveform(wave: List<Int>) = add(WaveformTag.TAG_NAME, WaveformTag.assembleWave(wave))
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* 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.nipA0VoiceMessages
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip92IMeta.IMetaTag
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.tags.DurationTag
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.tags.HashSha256Tag
|
||||||
|
import com.vitorpamplona.quartz.nipA0VoiceMessages.tags.WaveformTag
|
||||||
|
|
||||||
|
fun IMetaTag.hash() = properties.get(HashSha256Tag.TAG_NAME)
|
||||||
|
|
||||||
|
fun IMetaTag.duration() = properties.get(DurationTag.TAG_NAME)
|
||||||
|
|
||||||
|
fun IMetaTag.waveform() = properties.get(WaveformTag.TAG_NAME)
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* 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.nipA0VoiceMessages
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.nip94FileMetadata.tags.HashSha256Tag
|
||||||
|
|
||||||
|
fun <T : BaseVoiceEvent> TagArrayBuilder<T>.audioIMeta(
|
||||||
|
url: String,
|
||||||
|
hash: String? = null,
|
||||||
|
duration: Int? = null,
|
||||||
|
waveform: List<Int>? = null,
|
||||||
|
) = audioIMeta(AudioMeta(url, hash, duration, waveform))
|
||||||
|
|
||||||
|
fun <T : BaseVoiceEvent> TagArrayBuilder<T>.audioIMeta(imeta: AudioMeta): TagArrayBuilder<T> {
|
||||||
|
add(imeta.toIMetaArray())
|
||||||
|
imeta.hash?.let { add(HashSha256Tag.assemble(it)) }
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : BaseVoiceEvent> TagArrayBuilder<T>.audioIMeta(audioUrls: List<AudioMeta>): TagArrayBuilder<T> {
|
||||||
|
audioUrls.forEach { audioIMeta(it) }
|
||||||
|
return this
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* 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.nipA0VoiceMessages
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class VoiceEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : BaseVoiceEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
|
companion object {
|
||||||
|
const val KIND = 1222
|
||||||
|
const val ALT_DESCRIPTION = "Voice message"
|
||||||
|
|
||||||
|
fun build(
|
||||||
|
voiceMessage: AudioMeta,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
initializer: TagArrayBuilder<VoiceEvent>.() -> Unit = {},
|
||||||
|
) = build(voiceMessage, KIND, ALT_DESCRIPTION, createdAt, initializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* 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.nipA0VoiceMessages
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
class VoiceReplyEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: Array<Array<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey,
|
||||||
|
) : BaseVoiceEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
|
companion object {
|
||||||
|
const val KIND = 1244
|
||||||
|
const val ALT_DESCRIPTION = "Voice reply"
|
||||||
|
|
||||||
|
fun build(
|
||||||
|
voiceMessage: AudioMeta,
|
||||||
|
createdAt: Long = TimeUtils.now(),
|
||||||
|
initializer: TagArrayBuilder<VoiceReplyEvent>.() -> Unit = {},
|
||||||
|
) = build(voiceMessage, KIND, ALT_DESCRIPTION, createdAt, initializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* 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.nipA0VoiceMessages.tags
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||||
|
import com.vitorpamplona.quartz.utils.ensure
|
||||||
|
|
||||||
|
class DurationTag {
|
||||||
|
companion object {
|
||||||
|
const val TAG_NAME = "duration"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun parse(tag: Array<String>): Int? {
|
||||||
|
ensure(tag.has(1)) { return null }
|
||||||
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
|
ensure(tag[1].isNotEmpty()) { return null }
|
||||||
|
return tag[1].toIntOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun assemble(seconds: Int) = arrayOf(TAG_NAME, seconds.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* 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.nipA0VoiceMessages.tags
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||||
|
import com.vitorpamplona.quartz.utils.ensure
|
||||||
|
|
||||||
|
class HashSha256Tag {
|
||||||
|
companion object {
|
||||||
|
const val TAG_NAME = "sha256"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun parse(tag: Array<String>): String? {
|
||||||
|
ensure(tag.has(1)) { return null }
|
||||||
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
|
ensure(tag[1].isNotEmpty()) { return null }
|
||||||
|
return tag[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun assemble(hash: String) = arrayOf(TAG_NAME, hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* 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.nipA0VoiceMessages.tags
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||||
|
import com.vitorpamplona.quartz.utils.ensure
|
||||||
|
|
||||||
|
class WaveformTag(
|
||||||
|
val wave: List<Int>,
|
||||||
|
) {
|
||||||
|
fun toTagArray() = assemble(wave)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG_NAME = "waveform"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun parse(tag: Array<String>): WaveformTag? = parseWave(tag)?.let { WaveformTag(it) }
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun parseWave(tag: Array<String>): List<Int>? {
|
||||||
|
ensure(tag.has(1)) { return null }
|
||||||
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
|
ensure(tag[1].isNotEmpty()) { return null }
|
||||||
|
|
||||||
|
val wave = tag[1].split(" ").mapNotNull { it.toIntOrNull() }
|
||||||
|
if (wave.isEmpty()) return null
|
||||||
|
return wave
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseWave(wave: String): List<Int>? {
|
||||||
|
val wave = wave.split(" ").mapNotNull { it.toIntOrNull() }
|
||||||
|
if (wave.isEmpty()) return null
|
||||||
|
return wave
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun assembleWave(wave: List<Int>) = wave.joinToString(" ")
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun assemble(wave: List<Int>) = arrayOf(TAG_NAME, assembleWave(wave))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user