587 lines
22 KiB
Rust
587 lines
22 KiB
Rust
use rusqlite::Connection;
|
|
use serde::{Deserialize, Deserializer, Serialize};
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct DisplayPreferences {
|
|
pub density: String,
|
|
pub theme: String,
|
|
pub language: String,
|
|
pub compose_format: String,
|
|
pub compose_autosave_ms: i32,
|
|
pub default_priority: String,
|
|
pub request_mdn_default: bool,
|
|
pub attachment_reminder: bool,
|
|
pub deep_index: bool,
|
|
pub animation_mode: Option<String>,
|
|
pub preview_pane: String,
|
|
pub ui_scale: String,
|
|
pub color_preset: String,
|
|
pub accent_color: String,
|
|
pub background_color: String,
|
|
pub panel_color: String,
|
|
pub text_color: String,
|
|
pub font_family: String,
|
|
pub font_size: i32,
|
|
pub row_height: i32,
|
|
pub folder_pane_width: i32,
|
|
pub message_pane_width: i32,
|
|
pub border_radius: i32,
|
|
pub contrast: String,
|
|
pub show_avatars: bool,
|
|
pub show_attachment_icons: bool,
|
|
pub show_flags: bool,
|
|
pub reduce_motion: bool,
|
|
pub date_format: String,
|
|
pub time_format: String,
|
|
pub icon_size: i32,
|
|
pub toolbar_density: String,
|
|
pub sidebar_item_height: i32,
|
|
pub reader_max_width: i32,
|
|
pub composer_width: i32,
|
|
pub selection_color: String,
|
|
pub unread_font_weight: i32,
|
|
pub unread_badge_style: String,
|
|
pub compact_headers: bool,
|
|
pub show_borders: bool,
|
|
pub scrollbar_density: String,
|
|
pub monochrome: bool,
|
|
pub folder_background: String,
|
|
pub message_background: String,
|
|
pub reader_background: String,
|
|
pub compose_font_family: String,
|
|
pub reply_position: String,
|
|
pub quote_style: String,
|
|
pub forward_mode: String,
|
|
pub spellcheck_enabled: bool,
|
|
pub interface_skin: String,
|
|
pub preset_scale_unlocked: bool,
|
|
pub updated_at: String,
|
|
}
|
|
|
|
#[derive(Debug, Default, Deserialize)]
|
|
pub struct UpdateDisplayPreferences {
|
|
pub density: Option<String>,
|
|
pub theme: Option<String>,
|
|
pub language: Option<String>,
|
|
pub compose_format: Option<String>,
|
|
pub compose_autosave_ms: Option<i32>,
|
|
pub default_priority: Option<String>,
|
|
pub request_mdn_default: Option<bool>,
|
|
pub attachment_reminder: Option<bool>,
|
|
pub deep_index: Option<bool>,
|
|
#[serde(default, deserialize_with = "deserialize_animation_mode_field")]
|
|
pub animation_mode: Option<Option<String>>,
|
|
pub preview_pane: Option<String>,
|
|
pub ui_scale: Option<String>,
|
|
pub color_preset: Option<String>,
|
|
pub accent_color: Option<String>,
|
|
pub background_color: Option<String>,
|
|
pub panel_color: Option<String>,
|
|
pub text_color: Option<String>,
|
|
pub font_family: Option<String>,
|
|
pub font_size: Option<i32>,
|
|
pub row_height: Option<i32>,
|
|
pub folder_pane_width: Option<i32>,
|
|
pub message_pane_width: Option<i32>,
|
|
pub border_radius: Option<i32>,
|
|
pub contrast: Option<String>,
|
|
pub show_avatars: Option<bool>,
|
|
pub show_attachment_icons: Option<bool>,
|
|
pub show_flags: Option<bool>,
|
|
pub reduce_motion: Option<bool>,
|
|
pub date_format: Option<String>,
|
|
pub time_format: Option<String>,
|
|
pub icon_size: Option<i32>,
|
|
pub toolbar_density: Option<String>,
|
|
pub sidebar_item_height: Option<i32>,
|
|
pub reader_max_width: Option<i32>,
|
|
pub composer_width: Option<i32>,
|
|
pub selection_color: Option<String>,
|
|
pub unread_font_weight: Option<i32>,
|
|
pub unread_badge_style: Option<String>,
|
|
pub compact_headers: Option<bool>,
|
|
pub show_borders: Option<bool>,
|
|
pub scrollbar_density: Option<String>,
|
|
pub monochrome: Option<bool>,
|
|
pub folder_background: Option<String>,
|
|
pub message_background: Option<String>,
|
|
pub reader_background: Option<String>,
|
|
pub compose_font_family: Option<String>,
|
|
pub reply_position: Option<String>,
|
|
pub quote_style: Option<String>,
|
|
pub forward_mode: Option<String>,
|
|
pub spellcheck_enabled: Option<bool>,
|
|
pub interface_skin: Option<String>,
|
|
pub preset_scale_unlocked: Option<bool>,
|
|
}
|
|
|
|
fn deserialize_animation_mode_field<'de, D>(
|
|
deserializer: D,
|
|
) -> Result<Option<Option<String>>, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
let value = Option::<String>::deserialize(deserializer)?;
|
|
Ok(Some(value))
|
|
}
|
|
|
|
fn defaults() -> DisplayPreferences {
|
|
DisplayPreferences {
|
|
density: "compact".into(),
|
|
theme: "system".into(),
|
|
language: "en".into(),
|
|
compose_format: "html".into(),
|
|
compose_autosave_ms: 3000,
|
|
default_priority: "normal".into(),
|
|
request_mdn_default: false,
|
|
attachment_reminder: true,
|
|
deep_index: false,
|
|
animation_mode: None,
|
|
preview_pane: "right".into(),
|
|
ui_scale: "normal".into(),
|
|
color_preset: "sage".into(),
|
|
accent_color: "#6f9c86".into(),
|
|
background_color: "#121513".into(),
|
|
panel_color: "#181c19".into(),
|
|
text_color: "#edf0ec".into(),
|
|
font_family: "system".into(),
|
|
font_size: 13,
|
|
row_height: 58,
|
|
folder_pane_width: 228,
|
|
message_pane_width: 420,
|
|
border_radius: 4,
|
|
contrast: "normal".into(),
|
|
show_avatars: true,
|
|
show_attachment_icons: true,
|
|
show_flags: true,
|
|
reduce_motion: false,
|
|
date_format: "locale".into(),
|
|
time_format: "locale".into(),
|
|
icon_size: 18,
|
|
toolbar_density: "normal".into(),
|
|
sidebar_item_height: 29,
|
|
reader_max_width: 0,
|
|
composer_width: 900,
|
|
selection_color: "#28352e".into(),
|
|
unread_font_weight: 730,
|
|
unread_badge_style: "dot".into(),
|
|
compact_headers: false,
|
|
show_borders: true,
|
|
scrollbar_density: "thin".into(),
|
|
monochrome: false,
|
|
folder_background: String::new(),
|
|
message_background: String::new(),
|
|
reader_background: String::new(),
|
|
compose_font_family: "system".into(),
|
|
reply_position: "above".into(),
|
|
quote_style: "standard".into(),
|
|
forward_mode: "inline".into(),
|
|
spellcheck_enabled: true,
|
|
interface_skin: "default".into(),
|
|
preset_scale_unlocked: false,
|
|
updated_at: String::new(),
|
|
}
|
|
}
|
|
|
|
pub fn get_preferences(conn: &Connection) -> Result<DisplayPreferences, String> {
|
|
let result = conn.query_row(
|
|
"SELECT density, theme, language, compose_format, deep_index, animation_mode, preview_pane, ui_scale, color_preset, accent_color, background_color, panel_color, text_color, font_family, font_size, row_height, folder_pane_width, message_pane_width, border_radius, contrast, show_avatars, show_attachment_icons, show_flags, reduce_motion, date_format, time_format, compose_autosave_ms, default_priority, request_mdn_default, attachment_reminder, icon_size, toolbar_density, sidebar_item_height, reader_max_width, composer_width, selection_color, unread_font_weight, unread_badge_style, compact_headers, show_borders, scrollbar_density, monochrome, folder_background, message_background, reader_background, compose_font_family, reply_position, quote_style, forward_mode, spellcheck_enabled, interface_skin, preset_scale_unlocked, updated_at FROM display_preferences WHERE id = 1",
|
|
[],
|
|
|row| Ok(DisplayPreferences {
|
|
density: row.get(0)?, theme: row.get(1)?, language: row.get(2)?, compose_format: row.get(3)?,
|
|
deep_index: row.get::<_, i32>(4)? != 0, animation_mode: row.get(5)?, preview_pane: row.get(6)?,
|
|
ui_scale: row.get(7)?, color_preset: row.get(8)?, accent_color: row.get(9)?, background_color: row.get(10)?, panel_color: row.get(11)?, text_color: row.get(12)?, font_family: row.get(13)?,
|
|
font_size: row.get(14)?, row_height: row.get(15)?, folder_pane_width: row.get(16)?, message_pane_width: row.get(17)?,
|
|
border_radius: row.get(18)?, contrast: row.get(19)?, show_avatars: row.get::<_, i32>(20)? != 0,
|
|
show_attachment_icons: row.get::<_, i32>(21)? != 0, show_flags: row.get::<_, i32>(22)? != 0,
|
|
reduce_motion: row.get::<_, i32>(23)? != 0, date_format: row.get(24)?, time_format: row.get(25)?,
|
|
compose_autosave_ms: row.get(26)?, default_priority: row.get(27)?, request_mdn_default: row.get::<_, i32>(28)? != 0, attachment_reminder: row.get::<_, i32>(29)? != 0,
|
|
icon_size: row.get(30)?, toolbar_density: row.get(31)?, sidebar_item_height: row.get(32)?, reader_max_width: row.get(33)?, composer_width: row.get(34)?,
|
|
selection_color: row.get(35)?, unread_font_weight: row.get(36)?, unread_badge_style: row.get(37)?, compact_headers: row.get::<_, i32>(38)? != 0, show_borders: row.get::<_, i32>(39)? != 0,
|
|
scrollbar_density: row.get(40)?, monochrome: row.get::<_, i32>(41)? != 0, folder_background: row.get(42)?, message_background: row.get(43)?, reader_background: row.get(44)?, compose_font_family: row.get(45)?, reply_position: row.get(46)?, quote_style: row.get(47)?, forward_mode: row.get(48)?, spellcheck_enabled: row.get::<_, i32>(49)? != 0, interface_skin: row.get(50)?, preset_scale_unlocked: row.get::<_, i32>(51)? != 0, updated_at: row.get(52)?,
|
|
}),
|
|
);
|
|
match result {
|
|
Ok(value) => Ok(value),
|
|
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(defaults()),
|
|
Err(error) => Err(format!("Failed to get display preferences: {error}")),
|
|
}
|
|
}
|
|
|
|
fn enum_value(name: &str, value: &str, allowed: &[&str]) -> Result<(), String> {
|
|
if allowed.contains(&value) {
|
|
Ok(())
|
|
} else {
|
|
Err(format!("Invalid {name}: {value}"))
|
|
}
|
|
}
|
|
fn range(name: &str, value: i32, min: i32, max: i32) -> Result<(), String> {
|
|
if (min..=max).contains(&value) {
|
|
Ok(())
|
|
} else {
|
|
Err(format!("Invalid {name}: {value}"))
|
|
}
|
|
}
|
|
fn valid_hex(value: &str) -> bool {
|
|
value.len() == 7 && value.starts_with('#') && value[1..].chars().all(|c| c.is_ascii_hexdigit())
|
|
}
|
|
|
|
pub fn update_preferences(
|
|
conn: &Connection,
|
|
data: &UpdateDisplayPreferences,
|
|
) -> Result<DisplayPreferences, String> {
|
|
conn.execute(
|
|
"INSERT OR IGNORE INTO display_preferences (id) VALUES (1)",
|
|
[],
|
|
)
|
|
.map_err(|e| format!("Failed to ensure preferences row: {e}"))?;
|
|
|
|
let mut sets = Vec::new();
|
|
let mut values: Vec<Box<dyn rusqlite::types::ToSql>> = Vec::new();
|
|
let mut idx = 1usize;
|
|
macro_rules! set_text {
|
|
($field:ident, $column:literal) => {
|
|
if let Some(ref value) = data.$field {
|
|
sets.push(format!(concat!($column, " = ?{}"), idx));
|
|
values.push(Box::new(value.clone()));
|
|
idx += 1;
|
|
}
|
|
};
|
|
}
|
|
macro_rules! set_int {
|
|
($field:ident, $column:literal) => {
|
|
if let Some(value) = data.$field {
|
|
sets.push(format!(concat!($column, " = ?{}"), idx));
|
|
values.push(Box::new(value));
|
|
idx += 1;
|
|
}
|
|
};
|
|
}
|
|
macro_rules! set_bool {
|
|
($field:ident, $column:literal) => {
|
|
if let Some(value) = data.$field {
|
|
sets.push(format!(concat!($column, " = ?{}"), idx));
|
|
values.push(Box::new(value as i32));
|
|
idx += 1;
|
|
}
|
|
};
|
|
}
|
|
|
|
if let Some(ref v) = data.density {
|
|
enum_value("density", v, &["compact", "comfortable"])?;
|
|
}
|
|
if let Some(ref v) = data.theme {
|
|
enum_value("theme", v, &["light", "dark", "system"])?;
|
|
}
|
|
if let Some(ref v) = data.compose_format {
|
|
enum_value("compose_format", v, &["html", "text", "markdown"])?;
|
|
}
|
|
if let Some(v) = data.compose_autosave_ms {
|
|
range("compose_autosave_ms", v, 1000, 60000)?;
|
|
}
|
|
if let Some(ref v) = data.default_priority {
|
|
enum_value("default_priority", v, &["high", "normal", "low"])?;
|
|
}
|
|
if let Some(ref v) = data.preview_pane {
|
|
enum_value("preview_pane", v, &["right", "bottom", "off"])?;
|
|
}
|
|
if let Some(ref v) = data.ui_scale {
|
|
enum_value("ui_scale", v, &["small", "normal", "large"])?;
|
|
}
|
|
if let Some(ref v) = data.color_preset {
|
|
enum_value(
|
|
"color_preset",
|
|
v,
|
|
&[
|
|
"sage", "blue", "teal", "cyan", "indigo", "slate", "graphite", "amber", "orange",
|
|
"violet", "rose", "red", "emerald", "lime", "sky", "navy", "plum", "magenta",
|
|
"copper", "sand", "custom",
|
|
],
|
|
)?;
|
|
}
|
|
if let Some(ref v) = data.font_family {
|
|
enum_value("font_family", v, &["system", "inter", "humanist", "mono"])?;
|
|
}
|
|
if let Some(ref v) = data.contrast {
|
|
enum_value("contrast", v, &["soft", "normal", "high"])?;
|
|
}
|
|
if let Some(ref v) = data.language {
|
|
enum_value("language", v, &["en", "pl"])?;
|
|
}
|
|
if let Some(ref v) = data.date_format {
|
|
enum_value("date_format", v, &["locale", "iso", "eu", "us"])?;
|
|
}
|
|
if let Some(ref v) = data.time_format {
|
|
enum_value("time_format", v, &["locale", "24h", "12h"])?;
|
|
}
|
|
if let Some(ref v) = data.toolbar_density {
|
|
enum_value("toolbar_density", v, &["compact", "normal", "spacious"])?;
|
|
}
|
|
if let Some(ref v) = data.unread_badge_style {
|
|
enum_value("unread_badge_style", v, &["dot", "outline", "solid"])?;
|
|
}
|
|
if let Some(ref v) = data.scrollbar_density {
|
|
enum_value("scrollbar_density", v, &["hidden", "thin", "normal"])?;
|
|
}
|
|
if let Some(ref v) = data.compose_font_family {
|
|
enum_value(
|
|
"compose_font_family",
|
|
v,
|
|
&["system", "humanist", "serif", "mono"],
|
|
)?;
|
|
}
|
|
if let Some(ref v) = data.reply_position {
|
|
enum_value("reply_position", v, &["above", "below"])?;
|
|
}
|
|
if let Some(ref v) = data.quote_style {
|
|
enum_value("quote_style", v, &["standard", "compact", "none"])?;
|
|
}
|
|
if let Some(ref v) = data.forward_mode {
|
|
enum_value("forward_mode", v, &["inline", "attachment"])?;
|
|
}
|
|
if let Some(ref v) = data.interface_skin {
|
|
enum_value(
|
|
"interface_skin",
|
|
v,
|
|
&[
|
|
"default",
|
|
"rustbox",
|
|
"roundcube-classic",
|
|
"roundcube-larry",
|
|
"roundcube-elastic",
|
|
"clean",
|
|
"gmail",
|
|
"proton",
|
|
"zoho",
|
|
"outlook",
|
|
"minimal",
|
|
"graphite",
|
|
"high-contrast",
|
|
"fastmail",
|
|
"apple-mail",
|
|
"yahoo",
|
|
"hey",
|
|
"nord",
|
|
],
|
|
)?;
|
|
}
|
|
if let Some(v) = data.font_size {
|
|
range("font_size", v, 11, 18)?;
|
|
}
|
|
if let Some(v) = data.row_height {
|
|
range("row_height", v, 42, 88)?;
|
|
}
|
|
if let Some(v) = data.folder_pane_width {
|
|
range("folder_pane_width", v, 180, 360)?;
|
|
}
|
|
if let Some(v) = data.message_pane_width {
|
|
range("message_pane_width", v, 300, 720)?;
|
|
}
|
|
if let Some(v) = data.border_radius {
|
|
range("border_radius", v, 0, 12)?;
|
|
}
|
|
if let Some(v) = data.icon_size {
|
|
range("icon_size", v, 14, 28)?;
|
|
}
|
|
if let Some(v) = data.sidebar_item_height {
|
|
range("sidebar_item_height", v, 24, 48)?;
|
|
}
|
|
if let Some(v) = data.reader_max_width {
|
|
if v != 0 {
|
|
range("reader_max_width", v, 560, 1400)?;
|
|
}
|
|
}
|
|
if let Some(v) = data.composer_width {
|
|
range("composer_width", v, 640, 1400)?;
|
|
}
|
|
if let Some(v) = data.unread_font_weight {
|
|
range("unread_font_weight", v, 500, 800)?;
|
|
}
|
|
if let Some(ref v) = data.accent_color {
|
|
if !valid_hex(v) {
|
|
return Err(format!("Invalid accent_color: {v}"));
|
|
}
|
|
}
|
|
if let Some(ref v) = data.background_color {
|
|
if !valid_hex(v) {
|
|
return Err(format!("Invalid background_color: {v}"));
|
|
}
|
|
}
|
|
if let Some(ref v) = data.panel_color {
|
|
if !valid_hex(v) {
|
|
return Err(format!("Invalid panel_color: {v}"));
|
|
}
|
|
}
|
|
if let Some(ref v) = data.text_color {
|
|
if !valid_hex(v) {
|
|
return Err(format!("Invalid text_color: {v}"));
|
|
}
|
|
}
|
|
if let Some(ref v) = data.selection_color {
|
|
if !valid_hex(v) {
|
|
return Err(format!("Invalid selection_color: {v}"));
|
|
}
|
|
}
|
|
for (name, value) in [
|
|
("folder_background", data.folder_background.as_deref()),
|
|
("message_background", data.message_background.as_deref()),
|
|
("reader_background", data.reader_background.as_deref()),
|
|
] {
|
|
if let Some(v) = value {
|
|
if !v.is_empty() && !valid_hex(v) {
|
|
return Err(format!("Invalid {name}: {v}"));
|
|
}
|
|
}
|
|
}
|
|
if let Some(ref mode) = data.animation_mode {
|
|
if let Some(v) = mode {
|
|
enum_value("animation_mode", v, &["rich", "medium", "subtle", "off"])?;
|
|
}
|
|
}
|
|
|
|
set_text!(density, "density");
|
|
set_text!(theme, "theme");
|
|
set_text!(language, "language");
|
|
set_text!(compose_format, "compose_format");
|
|
set_int!(compose_autosave_ms, "compose_autosave_ms");
|
|
set_text!(default_priority, "default_priority");
|
|
set_bool!(request_mdn_default, "request_mdn_default");
|
|
set_bool!(attachment_reminder, "attachment_reminder");
|
|
set_bool!(deep_index, "deep_index");
|
|
if let Some(ref value) = data.animation_mode {
|
|
sets.push(format!("animation_mode = ?{}", idx));
|
|
values.push(Box::new(value.clone()));
|
|
idx += 1;
|
|
}
|
|
set_text!(preview_pane, "preview_pane");
|
|
set_text!(ui_scale, "ui_scale");
|
|
set_text!(color_preset, "color_preset");
|
|
set_text!(accent_color, "accent_color");
|
|
set_text!(background_color, "background_color");
|
|
set_text!(panel_color, "panel_color");
|
|
set_text!(text_color, "text_color");
|
|
set_text!(font_family, "font_family");
|
|
set_int!(font_size, "font_size");
|
|
set_int!(row_height, "row_height");
|
|
set_int!(folder_pane_width, "folder_pane_width");
|
|
set_int!(message_pane_width, "message_pane_width");
|
|
set_int!(border_radius, "border_radius");
|
|
set_text!(contrast, "contrast");
|
|
set_bool!(show_avatars, "show_avatars");
|
|
set_bool!(show_attachment_icons, "show_attachment_icons");
|
|
set_bool!(show_flags, "show_flags");
|
|
set_bool!(reduce_motion, "reduce_motion");
|
|
set_text!(date_format, "date_format");
|
|
set_text!(time_format, "time_format");
|
|
set_int!(icon_size, "icon_size");
|
|
set_text!(toolbar_density, "toolbar_density");
|
|
set_int!(sidebar_item_height, "sidebar_item_height");
|
|
set_int!(reader_max_width, "reader_max_width");
|
|
set_int!(composer_width, "composer_width");
|
|
set_text!(selection_color, "selection_color");
|
|
set_int!(unread_font_weight, "unread_font_weight");
|
|
set_text!(unread_badge_style, "unread_badge_style");
|
|
set_bool!(compact_headers, "compact_headers");
|
|
set_bool!(show_borders, "show_borders");
|
|
set_text!(scrollbar_density, "scrollbar_density");
|
|
set_bool!(monochrome, "monochrome");
|
|
set_text!(folder_background, "folder_background");
|
|
set_text!(message_background, "message_background");
|
|
set_text!(reader_background, "reader_background");
|
|
set_text!(compose_font_family, "compose_font_family");
|
|
set_text!(reply_position, "reply_position");
|
|
set_text!(quote_style, "quote_style");
|
|
set_text!(forward_mode, "forward_mode");
|
|
set_bool!(spellcheck_enabled, "spellcheck_enabled");
|
|
set_text!(interface_skin, "interface_skin");
|
|
set_bool!(preset_scale_unlocked, "preset_scale_unlocked");
|
|
|
|
if sets.is_empty() {
|
|
return get_preferences(conn);
|
|
}
|
|
sets.push("updated_at = datetime('now')".to_string());
|
|
let sql = format!(
|
|
"UPDATE display_preferences SET {} WHERE id = ?{}",
|
|
sets.join(", "),
|
|
idx
|
|
);
|
|
values.push(Box::new(1_i32));
|
|
let refs: Vec<&dyn rusqlite::types::ToSql> = values.iter().map(|v| v.as_ref()).collect();
|
|
conn.execute(&sql, refs.as_slice())
|
|
.map_err(|e| format!("Failed to update display preferences: {e}"))?;
|
|
get_preferences(conn)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::db::pool::open_test_db;
|
|
|
|
#[test]
|
|
fn defaults_and_personalization_roundtrip() {
|
|
let conn = open_test_db();
|
|
let defaults = get_preferences(&conn).unwrap();
|
|
assert_eq!(defaults.color_preset, "sage");
|
|
let update: UpdateDisplayPreferences = serde_json::from_value(serde_json::json!({
|
|
"theme":"dark", "language":"pl", "color_preset":"blue", "accent_color":"#336699",
|
|
"font_size":15, "row_height":64, "folder_pane_width":250, "message_pane_width":480,
|
|
"show_avatars":false, "reduce_motion":true, "interface_skin":"roundcube-elastic", "preset_scale_unlocked":true
|
|
}))
|
|
.unwrap();
|
|
let prefs = update_preferences(&conn, &update).unwrap();
|
|
assert_eq!(prefs.language, "pl");
|
|
assert_eq!(prefs.accent_color, "#336699");
|
|
assert_eq!(prefs.font_size, 15);
|
|
assert!(!prefs.show_avatars);
|
|
assert_eq!(prefs.interface_skin, "roundcube-elastic");
|
|
assert!(prefs.preset_scale_unlocked);
|
|
}
|
|
|
|
#[test]
|
|
fn built_in_interface_skins_are_accepted() {
|
|
for skin in [
|
|
"default",
|
|
"rustbox",
|
|
"roundcube-classic",
|
|
"roundcube-larry",
|
|
"roundcube-elastic",
|
|
"clean",
|
|
"gmail",
|
|
"proton",
|
|
"zoho",
|
|
"outlook",
|
|
"minimal",
|
|
"graphite",
|
|
"high-contrast",
|
|
"fastmail",
|
|
"apple-mail",
|
|
"yahoo",
|
|
"hey",
|
|
"nord",
|
|
] {
|
|
let conn = open_test_db();
|
|
let update: UpdateDisplayPreferences =
|
|
serde_json::from_value(serde_json::json!({"interface_skin": skin})).unwrap();
|
|
let prefs = update_preferences(&conn, &update).unwrap();
|
|
assert_eq!(prefs.interface_skin, skin);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_interface_skin_is_rejected() {
|
|
let conn = open_test_db();
|
|
let update: UpdateDisplayPreferences =
|
|
serde_json::from_value(serde_json::json!({"interface_skin":"roundcube-unknown"}))
|
|
.unwrap();
|
|
assert!(update_preferences(&conn, &update).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_customization_is_rejected() {
|
|
let conn = open_test_db();
|
|
let update: UpdateDisplayPreferences =
|
|
serde_json::from_value(serde_json::json!({"accent_color":"red"})).unwrap();
|
|
assert!(update_preferences(&conn, &update).is_err());
|
|
}
|
|
}
|