refactor: use LocalCache.observeNotes flow for open polls

Replace LocalCache.notes.forEach iteration with observeNotes using a
Filter for PollEvent.KIND and ZapPollEvent.KIND by the current user.
This makes the open polls list reactive — it updates automatically
when new polls arrive instead of only computing once on composition.

https://claude.ai/code/session_01LLLN5MDA5nJFYx38MVo81Q
This commit is contained in:
Claude
2026-03-20 21:38:48 +00:00
parent 638bad3672
commit b6e54db81b
@@ -24,74 +24,61 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.produceState import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@Composable @Composable
fun openPollsState(accountViewModel: AccountViewModel) = fun openPollsState(accountViewModel: AccountViewModel) =
produceState(initialValue = emptyList<Note>(), accountViewModel) { remember(accountViewModel) {
value = val userPubkeyHex = accountViewModel.account.userProfile().pubkeyHex
withContext(Dispatchers.IO) {
findOpenPolls(accountViewModel)
}
}
private fun findOpenPolls(accountViewModel: AccountViewModel): List<Note> { val filter =
val userPubkeyHex = accountViewModel.account.userProfile().pubkeyHex Filter(
kinds = listOf(PollEvent.KIND, ZapPollEvent.KIND),
authors = listOf(userPubkeyHex),
)
accountViewModel.account.cache
.observeNotes(filter)
.map { notes -> filterOpenPolls(notes) }
.flowOn(Dispatchers.IO)
}.collectAsStateWithLifecycle(initialValue = emptyList())
private fun filterOpenPolls(notes: List<Note>): List<Note> {
val now = TimeUtils.now() val now = TimeUtils.now()
val oneDayInSeconds = TimeUtils.ONE_DAY val oneDayInSeconds = TimeUtils.ONE_DAY
val openPolls = mutableListOf<Note>() return notes.filter { note ->
val event = note.event ?: return@filter false
LocalCache.notes.forEach { _, note ->
val event = note.event ?: return@forEach
when (event) { when (event) {
is PollEvent -> { is PollEvent -> {
if (event.pubKey == userPubkeyHex) { val endsAt = event.endsAt()
val endsAt = event.endsAt() if (endsAt != null) endsAt > now else event.createdAt + oneDayInSeconds > now
val isOpen =
if (endsAt != null) {
endsAt > now
} else {
event.createdAt + oneDayInSeconds > now
}
if (isOpen) {
openPolls.add(note)
}
}
} }
is ZapPollEvent -> { is ZapPollEvent -> {
if (event.pubKey == userPubkeyHex) { val closedAt = event.closedAt()
val closedAt = event.closedAt() if (closedAt != null) closedAt > now else event.createdAt + oneDayInSeconds > now
val isOpen =
if (closedAt != null) {
closedAt > now
} else {
event.createdAt + oneDayInSeconds > now
}
if (isOpen) {
openPolls.add(note)
}
}
} }
}
}
return openPolls.sortedByDescending { it.createdAt() } else -> false
}
}.sortedByDescending { it.createdAt() }
} }
@Composable @Composable