1use std::collections::HashMap;
6
7use crate::notify::ScopePrefs;
8
9fn row_to_prefs(row: &rusqlite::Row<'_>) -> rusqlite::Result<(String, ScopePrefs)> {
10 let id: String = row.get(0)?;
11 let level: Option<u8> = row.get::<_, Option<i64>>(1)?.map(|v| v as u8);
12 let mute_until: i64 = row.get(2)?;
13 let suppress: Option<bool> = row.get::<_, Option<i64>>(3)?.map(|v| v != 0);
14 Ok((id, ScopePrefs {
15 level: level.and_then(crate::notify::NotifyLevel::from_u8),
16 mute_until,
17 suppress_everyone: suppress,
18 }))
19}
20
21pub fn load_all() -> Result<HashMap<String, ScopePrefs>, String> {
24 let conn = super::get_db_connection_guard_static()?;
25 let mut stmt = conn
26 .prepare("SELECT scope_id, level, mute_until, suppress_everyone FROM notify_prefs")
27 .map_err(|e| format!("prepare notify_prefs: {e}"))?;
28 let rows = stmt
29 .query_map([], row_to_prefs)
30 .map_err(|e| format!("query notify_prefs: {e}"))?;
31 let mut out = HashMap::new();
32 for row in rows {
33 let (id, prefs) = row.map_err(|e| format!("read notify_prefs: {e}"))?;
34 out.insert(id, prefs);
35 }
36 Ok(out)
37}
38
39pub fn save(scope_id: &str, prefs: &ScopePrefs) -> Result<(), String> {
42 let conn = super::get_write_connection_guard_static()?;
43 if prefs.is_default() {
44 conn.execute("DELETE FROM notify_prefs WHERE scope_id = ?1", rusqlite::params![scope_id])
45 .map_err(|e| format!("clear notify_prefs: {e}"))?;
46 return Ok(());
47 }
48 conn.execute(
49 "INSERT OR REPLACE INTO notify_prefs (scope_id, level, mute_until, suppress_everyone) \
50 VALUES (?1, ?2, ?3, ?4)",
51 rusqlite::params![
52 scope_id,
53 prefs.level.map(|l| l.as_u8() as i64),
54 prefs.mute_until,
55 prefs.suppress_everyone.map(|s| s as i64),
56 ],
57 )
58 .map_err(|e| format!("save notify_prefs: {e}"))?;
59 Ok(())
60}
61
62pub fn clear_expired_mutes(now_ms: i64) -> Result<(), String> {
64 let conn = super::get_write_connection_guard_static()?;
65 conn.execute(
66 "UPDATE notify_prefs SET mute_until = 0 WHERE mute_until > 0 AND mute_until <= ?1",
67 rusqlite::params![now_ms],
68 )
69 .map_err(|e| format!("clear expired mutes: {e}"))?;
70 conn.execute(
71 "DELETE FROM notify_prefs WHERE level IS NULL AND mute_until = 0 AND suppress_everyone IS NULL",
72 [],
73 )
74 .map_err(|e| format!("prune notify_prefs: {e}"))?;
75 Ok(())
76}
77
78pub fn replace_all(scopes: &HashMap<String, ScopePrefs>) -> Result<(), String> {
82 let mut conn = super::get_write_connection_guard_static()?;
83 let tx = conn.transaction().map_err(|e| format!("begin notify tx: {e}"))?;
84 tx.execute("DELETE FROM notify_prefs", [])
85 .map_err(|e| format!("clear notify_prefs: {e}"))?;
86 for (scope_id, prefs) in scopes.iter().filter(|(_, p)| !p.is_default()) {
87 tx.execute(
88 "INSERT OR REPLACE INTO notify_prefs (scope_id, level, mute_until, suppress_everyone) \
89 VALUES (?1, ?2, ?3, ?4)",
90 rusqlite::params![
91 scope_id,
92 prefs.level.map(|l| l.as_u8() as i64),
93 prefs.mute_until,
94 prefs.suppress_everyone.map(|s| s as i64),
95 ],
96 )
97 .map_err(|e| format!("write notify_prefs: {e}"))?;
98 }
99 tx.commit().map_err(|e| format!("commit notify tx: {e}"))?;
100 Ok(())
101}
102
103pub fn live_channel_ids() -> Result<std::collections::HashSet<String>, String> {
107 let conn = super::get_db_connection_guard_static()?;
108 let mut stmt = conn
109 .prepare("SELECT channel_id FROM community_channels")
110 .map_err(|e| format!("prepare live channels: {e}"))?;
111 let rows = stmt
112 .query_map([], |row| row.get::<_, String>(0))
113 .map_err(|e| format!("query live channels: {e}"))?;
114 let mut out = std::collections::HashSet::new();
115 for row in rows {
116 out.insert(row.map_err(|e| format!("read live channels: {e}"))?);
117 }
118 Ok(out)
119}