Separates EOSE for drafts alone.

This commit is contained in:
Vitor Pamplona
2025-10-03 18:37:48 -04:00
parent df77bd6633
commit 0843d5db95
4 changed files with 142 additions and 46 deletions
@@ -0,0 +1,83 @@
/**
* 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.service.relayClient.reqCommand.account.drafts
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.relayClient.eoseManagers.PerUserEoseManager
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.AccountQueryState
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
class AccountDraftsEoseManager(
client: INostrClient,
allKeys: () -> Set<AccountQueryState>,
) : PerUserEoseManager<AccountQueryState>(client, allKeys) {
override fun user(key: AccountQueryState) = key.account.userProfile()
fun relayFlow(query: AccountQueryState) = query.account.homeRelays.flow
override fun updateFilter(
key: AccountQueryState,
since: SincePerRelayMap?,
): List<RelayBasedFilter> =
if (key.account.isWriteable()) {
relayFlow(key).value.flatMap {
listOf(
filterDraftsFromKey(it, user(key).pubkeyHex, since?.get(it)?.time),
).flatten()
}
} else {
emptyList()
}
val userJobMap = mutableMapOf<User, List<Job>>()
@OptIn(FlowPreview::class)
override fun newSub(key: AccountQueryState): Subscription {
val user = user(key)
userJobMap[user]?.forEach { it.cancel() }
userJobMap[user] =
listOf(
key.account.scope.launch(Dispatchers.Default) {
relayFlow(key).collectLatest {
invalidateFilters()
}
},
)
return super.newSub(key)
}
override fun endSub(
key: User,
subId: String,
) {
super.endSub(key, subId)
userJobMap[key]?.forEach { it.cancel() }
}
}
@@ -0,0 +1,52 @@
/**
* 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.service.relayClient.reqCommand.account.drafts
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
val DraftKinds =
listOf(
DraftWrapEvent.KIND,
)
fun filterDraftsFromKey(
relay: NormalizedRelayUrl,
pubkey: HexKey?,
since: Long?,
): List<RelayBasedFilter> {
if (pubkey == null || pubkey.isEmpty()) return emptyList()
return listOf(
RelayBasedFilter(
relay = relay,
filter =
Filter(
kinds = DraftKinds,
authors = listOf(pubkey),
since = since,
),
),
)
}
@@ -48,23 +48,13 @@ class AccountMetadataEoseManager(
): List<RelayBasedFilter> =
relayFlow(key).value.flatMap {
val since = since?.get(it)?.time
if (key.account.isWriteable()) {
listOf(
filterAccountInfoAndListsFromKey(it, user(key).pubkeyHex, since),
filterFollowsAndMutesFromKey(it, user(key).pubkeyHex, since),
filterDraftsAndReportsFromKey(it, user(key).pubkeyHex, since),
filterLastPostsFromKey(it, user(key).pubkeyHex, since ?: TimeUtils.oneMonthAgo()),
filterBasicAccountInfoFromKeys(it, key.otherAccounts.minus(key.account.userProfile().pubkeyHex).toList(), since),
).flatten()
} else {
listOf(
filterAccountInfoAndListsFromKey(it, user(key).pubkeyHex, since),
filterFollowsAndMutesFromKey(it, user(key).pubkeyHex, since),
filterBookmarksAndReportsFromKey(it, user(key).pubkeyHex, since),
filterLastPostsFromKey(it, user(key).pubkeyHex, since ?: TimeUtils.oneMonthAgo()),
filterBasicAccountInfoFromKeys(it, key.otherAccounts.minus(key.account.userProfile().pubkeyHex).toList(), since),
).flatten()
}
listOf(
filterAccountInfoAndListsFromKey(it, user(key).pubkeyHex, since),
filterFollowsAndMutesFromKey(it, user(key).pubkeyHex, since),
filterBookmarksAndReportsFromKey(it, user(key).pubkeyHex, since),
filterLastPostsFromKey(it, user(key).pubkeyHex, since ?: TimeUtils.oneMonthAgo()),
filterBasicAccountInfoFromKeys(it, key.otherAccounts.minus(key.account.userProfile().pubkeyHex).toList(), since),
).flatten()
}
val userJobMap = mutableMapOf<User, List<Job>>()
@@ -24,44 +24,15 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
import com.vitorpamplona.quartz.nip56Reports.ReportEvent
val DraftsReportsAndBookmarksFromKeyKinds =
listOf(
DraftWrapEvent.KIND,
ReportEvent.KIND,
BookmarkListEvent.KIND,
)
val ReportsAndBookmarksFromKeyKinds =
listOf(
DraftWrapEvent.KIND,
ReportEvent.KIND,
BookmarkListEvent.KIND,
)
fun filterDraftsAndReportsFromKey(
relay: NormalizedRelayUrl,
pubkey: HexKey?,
since: Long?,
): List<RelayBasedFilter> {
if (pubkey == null || pubkey.isEmpty()) return emptyList()
return listOf(
RelayBasedFilter(
relay = relay,
filter =
Filter(
kinds = DraftsReportsAndBookmarksFromKeyKinds,
authors = listOf(pubkey),
since = since,
),
),
)
}
fun filterBookmarksAndReportsFromKey(
relay: NormalizedRelayUrl,
pubkey: HexKey?,