feat: enhance image viewer navigation with tap animations and controls visibility

This commit is contained in:
2026-02-09 21:14:10 +05:00
parent efdb93d136
commit 3c37a3b0e5
12 changed files with 1620 additions and 376 deletions

View File

@@ -10,6 +10,7 @@ import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.core.stringSetPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
private val Context.dataStore: DataStore<Preferences> by
@@ -49,6 +50,9 @@ class PreferencesManager(private val context: Context) {
// Pinned Chats (max 3)
val PINNED_CHATS = stringSetPreferencesKey("pinned_chats") // Set of opponent public keys
// Muted Chats (stored as "account::opponentKey")
val MUTED_CHATS = stringSetPreferencesKey("muted_chats")
}
// ═════════════════════════════════════════════════════════════
@@ -238,4 +242,63 @@ class PreferencesManager(private val context: Context) {
}
return wasPinned
}
// ═════════════════════════════════════════════════════════════
// 🔕 MUTED CHATS
// ═════════════════════════════════════════════════════════════
private fun buildMutedKey(account: String, opponentKey: String): String {
val trimmedAccount = account.trim()
val trimmedOpponent = opponentKey.trim()
return if (trimmedAccount.isBlank()) trimmedOpponent else "$trimmedAccount::$trimmedOpponent"
}
private fun parseMutedKeyForAccount(rawKey: String, account: String): String? {
val trimmedAccount = account.trim()
if (rawKey.isBlank()) return null
// Legacy format support: plain opponentKey without account prefix.
if ("::" !in rawKey) return rawKey
val parts = rawKey.split("::", limit = 2)
if (parts.size != 2) return null
val keyAccount = parts[0]
val opponentKey = parts[1]
return if (trimmedAccount.isBlank() || keyAccount == trimmedAccount) opponentKey else null
}
val mutedChats: Flow<Set<String>> =
context.dataStore.data.map { preferences -> preferences[MUTED_CHATS] ?: emptySet() }
fun mutedChatsForAccount(account: String): Flow<Set<String>> =
mutedChats.map { muted ->
muted.mapNotNull { parseMutedKeyForAccount(it, account) }.toSet()
}
suspend fun isChatMuted(account: String, opponentKey: String): Boolean {
if (opponentKey.isBlank()) return false
val current = mutedChats.first()
return current.contains(buildMutedKey(account, opponentKey)) || current.contains(opponentKey)
}
suspend fun setChatMuted(account: String, opponentKey: String, muted: Boolean) {
if (opponentKey.isBlank()) return
val scopedKey = buildMutedKey(account, opponentKey)
context.dataStore.edit { preferences ->
val current = preferences[MUTED_CHATS] ?: emptySet()
preferences[MUTED_CHATS] =
if (muted) {
current + scopedKey
} else {
current - scopedKey - opponentKey
}
}
}
suspend fun toggleMuteChat(account: String, opponentKey: String): Boolean {
if (opponentKey.isBlank()) return false
val mutedNow = !isChatMuted(account, opponentKey)
setChatMuted(account, opponentKey, mutedNow)
return mutedNow
}
}