- Adds support for multiple media uploads at the same time.
- Adds support to display PictureEvents with multiple images at the same time - Improves Uploading feedback for the NewPost screen - 10x better performance on Blurhash generation - Fixes cosine caching on Blurhash - Removes troublesome dependency on blurhash encoder liberary - Restructures contentScale for Images and Video dialogs - Refactors Media Uploaders to improve code reuse - Refactors iMeta usage on Quartz to move away from NIP-94
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2024 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.encoders
|
||||
|
||||
class Dimension(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
) {
|
||||
fun aspectRatio() = width.toFloat() / height.toFloat()
|
||||
|
||||
fun hasSize() = width > 0 && height > 0
|
||||
|
||||
override fun toString() = "${width}x$height"
|
||||
|
||||
companion object {
|
||||
fun parse(dim: String): Dimension? {
|
||||
if (dim == "0x0") return null
|
||||
|
||||
val parts = dim.split("x")
|
||||
if (parts.size != 2) return null
|
||||
|
||||
return try {
|
||||
val width = parts[0].toInt()
|
||||
val height = parts[1].toInt()
|
||||
|
||||
Dimension(width, height)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,43 +20,36 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.encoders
|
||||
|
||||
import com.vitorpamplona.quartz.events.FileHeaderEvent
|
||||
import java.net.URI
|
||||
import java.net.URLDecoder
|
||||
import java.net.URLEncoder
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
class Nip54InlineMetadata {
|
||||
fun convertFromFileHeader(header: FileHeaderEvent): String? {
|
||||
val myUrl = header.url() ?: return null
|
||||
return createUrl(
|
||||
myUrl,
|
||||
header.tags,
|
||||
fun createUrl(header: IMetaTag): String =
|
||||
createUrl(
|
||||
header.url,
|
||||
header.properties,
|
||||
)
|
||||
}
|
||||
|
||||
fun createUrl(
|
||||
imageUrl: String,
|
||||
tags: Array<Array<String>>,
|
||||
url: String,
|
||||
tags: Map<String, String>,
|
||||
): String {
|
||||
val extension =
|
||||
tags
|
||||
.mapNotNull {
|
||||
if (it.isNotEmpty() && it[0] != "url") {
|
||||
if (it.size > 1) {
|
||||
"${it[0]}=${URLEncoder.encode(it[1], "utf-8")}"
|
||||
} else {
|
||||
"${it[0]}}="
|
||||
}
|
||||
if (it.key != "url") {
|
||||
"${it.key}=${URLEncoder.encode(it.value, "utf-8")}"
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}.joinToString("&")
|
||||
|
||||
return if (imageUrl.contains("#")) {
|
||||
"$imageUrl&$extension"
|
||||
return if (url.contains("#")) {
|
||||
"$url&$extension"
|
||||
} else {
|
||||
"$imageUrl#$extension"
|
||||
"$url#$extension"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,65 +20,108 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.encoders
|
||||
|
||||
import com.vitorpamplona.quartz.events.FileHeaderEvent
|
||||
import com.vitorpamplona.quartz.events.FileHeaderEvent.Companion.ALT
|
||||
import com.vitorpamplona.quartz.events.FileHeaderEvent.Companion.BLUR_HASH
|
||||
import com.vitorpamplona.quartz.events.FileHeaderEvent.Companion.DIMENSION
|
||||
import com.vitorpamplona.quartz.events.FileHeaderEvent.Companion.FILE_SIZE
|
||||
import com.vitorpamplona.quartz.events.FileHeaderEvent.Companion.HASH
|
||||
import com.vitorpamplona.quartz.events.FileHeaderEvent.Companion.MAGNET_URI
|
||||
import com.vitorpamplona.quartz.events.FileHeaderEvent.Companion.MIME_TYPE
|
||||
import com.vitorpamplona.quartz.events.FileHeaderEvent.Companion.ORIGINAL_HASH
|
||||
import com.vitorpamplona.quartz.events.FileHeaderEvent.Companion.TORRENT_INFOHASH
|
||||
|
||||
class IMetaTag(
|
||||
val url: String,
|
||||
val properties: Map<String, String>,
|
||||
)
|
||||
|
||||
class IMetaTagBuilder(
|
||||
val url: String,
|
||||
) {
|
||||
val properties = mutableMapOf<String, String>()
|
||||
|
||||
fun add(
|
||||
key: String,
|
||||
value: String,
|
||||
): IMetaTagBuilder {
|
||||
properties.set(key, value)
|
||||
return this
|
||||
}
|
||||
|
||||
fun magnet(uri: String) = add(MAGNET_URI, uri)
|
||||
|
||||
fun mimeType(mime: String) = add(MIME_TYPE, mime)
|
||||
|
||||
fun alt(alt: String) = add(ALT, alt)
|
||||
|
||||
fun hash(hash: HexKey) = add(HASH, hash)
|
||||
|
||||
fun size(size: Int) = add(FILE_SIZE, size.toString())
|
||||
|
||||
fun dims(dims: Dimension) = add(DIMENSION, dims.toString())
|
||||
|
||||
fun blurhash(blurhash: String) = add(BLUR_HASH, blurhash)
|
||||
|
||||
fun originalHash(originalHash: String) = add(ORIGINAL_HASH, originalHash)
|
||||
|
||||
fun torrent(uri: String) = add(TORRENT_INFOHASH, uri)
|
||||
|
||||
fun sensitiveContent(reason: String) = add("content-warning", reason)
|
||||
|
||||
fun build() = IMetaTag(url, properties)
|
||||
}
|
||||
|
||||
class Nip92MediaAttachments {
|
||||
companion object {
|
||||
const val IMETA = "imeta"
|
||||
}
|
||||
|
||||
fun convertFromFileHeader(header: FileHeaderEvent): Array<String>? {
|
||||
val myUrl = header.url() ?: return null
|
||||
return createTag(
|
||||
myUrl,
|
||||
header.tags,
|
||||
)
|
||||
}
|
||||
fun createTag(header: IMetaTag): Array<String> =
|
||||
createTag(
|
||||
header.url,
|
||||
header.properties,
|
||||
)
|
||||
|
||||
fun createTag(
|
||||
imageUrl: String,
|
||||
tags: Array<Array<String>>,
|
||||
): Array<String> =
|
||||
arrayOf(
|
||||
IMETA,
|
||||
"url $imageUrl",
|
||||
) +
|
||||
tags.mapNotNull {
|
||||
if (it.isNotEmpty() && it[0] != "url") {
|
||||
if (it.size > 1) {
|
||||
"${it[0]} ${it[1]}"
|
||||
fun createTag(
|
||||
url: String,
|
||||
tags: Map<String, String>,
|
||||
): Array<String> =
|
||||
arrayOf(
|
||||
IMETA,
|
||||
"url $url",
|
||||
) +
|
||||
tags.mapNotNull {
|
||||
if (it.key != "url") {
|
||||
"${it.key} ${it.value}"
|
||||
} else {
|
||||
"${it[0]}}"
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
fun parse(
|
||||
url: String,
|
||||
tags: Array<Array<String>>,
|
||||
): Map<String, String> =
|
||||
tags
|
||||
.firstOrNull {
|
||||
it.size > 1 && it[0] == IMETA && it[1] == "url $url"
|
||||
}?.let { tagList ->
|
||||
parseIMeta(tagList)
|
||||
} ?: emptyMap()
|
||||
|
||||
fun parse(tags: Array<Array<String>>): Map<String, Map<String, String>> =
|
||||
tags.filter { it.size > 1 && it[0] == IMETA }.associate {
|
||||
val allTags = parseIMeta(it)
|
||||
(allTags.get("url") ?: "") to allTags
|
||||
}
|
||||
|
||||
private fun parseIMeta(tags: Array<String>): Map<String, String> =
|
||||
tags.associate { tag ->
|
||||
val parts = tag.split(" ", limit = 2)
|
||||
when (parts.size) {
|
||||
2 -> parts[0] to parts[1]
|
||||
1 -> parts[0] to ""
|
||||
else -> "" to ""
|
||||
}
|
||||
}
|
||||
|
||||
fun parse(
|
||||
imageUrl: String,
|
||||
tags: Array<Array<String>>,
|
||||
): Map<String, String> =
|
||||
tags
|
||||
.firstOrNull {
|
||||
it.size > 1 && it[0] == IMETA && it[1] == "url $imageUrl"
|
||||
}?.let { tagList ->
|
||||
parseIMeta(tagList)
|
||||
} ?: emptyMap()
|
||||
|
||||
fun parse(tags: Array<Array<String>>): Map<String, Map<String, String>> =
|
||||
tags.filter { it.size > 1 && it[0] == IMETA }.associate {
|
||||
val allTags = parseIMeta(it)
|
||||
(allTags.get("url") ?: "") to allTags
|
||||
}
|
||||
|
||||
fun parseIMeta(tags: Array<String>): Map<String, String> =
|
||||
tags.associate { tag ->
|
||||
val parts = tag.split(" ", limit = 2)
|
||||
when (parts.size) {
|
||||
2 -> parts[0] to parts[1]
|
||||
1 -> parts[0] to ""
|
||||
else -> "" to ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
@@ -62,7 +63,7 @@ class ChannelMessageEvent(
|
||||
markAsSensitive: Boolean,
|
||||
zapRaiserAmount: Long?,
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
isDraft: Boolean,
|
||||
onReady: (ChannelMessageEvent) -> Unit,
|
||||
) {
|
||||
@@ -80,12 +81,8 @@ class ChannelMessageEvent(
|
||||
}
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
nip94attachments?.let {
|
||||
it.forEach {
|
||||
Nip92MediaAttachments().convertFromFileHeader(it)?.let {
|
||||
tags.add(it)
|
||||
}
|
||||
}
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
tags.add(
|
||||
arrayOf("alt", ALT),
|
||||
|
||||
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.events
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
@@ -79,7 +80,7 @@ class ChatMessageEvent(
|
||||
geohash: String? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
isDraft: Boolean,
|
||||
onReady: (ChatMessageEvent) -> Unit,
|
||||
) {
|
||||
@@ -96,12 +97,8 @@ class ChatMessageEvent(
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
subject?.let { tags.add(arrayOf("subject", it)) }
|
||||
nip94attachments?.let {
|
||||
it.forEach {
|
||||
Nip92MediaAttachments().convertFromFileHeader(it)?.let {
|
||||
tags.add(it)
|
||||
}
|
||||
}
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
// tags.add(arrayOf("alt", alt))
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ package com.vitorpamplona.quartz.events
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@@ -112,7 +114,7 @@ class ClassifiedsEvent(
|
||||
markAsSensitive: Boolean,
|
||||
zapRaiserAmount: Long?,
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<Event>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
isDraft: Boolean,
|
||||
@@ -188,10 +190,8 @@ class ClassifiedsEvent(
|
||||
}
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
nip94attachments?.let {
|
||||
it.forEach {
|
||||
// tags.add(arrayOf("nip94", it.toJson()))
|
||||
}
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
tags.add(arrayOf("alt", ALT))
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.ETag
|
||||
import com.vitorpamplona.quartz.encoders.EventHint
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments
|
||||
import com.vitorpamplona.quartz.encoders.PTag
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
@@ -96,7 +97,7 @@ class CommentEvent(
|
||||
usersMentioned: Set<PTag> = emptySet(),
|
||||
addressesMentioned: Set<ATag> = emptySet(),
|
||||
eventsMentioned: Set<ETag> = emptySet(),
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
geohash: String? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
@@ -119,7 +120,7 @@ class CommentEvent(
|
||||
tags.add(removeTrailingNullsAndEmptyOthers("e", replyingTo.event.id, replyingTo.relay, replyingTo.event.pubKey))
|
||||
tags.add(arrayOf("k", "${replyingTo.event.kind}"))
|
||||
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, nip94attachments, geohash, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, imetas, geohash, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
}
|
||||
|
||||
fun replyComment(
|
||||
@@ -128,7 +129,7 @@ class CommentEvent(
|
||||
usersMentioned: Set<PTag> = emptySet(),
|
||||
addressesMentioned: Set<ATag> = emptySet(),
|
||||
eventsMentioned: Set<ETag> = emptySet(),
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
geohash: String? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
@@ -146,7 +147,7 @@ class CommentEvent(
|
||||
tags.add(removeTrailingNullsAndEmptyOthers("e", replyingTo.event.id, replyingTo.relay, replyingTo.event.pubKey))
|
||||
tags.add(arrayOf("k", "${replyingTo.event.kind}"))
|
||||
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, nip94attachments, geohash, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, imetas, geohash, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
}
|
||||
|
||||
fun createGeoComment(
|
||||
@@ -155,7 +156,7 @@ class CommentEvent(
|
||||
usersMentioned: Set<PTag> = emptySet(),
|
||||
addressesMentioned: Set<ATag> = emptySet(),
|
||||
eventsMentioned: Set<ETag> = emptySet(),
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
@@ -168,7 +169,7 @@ class CommentEvent(
|
||||
geohash?.let { tags.addAll(rootGeohashMipMap(it)) }
|
||||
tags.add(arrayOf("K", "geo"))
|
||||
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, nip94attachments, null, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
create(msg, tags, usersMentioned, addressesMentioned, eventsMentioned, imetas, null, zapReceiver, markAsSensitive, zapRaiserAmount, isDraft, signer, createdAt, onReady)
|
||||
}
|
||||
|
||||
private fun create(
|
||||
@@ -177,7 +178,7 @@ class CommentEvent(
|
||||
usersMentioned: Set<PTag> = emptySet(),
|
||||
addressesMentioned: Set<ATag> = emptySet(),
|
||||
eventsMentioned: Set<ETag> = emptySet(),
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
geohash: String? = null,
|
||||
zapReceiver: List<ZapSplitSetup>? = null,
|
||||
markAsSensitive: Boolean = false,
|
||||
@@ -209,12 +210,8 @@ class CommentEvent(
|
||||
}
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
nip94attachments?.let {
|
||||
it.forEach {
|
||||
Nip92MediaAttachments().convertFromFileHeader(it)?.let {
|
||||
tags.add(it)
|
||||
}
|
||||
}
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
|
||||
if (isDraft) {
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.quartz.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.Dimension
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
@@ -74,6 +75,41 @@ class FileHeaderEvent(
|
||||
const val ORIGINAL_HASH = "ox"
|
||||
const val ALT = "alt"
|
||||
|
||||
fun buildTags(
|
||||
url: String,
|
||||
magnetUri: String? = null,
|
||||
mimeType: String? = null,
|
||||
alt: String? = null,
|
||||
hash: String? = null,
|
||||
size: String? = null,
|
||||
dimensions: Dimension? = null,
|
||||
blurhash: String? = null,
|
||||
originalHash: String? = null,
|
||||
magnetURI: String? = null,
|
||||
torrentInfoHash: String? = null,
|
||||
sensitiveContent: Boolean? = null,
|
||||
): Array<Array<String>> =
|
||||
listOfNotNull(
|
||||
arrayOf(URL, url),
|
||||
magnetUri?.let { arrayOf(MAGNET_URI, it) },
|
||||
mimeType?.let { arrayOf(MIME_TYPE, it) },
|
||||
alt?.ifBlank { null }?.let { arrayOf(ALT, it) } ?: arrayOf("alt", ALT_DESCRIPTION),
|
||||
hash?.let { arrayOf(HASH, it) },
|
||||
size?.let { arrayOf(FILE_SIZE, it) },
|
||||
dimensions?.let { arrayOf(DIMENSION, it.toString()) },
|
||||
blurhash?.let { arrayOf(BLUR_HASH, it) },
|
||||
originalHash?.let { arrayOf(ORIGINAL_HASH, it) },
|
||||
magnetURI?.let { arrayOf(MAGNET_URI, it) },
|
||||
torrentInfoHash?.let { arrayOf(TORRENT_INFOHASH, it) },
|
||||
sensitiveContent?.let {
|
||||
if (it) {
|
||||
arrayOf("content-warning", "")
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
).toTypedArray()
|
||||
|
||||
fun create(
|
||||
url: String,
|
||||
magnetUri: String? = null,
|
||||
@@ -91,30 +127,10 @@ class FileHeaderEvent(
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
onReady: (FileHeaderEvent) -> Unit,
|
||||
) {
|
||||
val tags =
|
||||
listOfNotNull(
|
||||
arrayOf(URL, url),
|
||||
magnetUri?.let { arrayOf(MAGNET_URI, it) },
|
||||
mimeType?.let { arrayOf(MIME_TYPE, it) },
|
||||
alt?.ifBlank { null }?.let { arrayOf(ALT, it) } ?: arrayOf("alt", ALT_DESCRIPTION),
|
||||
hash?.let { arrayOf(HASH, it) },
|
||||
size?.let { arrayOf(FILE_SIZE, it) },
|
||||
dimensions?.let { arrayOf(DIMENSION, it.toString()) },
|
||||
blurhash?.let { arrayOf(BLUR_HASH, it) },
|
||||
originalHash?.let { arrayOf(ORIGINAL_HASH, it) },
|
||||
magnetURI?.let { arrayOf(MAGNET_URI, it) },
|
||||
torrentInfoHash?.let { arrayOf(TORRENT_INFOHASH, it) },
|
||||
sensitiveContent?.let {
|
||||
if (it) {
|
||||
arrayOf("content-warning", "")
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
)
|
||||
val tags = buildTags(url, magnetUri, mimeType, alt, hash, size, dimensions, blurhash, originalHash, magnetURI, torrentInfoHash, sensitiveContent)
|
||||
|
||||
val content = alt ?: ""
|
||||
signer.sign(createdAt, KIND, tags.toTypedArray(), content, onReady)
|
||||
signer.sign(createdAt, KIND, tags, content, onReady)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.quartz.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.Dimension
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.events
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
@@ -90,7 +91,7 @@ class GitReplyEvent(
|
||||
root: String? = null,
|
||||
directMentions: Set<HexKey> = emptySet(),
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
forkedFrom: Event? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
@@ -148,12 +149,8 @@ class GitReplyEvent(
|
||||
}
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
nip94attachments?.let {
|
||||
it.forEach {
|
||||
Nip92MediaAttachments().convertFromFileHeader(it)?.let {
|
||||
tags.add(it)
|
||||
}
|
||||
}
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
tags.add(arrayOf("alt", "a git issue reply"))
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.events
|
||||
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments
|
||||
|
||||
open class InteractiveStoryBaseEvent(
|
||||
@@ -51,7 +52,7 @@ open class InteractiveStoryBaseEvent(
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
): Array<Array<String>> {
|
||||
val tags = mutableListOf<Array<String>>()
|
||||
findHashtags(content).forEach {
|
||||
@@ -71,12 +72,8 @@ open class InteractiveStoryBaseEvent(
|
||||
}
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
nip94attachments?.let {
|
||||
it.forEach {
|
||||
Nip92MediaAttachments().convertFromFileHeader(it)?.let {
|
||||
tags.add(it)
|
||||
}
|
||||
}
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
return tags.toTypedArray()
|
||||
}
|
||||
|
||||
+3
-2
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.events
|
||||
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@@ -59,7 +60,7 @@ class InteractiveStoryPrologueEvent(
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
isDraft: Boolean,
|
||||
@@ -67,7 +68,7 @@ class InteractiveStoryPrologueEvent(
|
||||
) {
|
||||
val tags =
|
||||
makeTags(baseId, ALT + title, title, summary, image, options) +
|
||||
generalTags(content, zapReceiver, markAsSensitive, zapRaiserAmount, geohash, nip94attachments)
|
||||
generalTags(content, zapReceiver, markAsSensitive, zapRaiserAmount, geohash, imetas)
|
||||
|
||||
if (isDraft) {
|
||||
signer.assembleRumor(createdAt, KIND, tags, content, onReady)
|
||||
|
||||
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.events
|
||||
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@@ -57,7 +58,7 @@ class InteractiveStorySceneEvent(
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
isDraft: Boolean,
|
||||
@@ -65,7 +66,7 @@ class InteractiveStorySceneEvent(
|
||||
) {
|
||||
val tags =
|
||||
makeTags(baseId, ALT + title, title, options = options) +
|
||||
generalTags(content, zapReceiver, markAsSensitive, zapRaiserAmount, geohash, nip94attachments)
|
||||
generalTags(content, zapReceiver, markAsSensitive, zapRaiserAmount, geohash, imetas)
|
||||
|
||||
if (isDraft) {
|
||||
signer.assembleRumor(createdAt, KIND, tags, content, onReady)
|
||||
|
||||
+4
-7
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.events
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
@@ -73,7 +74,7 @@ class LiveActivitiesChatMessageEvent(
|
||||
markAsSensitive: Boolean,
|
||||
zapRaiserAmount: Long?,
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
isDraft: Boolean,
|
||||
onReady: (LiveActivitiesChatMessageEvent) -> Unit,
|
||||
) {
|
||||
@@ -92,12 +93,8 @@ class LiveActivitiesChatMessageEvent(
|
||||
}
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
nip94attachments?.let {
|
||||
it.forEach {
|
||||
Nip92MediaAttachments().convertFromFileHeader(it)?.let {
|
||||
tags.add(it)
|
||||
}
|
||||
}
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
tags.add(arrayOf("alt", ALT))
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.quartz.events
|
||||
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
|
||||
class NIP17Factory {
|
||||
@@ -79,7 +80,7 @@ class NIP17Factory {
|
||||
markAsSensitive: Boolean = false,
|
||||
zapRaiserAmount: Long? = null,
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
draftTag: String? = null,
|
||||
onReady: (Result) -> Unit,
|
||||
) {
|
||||
@@ -97,7 +98,7 @@ class NIP17Factory {
|
||||
zapRaiserAmount = zapRaiserAmount,
|
||||
geohash = geohash,
|
||||
isDraft = draftTag != null,
|
||||
nip94attachments = nip94attachments,
|
||||
imetas = imetas,
|
||||
) { senderMessage ->
|
||||
if (draftTag != null) {
|
||||
onReady(
|
||||
|
||||
@@ -22,13 +22,13 @@ package com.vitorpamplona.quartz.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.Dimension
|
||||
import com.vitorpamplona.quartz.encoders.ETag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments.Companion.IMETA
|
||||
import com.vitorpamplona.quartz.encoders.PTag
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
@Immutable
|
||||
class PictureEvent(
|
||||
@@ -334,36 +334,6 @@ class PictureMeta(
|
||||
}
|
||||
}
|
||||
|
||||
class Dimension(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
) {
|
||||
fun aspectRatio() = width.toFloat() / height.toFloat()
|
||||
|
||||
fun hasSize() = width > 0 && height > 0
|
||||
|
||||
override fun toString() = "${width}x$height"
|
||||
|
||||
companion object {
|
||||
fun parse(dim: String): Dimension? {
|
||||
if (dim == "0x0") return null
|
||||
|
||||
val parts = dim.split("x")
|
||||
if (parts.size != 2) return null
|
||||
|
||||
return try {
|
||||
val width = parts[0].toInt()
|
||||
val height = parts[1].toInt()
|
||||
|
||||
Dimension(width, height)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UserAnnotation(
|
||||
val pubkey: HexKey,
|
||||
val x: Int,
|
||||
|
||||
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.events
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
@@ -79,7 +80,7 @@ class PollNoteEvent(
|
||||
markAsSensitive: Boolean,
|
||||
zapRaiserAmount: Long?,
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
isDraft: Boolean,
|
||||
onReady: (PollNoteEvent) -> Unit,
|
||||
) {
|
||||
@@ -104,12 +105,8 @@ class PollNoteEvent(
|
||||
}
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
nip94attachments?.let {
|
||||
it.forEach {
|
||||
Nip92MediaAttachments().convertFromFileHeader(it)?.let {
|
||||
tags.add(it)
|
||||
}
|
||||
}
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
tags.add(arrayOf("alt", ALT))
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.Hex
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.HexValidator
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.encoders.Nip54InlineMetadata
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
@@ -124,16 +125,13 @@ class PrivateDmEvent(
|
||||
markAsSensitive: Boolean,
|
||||
zapRaiserAmount: Long?,
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
isDraft: Boolean,
|
||||
onReady: (PrivateDmEvent) -> Unit,
|
||||
) {
|
||||
var message = msg
|
||||
nip94attachments?.forEach {
|
||||
val myUrl = it.url()
|
||||
if (myUrl != null) {
|
||||
message = message.replace(myUrl, Nip54InlineMetadata().createUrl(myUrl, it.tags))
|
||||
}
|
||||
imetas?.forEach {
|
||||
message = message.replace(it.url, Nip54InlineMetadata().createUrl(it.url, it.properties))
|
||||
}
|
||||
|
||||
message =
|
||||
@@ -156,13 +154,10 @@ class PrivateDmEvent(
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
/* Privacy issue: DO NOT ADD THESE TO THE TAGS.
|
||||
nip94attachments?.let {
|
||||
it.forEach {
|
||||
Nip92().convertFromFileHeader(it)?.let {
|
||||
tags.add(it)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
*/
|
||||
|
||||
tags.add(arrayOf("alt", ALT))
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.quartz.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.Dimension
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.linkedin.urls.detection.UrlDetector
|
||||
import com.linkedin.urls.detection.UrlDetectorOptions
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
@@ -56,7 +57,7 @@ class TextNoteEvent(
|
||||
root: String? = null,
|
||||
directMentions: Set<HexKey> = emptySet(),
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
forkedFrom: Event? = null,
|
||||
signer: NostrSigner,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
@@ -122,12 +123,8 @@ class TextNoteEvent(
|
||||
}
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
nip94attachments?.let {
|
||||
it.forEach {
|
||||
Nip92MediaAttachments().convertFromFileHeader(it)?.let {
|
||||
tags.add(it)
|
||||
}
|
||||
}
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
|
||||
if (isDraft) {
|
||||
|
||||
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.events
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.IMetaTag
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
@@ -61,7 +62,7 @@ class TorrentCommentEvent(
|
||||
directMentions: Set<HexKey> = emptySet(),
|
||||
zapRaiserAmount: Long?,
|
||||
geohash: String? = null,
|
||||
nip94attachments: List<FileHeaderEvent>? = null,
|
||||
imetas: List<IMetaTag>? = null,
|
||||
forkedFrom: Event? = null,
|
||||
isDraft: Boolean,
|
||||
onReady: (TorrentCommentEvent) -> Unit,
|
||||
@@ -122,12 +123,8 @@ class TorrentCommentEvent(
|
||||
}
|
||||
zapRaiserAmount?.let { tags.add(arrayOf("zapraiser", "$it")) }
|
||||
geohash?.let { tags.addAll(geohashMipMap(it)) }
|
||||
nip94attachments?.let {
|
||||
it.forEach {
|
||||
Nip92MediaAttachments().convertFromFileHeader(it)?.let {
|
||||
tags.add(it)
|
||||
}
|
||||
}
|
||||
imetas?.forEach {
|
||||
tags.add(Nip92MediaAttachments.createTag(it))
|
||||
}
|
||||
tags.add(arrayOf("alt", ALT))
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.quartz.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.Dimension
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.Nip92MediaAttachments.Companion.IMETA
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.quartz.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.Dimension
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.quartz.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.encoders.Dimension
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
Reference in New Issue
Block a user