Merge remote-tracking branch 'origin/main' into claude/fix-arrowback-icon-0v6aT

This commit is contained in:
Claude
2026-04-24 02:44:48 +00:00
4 changed files with 55 additions and 16 deletions
@@ -923,6 +923,12 @@ object LocalCache : ILocalCache, ICacheProvider {
event.taggedAddresses().map { getOrCreateAddressableNote(it) } event.taggedAddresses().map { getOrCreateAddressableNote(it) }
} }
is WakeUpEvent -> {
// Link the referenced events so filterMissingEvents will query
// for them when the WakeUp note is in an EventFinder subscription.
event.eventIds().mapNotNull { checkGetOrCreateNote(it) }
}
is ChannelMessageEvent -> { is ChannelMessageEvent -> {
event.tagsWithoutCitations().filter { it != event.channelId() }.mapNotNull { checkGetOrCreateNote(it) } event.tagsWithoutCitations().filter { it != event.channelId() }.mapNotNull { checkGetOrCreateNote(it) }
} }
@@ -89,6 +89,7 @@ class EventNotificationConsumer(
) { ) {
companion object { companion object {
private const val WAKELOCK_TIMEOUT_MS = 10 * 60 * 1000L // 10 minutes private const val WAKELOCK_TIMEOUT_MS = 10 * 60 * 1000L // 10 minutes
private const val WAKEUP_WINDOW_MS = 30_000L
} }
/** /**
@@ -162,7 +163,7 @@ class EventNotificationConsumer(
} }
is WakeUpEvent -> { is WakeUpEvent -> {
wakeUpFor(event, LocalCache.getOrCreateNote(event.id), account) wakeUpFor(event, account)
return return
} }
} }
@@ -185,40 +186,68 @@ class EventNotificationConsumer(
suspend fun wakeUpFor( suspend fun wakeUpFor(
event: WakeUpEvent, event: WakeUpEvent,
note: Note,
account: Account, account: Account,
) { ) {
// A WakeUp's whole purpose is the events it references. If it carries
// none, there's nothing to fetch — skip the 30s subscription window.
val referencedTags = event.events().distinctBy { it.eventId }
if (referencedTags.isEmpty()) {
Log.d(TAG) { "WakeUp ${event.id} has no referenced events — skipping" }
return
}
// Per spec, p-tags on a WakeUp are the authors of the referenced
// events; those are whose metadata we need to render the notification.
// Fall back to e-tag author hints and finally to the WakeUp signer.
val referencedNotes = referencedTags.map { LocalCache.getOrCreateNote(it.eventId) }
val authorCandidates =
(event.authorKeys() + referencedTags.mapNotNull { it.author })
.distinct()
.ifEmpty { listOf(event.pubKey) }
.map { LocalCache.getOrCreateUser(it) }
coroutineScope { coroutineScope {
// keeps the relay connection active for 30 seconds. // keeps the relay connection active for 30 seconds.
launch { launch {
try { try {
withTimeout(30_000L) { withTimeout(WAKEUP_WINDOW_MS) {
Amethyst.instance.relayProxyClientConnector.relayServices Amethyst.instance.relayProxyClientConnector.relayServices
.collect() .collect()
} }
} catch (_: TimeoutCancellationException) { } catch (_: TimeoutCancellationException) {
Log.d(TAG) { "WakeUp ${event.id}${WAKEUP_WINDOW_MS}ms relay window elapsed" }
} }
} }
// keeps the subscription to download this event active for 30 seconds. // keeps subscriptions active for 30 seconds so EventFinder can pull
// the referenced events from relays and UserFinder can resolve the
// referenced authors' metadata.
launch { launch {
val accountState = ScreenAuthAccount(account) val accountState = ScreenAuthAccount(account)
val eventState = EventFinderQueryState(note, account) val eventStates = referencedNotes.map { EventFinderQueryState(it, account) }
val authorState = UserFinderQueryState(note.author ?: LocalCache.getOrCreateUser(event.pubKey), account) val authorStates = authorCandidates.map { UserFinderQueryState(it, account) }
try { try {
Amethyst.instance.authCoordinator.subscribe(accountState) Amethyst.instance.authCoordinator.subscribe(accountState)
Amethyst.instance.sources.eventFinder eventStates.forEach {
.subscribe(eventState) Amethyst.instance.sources.eventFinder
Amethyst.instance.sources.userFinder .subscribe(it)
.subscribe(authorState) }
delay(30_000) authorStates.forEach {
Amethyst.instance.sources.userFinder
.subscribe(it)
}
delay(WAKEUP_WINDOW_MS)
} finally { } finally {
Amethyst.instance.authCoordinator.unsubscribe(accountState) Amethyst.instance.authCoordinator.unsubscribe(accountState)
Amethyst.instance.sources.eventFinder eventStates.forEach {
.unsubscribe(eventState) Amethyst.instance.sources.eventFinder
Amethyst.instance.sources.userFinder .unsubscribe(it)
.unsubscribe(authorState) }
authorStates.forEach {
Amethyst.instance.sources.userFinder
.unsubscribe(it)
}
} }
} }
} }
@@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.commons.icons.symbols
// Single source of truth for Material Symbols visual axes. Change WEIGHT here to globally // Single source of truth for Material Symbols visual axes. Change WEIGHT here to globally
// re-tune line thickness (100 = Thin, 200 = ExtraLight, 300 = Light, 400 = Regular, …, 700 = Bold). // re-tune line thickness (100 = Thin, 200 = ExtraLight, 300 = Light, 400 = Regular, …, 700 = Bold).
object MaterialSymbolsDefaults { object MaterialSymbolsDefaults {
const val WEIGHT: Int = 100 const val WEIGHT: Int = 300
const val FILL: Float = 0f const val FILL: Float = 0f
const val OPTICAL_SIZE: Float = 24f const val OPTICAL_SIZE: Float = 24f
const val GRADE: Float = 0f const val GRADE: Float = 0f
@@ -68,6 +68,10 @@ class WakeUpEvent(
const val KIND = 23903 const val KIND = 23903
const val ALT_DESCRIPTION = "WakeUp" const val ALT_DESCRIPTION = "WakeUp"
// p-tags on a WakeUp identify the AUTHORS of the referenced events —
// the people whose events are the subject of the wake-up and who should
// come online to handle new activity on them. Callers can add extra
// e/p tags via [initializer] when waking up about multiple events.
fun build( fun build(
about: EventHintBundle<Event>, about: EventHintBundle<Event>,
createdAt: Long = TimeUtils.now(), createdAt: Long = TimeUtils.now(),