1 parent
fb9eab87eb
commit
d91212d24c
40 files changed
+585
-380
No files matched your search
@@ -55,6 +55,7 @@ pub struct DisplayPreferences {
|
||||
pub spellcheck_enabled: bool,
|
||||
pub interface_skin: String,
|
||||
pub preset_scale_unlocked: bool,
|
||||
pub adaptive_email_colors: bool,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
@@ -113,6 +114,7 @@ pub struct UpdateDisplayPreferences {
|
||||
pub spellcheck_enabled: Option<bool>,
|
||||
pub interface_skin: Option<String>,
|
||||
pub preset_scale_unlocked: Option<bool>,
|
||||
pub adaptive_email_colors: Option<bool>,
|
||||
}
|
||||
|
||||
fn deserialize_animation_mode_field<'de, D>(
|
||||
@@ -179,6 +181,7 @@ fn defaults() -> DisplayPreferences {
|
||||
spellcheck_enabled: true,
|
||||
interface_skin: "default".into(),
|
||||
preset_scale_unlocked: false,
|
||||
adaptive_email_colors: false,
|
||||
updated_at: String::new(),
|
||||
}
|
||||
}
|
||||
@@ -238,7 +241,8 @@ pub fn get_preferences(conn: &Connection) -> Result<DisplayPreferences, String>
|
||||
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)?,
|
||||
adaptive_email_colors: row.get::<_, i32>(52)? != 0,
|
||||
updated_at: row.get(53)?,
|
||||
})
|
||||
});
|
||||
match result {
|
||||
@@ -539,6 +543,7 @@ pub fn update_preferences(
|
||||
set_bool!(spellcheck_enabled, "spellcheck_enabled");
|
||||
set_text!(interface_skin, "interface_skin");
|
||||
set_bool!(preset_scale_unlocked, "preset_scale_unlocked");
|
||||
set_bool!(adaptive_email_colors, "adaptive_email_colors");
|
||||
|
||||
if sets.is_empty() {
|
||||
return get_preferences(conn);
|
||||
@@ -568,7 +573,7 @@ mod tests {
|
||||
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
|
||||
"show_avatars":false, "reduce_motion":true, "interface_skin":"roundcube-elastic", "preset_scale_unlocked":true, "adaptive_email_colors":true
|
||||
}))
|
||||
.unwrap();
|
||||
let prefs = update_preferences(&conn, &update).unwrap();
|
||||
@@ -578,6 +583,7 @@ mod tests {
|
||||
assert!(!prefs.show_avatars);
|
||||
assert_eq!(prefs.interface_skin, "roundcube-elastic");
|
||||
assert!(prefs.preset_scale_unlocked);
|
||||
assert!(prefs.adaptive_email_colors);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -107,6 +107,7 @@ mod tests {
|
||||
&conn,
|
||||
&mail_preferences::UpdateMailPreferences {
|
||||
messages_per_page: None,
|
||||
message_loading_mode: None,
|
||||
mark_read_delay_ms: None,
|
||||
remote_images: None,
|
||||
preferred_body: None,
|
||||
|
||||
+27
-11
@@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct MailPreferences {
|
||||
pub messages_per_page: u32,
|
||||
pub message_loading_mode: String,
|
||||
pub mark_read_delay_ms: u64,
|
||||
pub remote_images: String,
|
||||
pub preferred_body: String,
|
||||
@@ -20,6 +21,7 @@ pub struct MailPreferences {
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct UpdateMailPreferences {
|
||||
pub messages_per_page: Option<u32>,
|
||||
pub message_loading_mode: Option<String>,
|
||||
pub mark_read_delay_ms: Option<u64>,
|
||||
pub remote_images: Option<String>,
|
||||
pub preferred_body: Option<String>,
|
||||
@@ -36,17 +38,18 @@ pub fn get_preferences(conn: &Connection) -> Result<MailPreferences, String> {
|
||||
conn.query_row(crate::dbq!(db_mail_preferences::Q_001), [], |row| {
|
||||
Ok(MailPreferences {
|
||||
messages_per_page: row.get::<_, u32>(0)?,
|
||||
mark_read_delay_ms: row.get::<_, u64>(1)?,
|
||||
remote_images: row.get(2)?,
|
||||
preferred_body: row.get(3)?,
|
||||
thread_mode: row.get(4)?,
|
||||
collapse_quotes: row.get::<_, i32>(5)? != 0,
|
||||
show_snippets: row.get::<_, i32>(6)? != 0,
|
||||
default_sort: row.get(7)?,
|
||||
flagged_on_top: row.get::<_, i32>(8)? != 0,
|
||||
sync_imap_keywords: row.get::<_, i32>(9)? != 0,
|
||||
compact_wide_rows: row.get::<_, i32>(10)? != 0,
|
||||
updated_at: row.get(11)?,
|
||||
message_loading_mode: row.get(1)?,
|
||||
mark_read_delay_ms: row.get::<_, u64>(2)?,
|
||||
remote_images: row.get(3)?,
|
||||
preferred_body: row.get(4)?,
|
||||
thread_mode: row.get(5)?,
|
||||
collapse_quotes: row.get::<_, i32>(6)? != 0,
|
||||
show_snippets: row.get::<_, i32>(7)? != 0,
|
||||
default_sort: row.get(8)?,
|
||||
flagged_on_top: row.get::<_, i32>(9)? != 0,
|
||||
sync_imap_keywords: row.get::<_, i32>(10)? != 0,
|
||||
compact_wide_rows: row.get::<_, i32>(11)? != 0,
|
||||
updated_at: row.get(12)?,
|
||||
})
|
||||
})
|
||||
.map_err(|error| format!("Failed to read mail preferences: {error}"))
|
||||
@@ -64,6 +67,11 @@ pub fn update_preferences(
|
||||
{
|
||||
return Err("messages_per_page must be between 20 and 300".to_string());
|
||||
}
|
||||
if let Some(ref value) = data.message_loading_mode
|
||||
&& !matches!(value.as_str(), "pagination" | "progressive" | "all")
|
||||
{
|
||||
return Err("message_loading_mode must be pagination, progressive or all".to_string());
|
||||
}
|
||||
if let Some(value) = data.mark_read_delay_ms
|
||||
&& value > 30_000
|
||||
{
|
||||
@@ -102,6 +110,10 @@ pub fn update_preferences(
|
||||
|
||||
let current = get_preferences(conn)?;
|
||||
let messages_per_page = data.messages_per_page.unwrap_or(current.messages_per_page);
|
||||
let message_loading_mode = data
|
||||
.message_loading_mode
|
||||
.as_deref()
|
||||
.unwrap_or(¤t.message_loading_mode);
|
||||
let mark_read_delay_ms = data
|
||||
.mark_read_delay_ms
|
||||
.unwrap_or(current.mark_read_delay_ms);
|
||||
@@ -130,6 +142,7 @@ pub fn update_preferences(
|
||||
crate::dbq!(db_mail_preferences::Q_003),
|
||||
params![
|
||||
messages_per_page,
|
||||
message_loading_mode,
|
||||
mark_read_delay_ms,
|
||||
remote_images,
|
||||
preferred_body,
|
||||
@@ -157,12 +170,14 @@ mod tests {
|
||||
let conn = open_test_db();
|
||||
let defaults = get_preferences(&conn).unwrap();
|
||||
assert_eq!(defaults.messages_per_page, 300);
|
||||
assert_eq!(defaults.message_loading_mode, "pagination");
|
||||
assert_eq!(defaults.remote_images, "ask");
|
||||
|
||||
let updated = update_preferences(
|
||||
&conn,
|
||||
&UpdateMailPreferences {
|
||||
messages_per_page: Some(100),
|
||||
message_loading_mode: Some("progressive".to_string()),
|
||||
mark_read_delay_ms: Some(1500),
|
||||
remote_images: Some("never".to_string()),
|
||||
preferred_body: Some("text".to_string()),
|
||||
@@ -177,6 +192,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(updated.messages_per_page, 100);
|
||||
assert_eq!(updated.message_loading_mode, "progressive");
|
||||
assert_eq!(updated.mark_read_delay_ms, 1500);
|
||||
assert_eq!(updated.thread_mode, "flat");
|
||||
assert!(updated.flagged_on_top);
|
||||
|
||||
+17
-59
@@ -16,65 +16,23 @@ use tokio::sync::Mutex as AsyncMutex;
|
||||
|
||||
use crate::config::AppConfig;
|
||||
|
||||
pub const BASE_SCHEMA_VERSION: u32 = 46;
|
||||
pub const BASE_SCHEMA_VERSION: u32 = 49;
|
||||
const SQLITE_SCHEMA: &str = include_str!("../../migrations/sqlite/schema.sql");
|
||||
const MYSQL_SCHEMA: &str = include_str!("../../migrations/mysql/schema.sql");
|
||||
const PGSQL_SCHEMA: &str = include_str!("../../migrations/pgsql/schema.sql");
|
||||
|
||||
const SQLITE_MIGRATIONS: &[(u32, &str)] = &[
|
||||
(
|
||||
47,
|
||||
include_str!("../../migrations/sqlite/V047__blob_keys_and_search_outbox.sql"),
|
||||
),
|
||||
(
|
||||
48,
|
||||
include_str!("../../migrations/sqlite/V048__folder_counter_generation.sql"),
|
||||
),
|
||||
(
|
||||
49,
|
||||
include_str!("../../migrations/sqlite/V049__blob_delete_outbox.sql"),
|
||||
),
|
||||
(
|
||||
50,
|
||||
include_str!("../../migrations/sqlite/V050__compact_rows_and_signature_mode.sql"),
|
||||
),
|
||||
];
|
||||
const MYSQL_MIGRATIONS: &[(u32, &str)] = &[
|
||||
(
|
||||
47,
|
||||
include_str!("../../migrations/mysql/V047__blob_keys_and_search_outbox.sql"),
|
||||
),
|
||||
(
|
||||
48,
|
||||
include_str!("../../migrations/mysql/V048__folder_counter_generation.sql"),
|
||||
),
|
||||
(
|
||||
49,
|
||||
include_str!("../../migrations/mysql/V049__blob_delete_outbox.sql"),
|
||||
),
|
||||
(
|
||||
50,
|
||||
include_str!("../../migrations/mysql/V050__compact_rows_and_signature_mode.sql"),
|
||||
),
|
||||
];
|
||||
const PGSQL_MIGRATIONS: &[(u32, &str)] = &[
|
||||
(
|
||||
47,
|
||||
include_str!("../../migrations/pgsql/V047__blob_keys_and_search_outbox.sql"),
|
||||
),
|
||||
(
|
||||
48,
|
||||
include_str!("../../migrations/pgsql/V048__folder_counter_generation.sql"),
|
||||
),
|
||||
(
|
||||
49,
|
||||
include_str!("../../migrations/pgsql/V049__blob_delete_outbox.sql"),
|
||||
),
|
||||
(
|
||||
50,
|
||||
include_str!("../../migrations/pgsql/V050__compact_rows_and_signature_mode.sql"),
|
||||
),
|
||||
];
|
||||
const SQLITE_MIGRATIONS: &[(u32, &str)] = &[(
|
||||
50,
|
||||
include_str!("../../migrations/sqlite/V050__compact_rows_and_signature_mode.sql"),
|
||||
)];
|
||||
const MYSQL_MIGRATIONS: &[(u32, &str)] = &[(
|
||||
50,
|
||||
include_str!("../../migrations/mysql/V050__compact_rows_and_signature_mode.sql"),
|
||||
)];
|
||||
const PGSQL_MIGRATIONS: &[(u32, &str)] = &[(
|
||||
50,
|
||||
include_str!("../../migrations/pgsql/V050__compact_rows_and_signature_mode.sql"),
|
||||
)];
|
||||
|
||||
const TENANT_TABLES: &[&str] = &[
|
||||
"blob_delete_outbox",
|
||||
@@ -1861,7 +1819,7 @@ fn read_legacy_sqlite(path: &Path) -> Result<Vec<LegacyTableData>, String> {
|
||||
.map_err(|e| format!("failed to read legacy SQLite schema version: {e}"))?;
|
||||
if version < BASE_SCHEMA_VERSION {
|
||||
return Err(format!(
|
||||
"legacy SQLite schema version {version} is older than the legacy RustBox baseline {BASE_SCHEMA_VERSION}; upgrade with RustBox 0.1.0 first"
|
||||
"legacy SQLite schema version {version} is older than the consolidated RustBox baseline {BASE_SCHEMA_VERSION}; upgrade to schema V049 first"
|
||||
));
|
||||
}
|
||||
let latest_supported_sqlite = SQLITE_MIGRATIONS
|
||||
@@ -2222,7 +2180,7 @@ fn apply_sqlite_migrations(
|
||||
) -> Result<(), String> {
|
||||
if current < BASE_SCHEMA_VERSION {
|
||||
return Err(format!(
|
||||
"database schema version {current} is older than the legacy RustBox baseline {BASE_SCHEMA_VERSION}; upgrade with RustBox 0.1.0 first"
|
||||
"database schema version {current} is older than the consolidated RustBox baseline {BASE_SCHEMA_VERSION}; upgrade to schema V049 first"
|
||||
));
|
||||
}
|
||||
let latest = latest_schema_version();
|
||||
@@ -2667,7 +2625,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_sqlite_v046_is_imported_and_backed_up() {
|
||||
fn legacy_sqlite_v050_is_imported_and_backed_up() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let data_dir = temp.path();
|
||||
let user_hash = format!("{:x}", Sha256::digest(b"legacy-user"));
|
||||
@@ -2683,7 +2641,7 @@ mod tests {
|
||||
legacy
|
||||
.execute_batch(&tenant_seed_sql(DatabaseBackend::Sqlite, ""))
|
||||
.unwrap();
|
||||
legacy.execute_batch("PRAGMA user_version=46;").unwrap();
|
||||
legacy.execute_batch("PRAGMA user_version=50;").unwrap();
|
||||
legacy.execute(
|
||||
"INSERT INTO display_preferences(id, theme, accent_color) VALUES(1, 'dark', '#123456')",
|
||||
[],
|
||||
|
||||
@@ -53,6 +53,7 @@ mod tests {
|
||||
&conn,
|
||||
&UpdateMailPreferences {
|
||||
messages_per_page: Some(100),
|
||||
message_loading_mode: None,
|
||||
mark_read_delay_ms: None,
|
||||
remote_images: None,
|
||||
preferred_body: None,
|
||||
|
||||
Reference in new issue
Block a user