Adds support for expirations in DMs
This commit is contained in:
@@ -144,7 +144,7 @@ fun AppNavigation(
|
|||||||
composableFromEndArgs<Route.RelayInfo> { RelayInformationScreen(it.url, accountViewModel, nav) }
|
composableFromEndArgs<Route.RelayInfo> { RelayInformationScreen(it.url, accountViewModel, nav) }
|
||||||
composableFromEndArgs<Route.Community> { CommunityScreen(Address(it.kind, it.pubKeyHex, it.dTag), accountViewModel, nav) }
|
composableFromEndArgs<Route.Community> { CommunityScreen(Address(it.kind, it.pubKeyHex, it.dTag), accountViewModel, nav) }
|
||||||
|
|
||||||
composableFromEndArgs<Route.Room> { ChatroomScreen(it.toKey(), it.message, it.replyId, it.draftId, accountViewModel, nav) }
|
composableFromEndArgs<Route.Room> { ChatroomScreen(it.toKey(), it.message, it.replyId, it.draftId, it.expiresDays, accountViewModel, nav) }
|
||||||
composableFromEndArgs<Route.RoomByAuthor> { ChatroomByAuthorScreen(it.id, null, accountViewModel, nav) }
|
composableFromEndArgs<Route.RoomByAuthor> { ChatroomByAuthorScreen(it.id, null, accountViewModel, nav) }
|
||||||
|
|
||||||
composableFromEndArgs<Route.PublicChatChannel> {
|
composableFromEndArgs<Route.PublicChatChannel> {
|
||||||
|
|||||||
+10
-3
@@ -138,6 +138,7 @@ fun routeToMessage(
|
|||||||
draftMessage: String?,
|
draftMessage: String?,
|
||||||
replyId: HexKey? = null,
|
replyId: HexKey? = null,
|
||||||
draftId: HexKey? = null,
|
draftId: HexKey? = null,
|
||||||
|
expiresDays: Int? = null,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
): Route =
|
): Route =
|
||||||
routeToMessage(
|
routeToMessage(
|
||||||
@@ -145,6 +146,7 @@ fun routeToMessage(
|
|||||||
draftMessage,
|
draftMessage,
|
||||||
replyId,
|
replyId,
|
||||||
draftId,
|
draftId,
|
||||||
|
expiresDays,
|
||||||
accountViewModel,
|
accountViewModel,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -153,12 +155,14 @@ fun routeToMessage(
|
|||||||
draftMessage: String?,
|
draftMessage: String?,
|
||||||
replyId: HexKey? = null,
|
replyId: HexKey? = null,
|
||||||
draftId: HexKey? = null,
|
draftId: HexKey? = null,
|
||||||
|
expiresDays: Int? = null,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
) = routeToMessage(
|
) = routeToMessage(
|
||||||
ChatroomKey(users),
|
ChatroomKey(users),
|
||||||
draftMessage,
|
draftMessage,
|
||||||
replyId,
|
replyId,
|
||||||
draftId,
|
draftId,
|
||||||
|
expiresDays,
|
||||||
accountViewModel,
|
accountViewModel,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -167,19 +171,21 @@ fun routeToMessage(
|
|||||||
draftMessage: String?,
|
draftMessage: String?,
|
||||||
replyId: HexKey? = null,
|
replyId: HexKey? = null,
|
||||||
draftId: HexKey? = null,
|
draftId: HexKey? = null,
|
||||||
|
expiresDays: Int? = null,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
): Route = routeToMessage(room, draftMessage, replyId, draftId, accountViewModel.account)
|
): Route = routeToMessage(room, draftMessage, replyId, draftId, expiresDays, accountViewModel.account)
|
||||||
|
|
||||||
fun routeToMessage(
|
fun routeToMessage(
|
||||||
room: ChatroomKey,
|
room: ChatroomKey,
|
||||||
draftMessage: String? = null,
|
draftMessage: String? = null,
|
||||||
replyId: HexKey? = null,
|
replyId: HexKey? = null,
|
||||||
draftId: HexKey? = null,
|
draftId: HexKey? = null,
|
||||||
|
expiresDays: Int? = null,
|
||||||
account: Account,
|
account: Account,
|
||||||
): Route {
|
): Route {
|
||||||
account.chatroomList.getOrCreatePrivateChatroom(room)
|
account.chatroomList.getOrCreatePrivateChatroom(room)
|
||||||
|
|
||||||
return Route.Room(room, draftMessage, replyId, draftId)
|
return Route.Room(room, draftMessage, replyId, draftId, expiresDays)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun routeToMessage(
|
fun routeToMessage(
|
||||||
@@ -187,8 +193,9 @@ fun routeToMessage(
|
|||||||
draftMessage: String?,
|
draftMessage: String?,
|
||||||
replyId: HexKey? = null,
|
replyId: HexKey? = null,
|
||||||
draftId: HexKey? = null,
|
draftId: HexKey? = null,
|
||||||
|
expiresDays: Int? = null,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
): Route = routeToMessage(user.pubkeyHex, draftMessage, replyId, draftId, accountViewModel)
|
): Route = routeToMessage(user.pubkeyHex, draftMessage, replyId, draftId, expiresDays, accountViewModel)
|
||||||
|
|
||||||
fun routeFor(note: EphemeralChatChannel): Route = Route.EphemeralChat(note.roomId.id, note.roomId.relayUrl.url)
|
fun routeFor(note: EphemeralChatChannel): Route = Route.EphemeralChat(note.roomId.id, note.roomId.relayUrl.url)
|
||||||
|
|
||||||
|
|||||||
@@ -126,12 +126,14 @@ sealed class Route {
|
|||||||
val message: String? = null,
|
val message: String? = null,
|
||||||
val replyId: HexKey? = null,
|
val replyId: HexKey? = null,
|
||||||
val draftId: HexKey? = null,
|
val draftId: HexKey? = null,
|
||||||
|
val expiresDays: Int? = null,
|
||||||
) : Route() {
|
) : Route() {
|
||||||
constructor(key: ChatroomKey, message: String? = null, replyId: HexKey? = null, draftId: HexKey? = null) : this(
|
constructor(key: ChatroomKey, message: String? = null, replyId: HexKey? = null, draftId: HexKey? = null, expiresDays: Int? = null) : this(
|
||||||
id = key.users.joinToString(","),
|
id = key.users.joinToString(","),
|
||||||
message = message,
|
message = message,
|
||||||
replyId = replyId,
|
replyId = replyId,
|
||||||
draftId = draftId,
|
draftId = draftId,
|
||||||
|
expiresDays = expiresDays,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun toKey(): ChatroomKey = ChatroomKey(id.split(",").toSet())
|
fun toKey(): ChatroomKey = ChatroomKey(id.split(",").toSet())
|
||||||
|
|||||||
+2
@@ -41,6 +41,7 @@ fun ChatroomScreen(
|
|||||||
draftMessage: String? = null,
|
draftMessage: String? = null,
|
||||||
replyToNote: HexKey? = null,
|
replyToNote: HexKey? = null,
|
||||||
editFromDraft: HexKey? = null,
|
editFromDraft: HexKey? = null,
|
||||||
|
expiresDays: Int? = null,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
@@ -57,6 +58,7 @@ fun ChatroomScreen(
|
|||||||
draftMessage = draftMessage,
|
draftMessage = draftMessage,
|
||||||
replyToNote = replyToNote,
|
replyToNote = replyToNote,
|
||||||
editFromDraft = editFromDraft,
|
editFromDraft = editFromDraft,
|
||||||
|
expiresDays = expiresDays,
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
nav = nav,
|
nav = nav,
|
||||||
)
|
)
|
||||||
|
|||||||
+7
-1
@@ -52,6 +52,7 @@ fun ChatroomView(
|
|||||||
draftMessage: String?,
|
draftMessage: String?,
|
||||||
replyToNote: HexKey? = null,
|
replyToNote: HexKey? = null,
|
||||||
editFromDraft: HexKey? = null,
|
editFromDraft: HexKey? = null,
|
||||||
|
expiresDays: Int? = null,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
@@ -79,7 +80,7 @@ fun ChatroomView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (editFromDraft != null) {
|
if (editFromDraft != null) {
|
||||||
LaunchedEffect(replyToNote, newPostModel, accountViewModel) {
|
LaunchedEffect(editFromDraft, newPostModel, accountViewModel) {
|
||||||
accountViewModel.checkGetOrCreateNote(editFromDraft) {
|
accountViewModel.checkGetOrCreateNote(editFromDraft) {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
newPostModel.editFromDraft(it)
|
newPostModel.editFromDraft(it)
|
||||||
@@ -87,6 +88,11 @@ fun ChatroomView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (expiresDays != null) {
|
||||||
|
LaunchedEffect(expiresDays, newPostModel, accountViewModel) {
|
||||||
|
newPostModel.loadExpiration(expiresDays)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (room.users.size == 1) {
|
if (room.users.size == 1) {
|
||||||
// Activates NIP-17 if the user has DM relays
|
// Activates NIP-17 if the user has DM relays
|
||||||
|
|||||||
+18
-1
@@ -80,6 +80,7 @@ import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
|
|||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
||||||
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
|
||||||
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||||
|
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup
|
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits
|
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits
|
||||||
@@ -88,6 +89,7 @@ import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
|
|||||||
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||||
import com.vitorpamplona.quartz.nip92IMeta.imetas
|
import com.vitorpamplona.quartz.nip92IMeta.imetas
|
||||||
import com.vitorpamplona.quartz.utils.Hex
|
import com.vitorpamplona.quartz.utils.Hex
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -168,6 +170,8 @@ class ChatNewMessageViewModel :
|
|||||||
var wantsZapraiser by mutableStateOf(false)
|
var wantsZapraiser by mutableStateOf(false)
|
||||||
override var zapRaiserAmount = mutableStateOf<Long?>(null)
|
override var zapRaiserAmount = mutableStateOf<Long?>(null)
|
||||||
|
|
||||||
|
var expirationDays by mutableStateOf<Int?>(null)
|
||||||
|
|
||||||
// NIP17 Wrapped DMs / Group messages
|
// NIP17 Wrapped DMs / Group messages
|
||||||
var nip17 by mutableStateOf(false)
|
var nip17 by mutableStateOf(false)
|
||||||
|
|
||||||
@@ -267,6 +271,10 @@ class ChatNewMessageViewModel :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun loadExpiration(expirationDays: Int) {
|
||||||
|
this.expirationDays = expirationDays
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadFromDraft(draft: Note) {
|
private fun loadFromDraft(draft: Note) {
|
||||||
val draftEvent = draft.event ?: return
|
val draftEvent = draft.event ?: return
|
||||||
|
|
||||||
@@ -341,6 +349,8 @@ class ChatNewMessageViewModel :
|
|||||||
}
|
}
|
||||||
urlPreviews.update(message)
|
urlPreviews.update(message)
|
||||||
|
|
||||||
|
expirationDays = draftEvent.expiration()?.let { (it / 86_400).toInt() }
|
||||||
|
|
||||||
iMetaAttachments.addAll(draftEvent.imetas())
|
iMetaAttachments.addAll(draftEvent.imetas())
|
||||||
|
|
||||||
requiresNIP17 = draftEvent is NIP17Group
|
requiresNIP17 = draftEvent is NIP17Group
|
||||||
@@ -438,6 +448,8 @@ class ChatNewMessageViewModel :
|
|||||||
val localZapRaiserAmount = if (wantsZapraiser) zapRaiserAmount.value else null
|
val localZapRaiserAmount = if (wantsZapraiser) zapRaiserAmount.value else null
|
||||||
val zapReceiver = if (wantsForwardZapTo) forwardZapTo.value.toZapSplitSetup() else null
|
val zapReceiver = if (wantsForwardZapTo) forwardZapTo.value.toZapSplitSetup() else null
|
||||||
|
|
||||||
|
val expiration = expirationDays?.let { TimeUtils.now() + it.toLong() * 86_400 }
|
||||||
|
|
||||||
if (nip17 || room.users.size > 1 || replyTo.value?.event is NIP17Group) {
|
if (nip17 || room.users.size > 1 || replyTo.value?.event is NIP17Group) {
|
||||||
val replyHint = replyTo.value?.toEventHint<BaseDMGroupEvent>()
|
val replyHint = replyTo.value?.toEventHint<BaseDMGroupEvent>()
|
||||||
|
|
||||||
@@ -452,6 +464,7 @@ class ChatNewMessageViewModel :
|
|||||||
localZapRaiserAmount?.let { zapraiser(it) }
|
localZapRaiserAmount?.let { zapraiser(it) }
|
||||||
zapReceiver?.let { zapSplits(it) }
|
zapReceiver?.let { zapSplits(it) }
|
||||||
contentWarningReason?.let { contentWarning(it) }
|
contentWarningReason?.let { contentWarning(it) }
|
||||||
|
expiration?.let { expiration(it) }
|
||||||
|
|
||||||
emojis(emojis)
|
emojis(emojis)
|
||||||
imetas(usedAttachments)
|
imetas(usedAttachments)
|
||||||
@@ -466,6 +479,7 @@ class ChatNewMessageViewModel :
|
|||||||
localZapRaiserAmount?.let { zapraiser(it) }
|
localZapRaiserAmount?.let { zapraiser(it) }
|
||||||
zapReceiver?.let { zapSplits(it) }
|
zapReceiver?.let { zapSplits(it) }
|
||||||
contentWarningReason?.let { contentWarning(it) }
|
contentWarningReason?.let { contentWarning(it) }
|
||||||
|
expiration?.let { expiration(it) }
|
||||||
|
|
||||||
emojis(emojis)
|
emojis(emojis)
|
||||||
imetas(usedAttachments)
|
imetas(usedAttachments)
|
||||||
@@ -487,7 +501,9 @@ class ChatNewMessageViewModel :
|
|||||||
imetas = usedAttachments,
|
imetas = usedAttachments,
|
||||||
replyingTo = replyTo.value?.toEId(),
|
replyingTo = replyTo.value?.toEId(),
|
||||||
signer = accountViewModel.account.signer,
|
signer = accountViewModel.account.signer,
|
||||||
)
|
) {
|
||||||
|
expiration?.let { expiration(it) }
|
||||||
|
}
|
||||||
|
|
||||||
if (draftTag != null) {
|
if (draftTag != null) {
|
||||||
accountViewModel.account.createAndSendDraftIgnoreErrors(draftTag, template)
|
accountViewModel.account.createAndSendDraftIgnoreErrors(draftTag, template)
|
||||||
@@ -518,6 +534,7 @@ class ChatNewMessageViewModel :
|
|||||||
subject = TextFieldValue("")
|
subject = TextFieldValue("")
|
||||||
|
|
||||||
replyTo.value = null
|
replyTo.value = null
|
||||||
|
expirationDays = null
|
||||||
|
|
||||||
wantsInvoice = false
|
wantsInvoice = false
|
||||||
wantsZapraiser = false
|
wantsZapraiser = false
|
||||||
|
|||||||
+1
-1
@@ -194,7 +194,7 @@ fun NewGroupDMScreen(
|
|||||||
accountViewModel.runIOCatching {
|
accountViewModel.runIOCatching {
|
||||||
postViewModel.sendPostSync()
|
postViewModel.sendPostSync()
|
||||||
postViewModel.room?.let {
|
postViewModel.room?.let {
|
||||||
nav.nav(routeToMessage(it, null, null, null, accountViewModel))
|
nav.nav(routeToMessage(it, null, null, null, null, accountViewModel))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nav.popBack()
|
nav.popBack()
|
||||||
|
|||||||
+16
@@ -21,6 +21,7 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
@@ -32,6 +33,8 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
@@ -52,6 +55,7 @@ import com.vitorpamplona.amethyst.ui.stringRes
|
|||||||
import com.vitorpamplona.amethyst.ui.theme.EditFieldBorder
|
import com.vitorpamplona.amethyst.ui.theme.EditFieldBorder
|
||||||
import com.vitorpamplona.amethyst.ui.theme.EditFieldModifier
|
import com.vitorpamplona.amethyst.ui.theme.EditFieldModifier
|
||||||
import com.vitorpamplona.amethyst.ui.theme.EditFieldTrailingIconModifier
|
import com.vitorpamplona.amethyst.ui.theme.EditFieldTrailingIconModifier
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Font12SP
|
||||||
import com.vitorpamplona.amethyst.ui.theme.PostKeyboard
|
import com.vitorpamplona.amethyst.ui.theme.PostKeyboard
|
||||||
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
|
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
|
||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
@@ -127,6 +131,18 @@ fun PrivateMessageEditFieldRow(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channelScreenModel.expirationDays?.let {
|
||||||
|
Row(Modifier.fillMaxWidth().padding(vertical = 2.dp), horizontalArrangement = Arrangement.Center) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.this_message_will_disappear_in_days, it),
|
||||||
|
fontSize = Font12SP,
|
||||||
|
color = MaterialTheme.colorScheme.placeholderText,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EditField(channelScreenModel, onSendNewMessage, accountViewModel)
|
EditField(channelScreenModel, onSendNewMessage, accountViewModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -110,6 +110,7 @@ fun MessagesTwoPane(
|
|||||||
draftMessage = it.message,
|
draftMessage = it.message,
|
||||||
replyToNote = it.replyId,
|
replyToNote = it.replyId,
|
||||||
editFromDraft = it.draftId,
|
editFromDraft = it.draftId,
|
||||||
|
expiresDays = it.expiresDays,
|
||||||
nav = nav,
|
nav = nav,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1249,4 +1249,5 @@
|
|||||||
<string name="crashreport_found">Crash Report found</string>
|
<string name="crashreport_found">Crash Report found</string>
|
||||||
<string name="would_you_like_to_send_the_recent_crash_report_to_amethyst_in_a_dm_no_personal_information_will_be_shared">Would you like to send the recent crash report to Amethyst in a DM? No personal information will be shared</string>
|
<string name="would_you_like_to_send_the_recent_crash_report_to_amethyst_in_a_dm_no_personal_information_will_be_shared">Would you like to send the recent crash report to Amethyst in a DM? No personal information will be shared</string>
|
||||||
<string name="crashreport_found_send">Send it</string>
|
<string name="crashreport_found_send">Send it</string>
|
||||||
|
<string name="this_message_will_disappear_in_days">This message will disappear in %1$d days</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -29,9 +29,11 @@ import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEven
|
|||||||
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
|
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
|
||||||
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
|
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
|
||||||
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
|
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
|
||||||
|
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||||
import com.vitorpamplona.quartz.utils.mapNotNullAsync
|
import com.vitorpamplona.quartz.utils.mapNotNullAsync
|
||||||
|
import java.time.temporal.TemporalAdjusters.next
|
||||||
|
|
||||||
class NIP17Factory {
|
class NIP17Factory {
|
||||||
data class Result(
|
data class Result(
|
||||||
@@ -43,8 +45,17 @@ class NIP17Factory {
|
|||||||
event: Event,
|
event: Event,
|
||||||
to: Set<HexKey>,
|
to: Set<HexKey>,
|
||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
): List<GiftWrapEvent> =
|
): List<GiftWrapEvent> {
|
||||||
mapNotNullAsync(
|
val innerExpDelta =
|
||||||
|
event.expiration()?.let {
|
||||||
|
if (it > event.createdAt) {
|
||||||
|
it - event.createdAt
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapNotNullAsync(
|
||||||
to.toList(),
|
to.toList(),
|
||||||
) { next ->
|
) { next ->
|
||||||
GiftWrapEvent.create(
|
GiftWrapEvent.create(
|
||||||
@@ -52,11 +63,14 @@ class NIP17Factory {
|
|||||||
SealedRumorEvent.create(
|
SealedRumorEvent.create(
|
||||||
event = event,
|
event = event,
|
||||||
encryptTo = next,
|
encryptTo = next,
|
||||||
|
expirationDelta = innerExpDelta,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
),
|
),
|
||||||
recipientPubKey = next,
|
recipientPubKey = next,
|
||||||
|
expirationDelta = innerExpDelta,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun createMessageNIP17(
|
suspend fun createMessageNIP17(
|
||||||
template: EventTemplate<ChatMessageEvent>,
|
template: EventTemplate<ChatMessageEvent>,
|
||||||
|
|||||||
+13
-2
@@ -25,6 +25,7 @@ import androidx.compose.runtime.Immutable
|
|||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip40Expiration.ExpirationTag
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.HostStub
|
import com.vitorpamplona.quartz.nip59Giftwrap.HostStub
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.WrappedEvent
|
import com.vitorpamplona.quartz.nip59Giftwrap.WrappedEvent
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.rumors.Rumor
|
import com.vitorpamplona.quartz.nip59Giftwrap.rumors.Rumor
|
||||||
@@ -99,23 +100,33 @@ class SealedRumorEvent(
|
|||||||
event: Event,
|
event: Event,
|
||||||
encryptTo: HexKey,
|
encryptTo: HexKey,
|
||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
|
expirationDelta: Long? = null,
|
||||||
createdAt: Long = TimeUtils.now(),
|
createdAt: Long = TimeUtils.now(),
|
||||||
): SealedRumorEvent {
|
): SealedRumorEvent {
|
||||||
val rumor = Rumor.create(event)
|
val rumor = Rumor.create(event)
|
||||||
return create(rumor, encryptTo, signer, createdAt)
|
return create(rumor, encryptTo, signer, expirationDelta, createdAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun create(
|
suspend fun create(
|
||||||
rumor: Rumor,
|
rumor: Rumor,
|
||||||
encryptTo: HexKey,
|
encryptTo: HexKey,
|
||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
|
expirationDelta: Long? = null,
|
||||||
createdAt: Long = TimeUtils.randomWithTwoDays(),
|
createdAt: Long = TimeUtils.randomWithTwoDays(),
|
||||||
): SealedRumorEvent {
|
): SealedRumorEvent {
|
||||||
val msg = Rumor.toJson(rumor)
|
val msg = Rumor.toJson(rumor)
|
||||||
|
|
||||||
|
val tags =
|
||||||
|
expirationDelta?.let {
|
||||||
|
// minimum expiration is two days in the future due to the random created at
|
||||||
|
// this will make sure the even arrives and is not deleted because of the 2 days.
|
||||||
|
arrayOf(ExpirationTag.assemble(createdAt + it + TimeUtils.twoDays()))
|
||||||
|
} ?: emptyArray()
|
||||||
|
|
||||||
return signer.sign(
|
return signer.sign(
|
||||||
createdAt = createdAt,
|
createdAt = createdAt,
|
||||||
kind = KIND,
|
kind = KIND,
|
||||||
tags = emptyArray(),
|
tags = tags,
|
||||||
content = signer.nip44Encrypt(msg, encryptTo),
|
content = signer.nip44Encrypt(msg, encryptTo),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-1
@@ -28,7 +28,9 @@ import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
|
|||||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
|
||||||
import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri
|
import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri
|
||||||
|
import com.vitorpamplona.quartz.nip40Expiration.ExpirationTag
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.HostStub
|
import com.vitorpamplona.quartz.nip59Giftwrap.HostStub
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.WrappedEvent
|
import com.vitorpamplona.quartz.nip59Giftwrap.WrappedEvent
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
@@ -103,13 +105,27 @@ class GiftWrapEvent(
|
|||||||
suspend fun create(
|
suspend fun create(
|
||||||
event: Event,
|
event: Event,
|
||||||
recipientPubKey: HexKey,
|
recipientPubKey: HexKey,
|
||||||
|
expirationDelta: Long? = null,
|
||||||
createdAt: Long = TimeUtils.randomWithTwoDays(),
|
createdAt: Long = TimeUtils.randomWithTwoDays(),
|
||||||
): GiftWrapEvent {
|
): GiftWrapEvent {
|
||||||
val signer = NostrSignerInternal(KeyPair()) // GiftWrap is always a random key
|
val signer = NostrSignerInternal(KeyPair()) // GiftWrap is always a random key
|
||||||
|
|
||||||
|
val tags =
|
||||||
|
expirationDelta?.let {
|
||||||
|
// minimum expiration is two days in the future due to the random created at
|
||||||
|
// this will make sure the even arrives and is not deleted because of the 2 days.
|
||||||
|
arrayOf(
|
||||||
|
PTag.assemble(recipientPubKey, null),
|
||||||
|
ExpirationTag.assemble(createdAt + it + TimeUtils.twoDays()),
|
||||||
|
)
|
||||||
|
} ?: arrayOf(
|
||||||
|
PTag.assemble(recipientPubKey, null),
|
||||||
|
)
|
||||||
|
|
||||||
return signer.sign(
|
return signer.sign(
|
||||||
createdAt = createdAt,
|
createdAt = createdAt,
|
||||||
kind = KIND,
|
kind = KIND,
|
||||||
tags = arrayOf(arrayOf("p", recipientPubKey)),
|
tags = tags,
|
||||||
content = signer.nip44Encrypt(event.toJson(), recipientPubKey),
|
content = signer.nip44Encrypt(event.toJson(), recipientPubKey),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user