feat: add dismiss button to active poll notification cards
Allow users to individually dismiss poll notification cards at the top of the notification screen, matching the existing Zap the Devs dismiss pattern. Dismissed poll IDs are persisted in SharedPreferences. https://claude.ai/code/session_013ECrmmshiF9SNTxr9SXkvC
This commit is contained in:
@@ -130,6 +130,7 @@ private object PrefKeys {
|
||||
const val LOGIN_WITH_EXTERNAL_SIGNER = "login_with_external_signer"
|
||||
const val SIGNER_PACKAGE_NAME = "signer_package_name"
|
||||
const val HAS_DONATED_IN_VERSION = "has_donated_in_version"
|
||||
const val DISMISSED_POLL_NOTE_IDS = "dismissed_poll_note_ids"
|
||||
const val PENDING_ATTESTATIONS = "pending_attestations"
|
||||
|
||||
const val ALL_ACCOUNT_INFO = "all_saved_accounts_info"
|
||||
@@ -389,6 +390,7 @@ object LocalPreferences {
|
||||
JsonMapper.toJson(regularMap),
|
||||
)
|
||||
putStringSet(PrefKeys.HAS_DONATED_IN_VERSION, settings.hasDonatedInVersion.value)
|
||||
putStringSet(PrefKeys.DISMISSED_POLL_NOTE_IDS, settings.dismissedPollNoteIds.value)
|
||||
|
||||
putString(
|
||||
PrefKeys.PENDING_ATTESTATIONS,
|
||||
@@ -473,6 +475,7 @@ object LocalPreferences {
|
||||
val hideBlockAlertDialog = getBoolean(PrefKeys.HIDE_BLOCK_ALERT_DIALOG, false)
|
||||
val hideNIP17WarningDialog = getBoolean(PrefKeys.HIDE_NIP_17_WARNING_DIALOG, false)
|
||||
val hasDonatedInVersion = getStringSet(PrefKeys.HAS_DONATED_IN_VERSION, null) ?: setOf()
|
||||
val dismissedPollNoteIds = getStringSet(PrefKeys.DISMISSED_POLL_NOTE_IDS, null) ?: setOf()
|
||||
val localRelayServers = getStringSet(PrefKeys.LOCAL_RELAY_SERVERS, null) ?: setOf()
|
||||
|
||||
val defaultHomeFollowListStr = getString(PrefKeys.DEFAULT_HOME_FOLLOW_LIST, null)
|
||||
@@ -594,6 +597,7 @@ object LocalPreferences {
|
||||
backupTrustProviderList = latestTrustProviderList.await(),
|
||||
lastReadPerRoute = MutableStateFlow(lastReadPerRoute.await()),
|
||||
hasDonatedInVersion = MutableStateFlow(hasDonatedInVersion),
|
||||
dismissedPollNoteIds = MutableStateFlow(dismissedPollNoteIds),
|
||||
pendingAttestations = MutableStateFlow(pendingAttestations.await()),
|
||||
backupNipA3PaymentTargets = latestPaymentTargets.await(),
|
||||
)
|
||||
|
||||
@@ -2149,6 +2149,8 @@ class Account(
|
||||
|
||||
fun markDonatedInThisVersion() = settings.markDonatedInThisVersion(BuildConfig.VERSION_NAME)
|
||||
|
||||
fun dismissPollNotification(noteId: String) = settings.dismissPollNotification(noteId)
|
||||
|
||||
init {
|
||||
Log.d("AccountRegisterObservers", "Init")
|
||||
|
||||
|
||||
@@ -193,6 +193,7 @@ class AccountSettings(
|
||||
var backupTrustProviderList: TrustProviderListEvent? = null,
|
||||
val lastReadPerRoute: MutableStateFlow<Map<String, MutableStateFlow<Long>>> = MutableStateFlow(mapOf()),
|
||||
val hasDonatedInVersion: MutableStateFlow<Set<String>> = MutableStateFlow(setOf()),
|
||||
val dismissedPollNoteIds: MutableStateFlow<Set<String>> = MutableStateFlow(setOf()),
|
||||
val pendingAttestations: MutableStateFlow<Map<HexKey, String>> = MutableStateFlow(mapOf()),
|
||||
var backupNipA3PaymentTargets: PaymentTargetsEvent? = null,
|
||||
) : EphemeralChatRepository,
|
||||
@@ -679,6 +680,21 @@ class AccountSettings(
|
||||
return false
|
||||
}
|
||||
|
||||
// ---
|
||||
// dismissed polls
|
||||
// ---
|
||||
|
||||
fun isDismissedPoll(noteId: String) = dismissedPollNoteIds.value.contains(noteId)
|
||||
|
||||
fun dismissPollNotification(noteId: String) {
|
||||
if (!dismissedPollNoteIds.value.contains(noteId)) {
|
||||
dismissedPollNoteIds.update {
|
||||
it + noteId
|
||||
}
|
||||
saveAccountSettings()
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// last read flows
|
||||
// ----
|
||||
|
||||
+2
@@ -1126,6 +1126,8 @@ class AccountViewModel(
|
||||
|
||||
fun markDonatedInThisVersion() = account.markDonatedInThisVersion()
|
||||
|
||||
fun dismissPollNotification(noteId: String) = account.dismissPollNotification(noteId)
|
||||
|
||||
fun dontTranslateFrom() = account.settings.syncedSettings.languages.dontTranslateFrom.value
|
||||
|
||||
fun translateTo() = account.settings.syncedSettings.languages.translateTo.value
|
||||
|
||||
+15
-1
@@ -36,6 +36,7 @@ import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
@@ -59,6 +60,7 @@ import com.vitorpamplona.amethyst.ui.feeds.FeedError
|
||||
import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.BadgeCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.CloseIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.MessageSetCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.MultiSetCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
@@ -184,7 +186,19 @@ private fun FeedLoaded(
|
||||
Card(
|
||||
modifier = MaterialTheme.colorScheme.imageModifier,
|
||||
) {
|
||||
OpenPollsSectionHeader()
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
OpenPollsSectionHeader()
|
||||
IconButton(
|
||||
modifier = Modifier.padding(end = Size10dp),
|
||||
onClick = { accountViewModel.dismissPollNotification(note.idHex) },
|
||||
) {
|
||||
CloseIcon()
|
||||
}
|
||||
}
|
||||
Row(Modifier.fillMaxWidth().animateItem()) {
|
||||
NoteCompose(
|
||||
baseNote = note,
|
||||
|
||||
+9
-4
@@ -31,6 +31,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
@@ -47,10 +48,14 @@ class OpenPollsState(
|
||||
)
|
||||
|
||||
val flow: StateFlow<List<Note>> =
|
||||
account.cache
|
||||
.observeNotes(filter)
|
||||
.map { notes -> filterOpenPolls(notes) }
|
||||
.flowOn(Dispatchers.IO)
|
||||
combine(
|
||||
account.cache
|
||||
.observeNotes(filter)
|
||||
.map { notes -> filterOpenPolls(notes) },
|
||||
account.settings.dismissedPollNoteIds,
|
||||
) { polls, dismissed ->
|
||||
polls.filter { it.idHex !in dismissed }
|
||||
}.flowOn(Dispatchers.IO)
|
||||
.stateIn(
|
||||
scope,
|
||||
SharingStarted.Eagerly,
|
||||
|
||||
Reference in New Issue
Block a user