From a116890e209d1267ae5742b413f9064e94b78c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Sun, 1 Nov 2026 21:36:04 +0100 Subject: [PATCH] v0.2.9 --- CHANGELOG.md | 9 +++++ Cargo.lock | 2 +- Cargo.toml | 2 +- NOTICE.md | 2 +- README.md | 6 ++-- RELEASE-MANIFEST.txt | 29 ++++++---------- RELEASE-VALIDATION.txt | 24 ++++++------- config.example.toml | 2 +- docs/IMPLEMENTATION_STATUS.md | 12 +++++-- docs/RELEASING.md | 2 +- docs/STORAGE_SEARCH_OPERATIONS.md | 4 +-- migrations/README.md | 4 +-- migrations/mysql/schema.sql | 2 +- migrations/pgsql/schema.sql | 2 +- migrations/sqlite/schema.sql | 2 +- packaging/rustbox.toml | 2 +- scripts/validate-release-static.py | 25 +++++++++---- src/db/mail_preferences.rs | 6 ++-- src/db/pool.rs | 6 ++-- src/db/settings.rs | 4 +-- src/queries/mysql/queries.rs | 3 +- src/queries/pgsql/queries.rs | 3 +- src/queries/sqlite/queries.rs | 3 +- src/routes/messages/mod.rs | 4 +-- src/routes/messages/types.rs | 2 +- src/web/i18n/en.json | 3 +- src/web/i18n/pl.json | 3 +- src/web/index.html | 3 +- src/web/js/00-bootstrap.js | 9 +++-- src/web/js/10-core.js | 1 + src/web/js/20-preferences-auth.js | 18 ++++++++-- src/web/js/60-mail-actions-search.js | 10 ++++++ src/web/js/80-settings-realtime.js | 52 +++++++++++++++++++++++++++- src/web/js/95-bindings-boot.js | 8 +++-- 34 files changed, 186 insertions(+), 83 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43f3b2a..d8c82ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # RustBox changelog +### 0.2.9 + +- Made realtime new-mail notifications less aggressive: startup synchronization is silent, notifications are armed only after the application is open, and bulk move/copy/archive/junk/delete destinations suppress self-generated `NewMessages` toasts while the bulk summary toast remains. +- Added a success toast after a completed authenticated startup/login. +- Restored browser/system modifier shortcuts by limiting RustBox single-key shortcuts to unmodified keys; `Ctrl/Cmd+C`, `Ctrl/Cmd+V`, `Ctrl/Cmd+X`, `Ctrl/Cmd+A`, `Ctrl/Cmd+F`, `Ctrl/Cmd+R` and `Ctrl/Cmd+N` are no longer intercepted by mail actions. +- Changed the default messages-per-page setting from 50 to 300 and raised the supported folder-page limit to 300. +- No schema migration is required; V049 remains the next migration number. +- Bumped the application/source release to 0.2.9. + ### 0.2.8 - Scoped default logging so RustBox can run at DEBUG/INFO without enabling verbose dependency logs; Tantivy merge/GC/file-watcher noise and its internal filesystem/source paths are hidden unless explicitly enabled through a custom `RUST_LOG` target filter. Development and deployment examples now use target-scoped filters. diff --git a/Cargo.lock b/Cargo.lock index b5e63d6..6239f78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3506,7 +3506,7 @@ dependencies = [ [[package]] name = "rustbox" -version = "0.2.8" +version = "0.2.9" dependencies = [ "aes-gcm", "async-imap", diff --git a/Cargo.toml b/Cargo.toml index fc9cf45..aad5b62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustbox" -version = "0.2.8" +version = "0.2.9" edition = "2024" rust-version = "1.99.1" default-run = "rustbox" diff --git a/NOTICE.md b/NOTICE.md index 21058cf..dfea5d9 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1,6 +1,6 @@ # RustBox attribution / notice -RustBox 0.2.8 is derived from the Oxi webmail project: +RustBox 0.2.9 is derived from the Oxi webmail project: - Upstream: `c0h1b4/oxi` - Base snapshot: `7f1507c486b78cf33e54beb701dd460d3bd2bd5d` diff --git a/README.md b/README.md index 03e1ca0..b50085f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# RustBox 0.2.8 +# RustBox 0.2.9 RustBox is a webmail derived from `c0h1b4/oxi`. The consumed Oxi revision is recorded in `UPSTREAM_BASE`. @@ -34,7 +34,7 @@ rustbox/ `build.rs` combines the CSS modules from `src/web/css/manifest.txt` and JavaScript modules from `src/web/js/manifest.txt`. Cargo embeds the generated CSS/JS bundles, HTML and SVG sprite in the RustBox executable. There is no standalone source `src/web/app.js`; `src/web/` is the single source of truth and Cargo generates the embedded bundles in `OUT_DIR`. -## Storage and search in 0.2.8 +## Storage and search in 0.2.9 RustBox separates three concerns: SQL metadata, blob storage and search. The SQL engine does **not** decide whether `data_dir` may be used. A single-node PostgreSQL/MySQL deployment may still use filesystem blobs and local Tantivy. In cluster mode, local node state is rejected unless the configured backend is explicitly shared. @@ -235,7 +235,7 @@ Initialize Git after extracting the release archive: ```bash git init git add . -git commit -m "RustBox 0.2.8 release" +git commit -m "RustBox 0.2.9 release" ./scripts/setup-upstream.sh ``` diff --git a/RELEASE-MANIFEST.txt b/RELEASE-MANIFEST.txt index ff4b32c..e680ce2 100644 --- a/RELEASE-MANIFEST.txt +++ b/RELEASE-MANIFEST.txt @@ -1,21 +1,12 @@ -RustBox 0.2.8 release manifest - -0.2.8 feature/configuration changes: -- fixes folder unread counts by treating IMAP STATUS UNSEEN as authoritative and never using SELECT/EXAMINE UNSEEN as a count -- falls back to UID SEARCH UNSEEN when STATUS is unavailable for the selected mailbox -- reconciles MESSAGES/UNSEEN even when HIGHESTMODSEQ is unchanged and rejects out-of-order stale counter writes with compare-and-set/retry -- updates individual message rows and folder counters optimistically/live for read/unread/flag events instead of rebuilding the message list, then reconciles them to authoritative IMAP state -- shows wide folder badges as total / unread, hides the unread suffix at zero, and switches to compact counters in narrow sidebars -- exposes IMAP header-cache and deep-index backlog through the activity indicator and tooltip -- keeps counter STATUS separate from the CONDSTORE cursor, falls back to full UID/FLAGS reconciliation when needed, and refuses to apply partial IMAP streams -- keeps security_headers_enabled opt-in, centralizes the RustBox-added browser security profile behind that switch, and applies the full document-only profile only to HTML/XHTML -- uses external same-origin JavaScript with `script-src 'self'`, disables inline script attributes, restricts stylesheets to `style-src 'self'`, and permits only dynamic style attributes via `style-src-attr 'unsafe-inline'` -- applies no-store, nosniff, same-origin CORP and an anti-framing `frame-ancestors 'none'`/X-Frame-Options policy to private API responses; the full document CSP plus Permissions-Policy/COOP remain HTML-only, and assets receive same-origin CORP where applicable -- leaves HSTS to the HTTPS/TLS-terminating deployment layer -- minimizes the public auth policy to rememberMeEnabled and moves optional feature availability behind authenticated /api/auth/session-policy -- adds/extends regression coverage and offline release checks for the changed behavior -- adds runtime migration V048 on SQLite/MySQL/PostgreSQL for monotonic folder-counter generations; V049 remains next +RustBox 0.2.9 release manifest +0.2.9 feature/configuration changes: +- suppresses startup new-mail toasts and arms realtime mail notifications only after authenticated startup is complete and the WebSocket has been open for a short grace period +- suppresses self-generated destination-folder NewMessages notifications during bulk move/copy/archive/junk/delete operations while retaining the bulk summary toast +- shows a success toast after authenticated startup/login +- preserves browser/system Ctrl/Cmd shortcuts by limiting RustBox single-key shortcuts to unmodified keys; compose remains available on C +- defaults mailbox pages/preferences to 300 messages and raises the supported per-page API/preference limit to 300 +- requires no runtime schema migration; V049 remains next Architecture: - database_backend = sqlite | mysql | pgsql selects authoritative SQL only - blob storage and search are independent from the SQL engine @@ -51,14 +42,14 @@ Docker examples: - docker/compose-mysql-nfs-opensearch.yml (MySQL + real NFS volume + OpenSearch) Operations/docs: -- README.md documents 0.2.8 configuration +- README.md documents 0.2.9 configuration - docker/README.md documents Compose variants - docs/STORAGE_SEARCH_OPERATIONS.md documents failure modes, shared-storage requirements and migration behavior - TODO.md leaves live SILO/MinIO/AWS/Ceph and real multi-node acceptance validation explicitly pending Build/release metadata: - `scripts/package-release.sh` runs the full Rust release gate before creating ZIP/SHA256 artifacts -- Cargo.toml version = 0.2.8 +- Cargo.toml version = 0.2.9 - rust-toolchain.toml pins Rust 1.99.1 - Cargo.lock is retained for reproducible application builds; the release gate generates it only when missing - CI and scripts/build-release.sh use the retained Cargo.lock (generating it only if missing), then run locked tests/Clippy/release build diff --git a/RELEASE-VALIDATION.txt b/RELEASE-VALIDATION.txt index c04933b..2e7b3c3 100644 --- a/RELEASE-VALIDATION.txt +++ b/RELEASE-VALIDATION.txt @@ -1,23 +1,21 @@ -RustBox 0.2.8 - final source validation +RustBox 0.2.9 - final source validation -0.2.8 scope: -- authoritative IMAP folder totals/unread counters from server state, including selected-mailbox fallback via UID SEARCH UNSEEN -- optimistic/targeted realtime read/unread/flag updates without full message-list rerenders -- responsive total/unread folder badges, authoritative FolderStatus updates, and separate counter/CONDSTORE cursor handling -- IMAP cache/deep-index progress surfaced through the activity indicator -- content-type-aware opt-in security headers with external-script `script-src 'self'` CSP and stylesheet-only `style-src 'self'` -- private API no-store/nosniff/same-origin-CORP plus anti-framing CSP/X-Frame-Options; full document CSP, Permissions-Policy and COOP remain HTML-only -- pre-login auth policy minimized to rememberMeEnabled; optional feature policy requires authentication -- runtime migration V048 adds monotonic folder-counter generations on SQLite/MySQL/PostgreSQL; V049 remains next -- version metadata updated to 0.2.8 +0.2.9 scope: +- startup synchronization does not emit new-mail toasts; realtime mail notifications are armed after application startup +- bulk move/copy/archive/junk/delete actions suppress self-generated destination-folder new-mail notifications and retain one summary toast +- authenticated startup/login shows a success toast +- browser/system Ctrl/Cmd shortcuts are not intercepted by RustBox single-key mail shortcuts +- mailbox pages default to 300 messages and accept up to 300 per page +- no runtime schema migration is added; V049 remains next +- version metadata updated to 0.2.9 Completed in this environment: -- offline release validator: scripts/validate-release-static.py (279/279 checks passed) +- offline release validator: scripts/validate-release-static.py (340/340 checks passed) - browser JavaScript assembled in manifest order passes `node --check` - all JSON files parse successfully - Python helper scripts compile successfully - shell scripts pass `bash -n` -- release metadata consistently identifies 0.2.8 +- release metadata consistently identifies 0.2.9 - V048 migration files/catalog registration and SQLite upgrade execution are validated offline diff --git a/config.example.toml b/config.example.toml index 1ca30a0..798d1bb 100644 --- a/config.example.toml +++ b/config.example.toml @@ -1,4 +1,4 @@ -# RustBox 0.2.8 +# RustBox 0.2.9 host = "127.0.0.1" port = 3001 environment = "production" diff --git a/docs/IMPLEMENTATION_STATUS.md b/docs/IMPLEMENTATION_STATUS.md index 9db52c5..98f21cd 100644 --- a/docs/IMPLEMENTATION_STATUS.md +++ b/docs/IMPLEMENTATION_STATUS.md @@ -1,6 +1,14 @@ -# RustBox 0.2.8 - implementation status +# RustBox 0.2.9 - implementation status -This file tracks the `0.2.8` source snapshot. Storage/search scope and outstanding acceptance work are tracked in `TODO.md`. A feature is only listed as implemented when working code exists in this source tree; partial items are called out explicitly. +This file tracks the `0.2.9` source snapshot. Storage/search scope and outstanding acceptance work are tracked in `TODO.md`. A feature is only listed as implemented when working code exists in this source tree; partial items are called out explicitly. + +## 0.2.9 browser mail UX + +- Initial synchronization no longer emits new-mail toasts. Realtime notifications are armed after the authenticated application is open. +- Bulk move/copy/archive/junk/delete operations suppress destination-folder `NewMessages` notifications generated by the operation itself and keep the single bulk summary toast. +- Browser/system modifier shortcuts are preserved; RustBox mail shortcuts use unmodified single keys. +- Mailbox pages default to 300 messages and the API/preferences accept values up to 300. +- No runtime schema migration is added; V049 remains next. ## 0.2.8 browser security headers diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 4e12746..b7a3ad3 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -26,7 +26,7 @@ RUSTBOX_CONFIG=./config.dev.toml ./target/release/rustbox --check-config Browser JavaScript source lives in functional modules under `src/web/js/`, ordered by `src/web/js/manifest.txt`. `build.rs` concatenates those modules into the embedded `rustbox-ui.js` asset during every Cargo build. The repository has no duplicate `ui/` snapshot; `src/web/` is the only browser UI source. -## Updating an existing checkout to 0.2.8 +## Updating an existing checkout to 0.2.9 Do not overlay the release with `cp *` or another command that skips dotfiles. That can retain stale `.github`, `.env.example`, or `Cargo.lock` files. diff --git a/docs/STORAGE_SEARCH_OPERATIONS.md b/docs/STORAGE_SEARCH_OPERATIONS.md index fc627e9..77691fa 100644 --- a/docs/STORAGE_SEARCH_OPERATIONS.md +++ b/docs/STORAGE_SEARCH_OPERATIONS.md @@ -1,6 +1,6 @@ -# RustBox 0.2.8 storage/search operations +# RustBox 0.2.9 storage/search operations -RustBox 0.2.8 treats SQL, blob storage and search as independent capabilities. The database engine does not decide whether `data_dir` is allowed. +RustBox 0.2.9 treats SQL, blob storage and search as independent capabilities. The database engine does not decide whether `data_dir` is allowed. ## Deployment matrix diff --git a/migrations/README.md b/migrations/README.md index e38d132..90e4a5b 100644 --- a/migrations/README.md +++ b/migrations/README.md @@ -1,6 +1,6 @@ # Database schemas and migrations -RustBox 0.2.8 keeps SQL, blob storage and search as independent concerns: +RustBox 0.2.9 keeps SQL, blob storage and search as independent concerns: ```text database_backend = sqlite | mysql | pgsql @@ -37,7 +37,7 @@ Account data is isolated inside the selected physical database with a determinis Every schema-changing release must add the same logical migration for all supported SQL engines and register it in `src/db/pool.rs`. Do not update only `schema.sql`: fresh installs use the baseline while existing installations use the numbered migration path. -The next migration number after RustBox 0.2.8 is **V049**. +RustBox 0.2.9 does not add a runtime schema migration; **V049** remains the next migration number. Redis and Redis Sentinel do not use SQL migrations. diff --git a/migrations/mysql/schema.sql b/migrations/mysql/schema.sql index 2ba713f..87b7c11 100644 --- a/migrations/mysql/schema.sql +++ b/migrations/mysql/schema.sql @@ -281,7 +281,7 @@ CREATE TABLE {{p}}known_addresses ( CREATE TABLE {{p}}mail_preferences ( id BIGINT PRIMARY KEY, - messages_per_page BIGINT NOT NULL DEFAULT 50, + messages_per_page BIGINT NOT NULL DEFAULT 300, mark_read_delay_ms BIGINT NOT NULL DEFAULT 0, remote_images TEXT NOT NULL DEFAULT ('ask'), preferred_body TEXT NOT NULL DEFAULT ('auto'), diff --git a/migrations/pgsql/schema.sql b/migrations/pgsql/schema.sql index 4506f92..3f29bc9 100644 --- a/migrations/pgsql/schema.sql +++ b/migrations/pgsql/schema.sql @@ -282,7 +282,7 @@ CREATE TABLE {{p}}known_addresses ( CREATE TABLE {{p}}mail_preferences ( id BIGINT PRIMARY KEY, - messages_per_page BIGINT NOT NULL DEFAULT 50, + messages_per_page BIGINT NOT NULL DEFAULT 300, mark_read_delay_ms BIGINT NOT NULL DEFAULT 0, remote_images TEXT NOT NULL DEFAULT 'ask', preferred_body TEXT NOT NULL DEFAULT 'auto', diff --git a/migrations/sqlite/schema.sql b/migrations/sqlite/schema.sql index 8e85cb1..15be6ae 100644 --- a/migrations/sqlite/schema.sql +++ b/migrations/sqlite/schema.sql @@ -206,7 +206,7 @@ CREATE TABLE {{p}}known_addresses ( CREATE TABLE {{p}}mail_preferences ( id INTEGER PRIMARY KEY CHECK (id = 1), - messages_per_page INTEGER NOT NULL DEFAULT 50, + messages_per_page INTEGER NOT NULL DEFAULT 300, mark_read_delay_ms INTEGER NOT NULL DEFAULT 0, remote_images TEXT NOT NULL DEFAULT 'ask', preferred_body TEXT NOT NULL DEFAULT 'auto', diff --git a/packaging/rustbox.toml b/packaging/rustbox.toml index 86ea358..229a650 100644 --- a/packaging/rustbox.toml +++ b/packaging/rustbox.toml @@ -1,4 +1,4 @@ -# RustBox 0.2.8 - packaged production configuration (DEB/RPM) +# RustBox 0.2.9 - packaged production configuration (DEB/RPM) # # This file contains all supported RustBox configuration options. # Commented options are optional and do not change the packaged defaults. diff --git a/scripts/validate-release-static.py b/scripts/validate-release-static.py index 4aa9e3f..4c03200 100755 --- a/scripts/validate-release-static.py +++ b/scripts/validate-release-static.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Static/offline release checks for RustBox 0.2.8. +"""Static/offline release checks for RustBox 0.2.9. These checks intentionally avoid pretending to replace `cargo test`: they validate configuration, SQL migrations, Docker examples, query-catalog parity and release @@ -49,14 +49,14 @@ def extract_section(text: str, begin: str, end: str) -> str: # Release/toolchain metadata. cargo = tomllib.loads(read("Cargo.toml")) -require(cargo["package"]["version"] == "0.2.8", "Cargo package version is 0.2.8") +require(cargo["package"]["version"] == "0.2.9", "Cargo package version is 0.2.9") require(cargo["package"]["rust-version"] == "1.99.1", "Cargo rust-version is 1.99.1") toolchain = tomllib.loads(read("rust-toolchain.toml")) require(toolchain["toolchain"]["channel"] == "1.99.1", "rust-toolchain pins Rust 1.99.1") require((ROOT / "Cargo.lock").is_file(), "Cargo.lock is retained for reproducible application builds") lock = tomllib.loads(read("Cargo.lock")) lock_rustbox = next((pkg for pkg in lock.get("package", []) if pkg.get("name") == "rustbox"), {}) -require(lock_rustbox.get("version") == "0.2.8", "Cargo.lock RustBox package version is 0.2.8") +require(lock_rustbox.get("version") == "0.2.9", "Cargo.lock RustBox package version is 0.2.9") require("cargo generate-lockfile" in read("scripts/build-release.sh"), "release script generates Cargo.lock when missing") release_script = read("scripts/build-release.sh") release_script_flat = " ".join(release_script.replace("\\", " ").split()) @@ -69,10 +69,21 @@ require("make_zip.py" in package_script and "sha256sum" in package_script, "rele require("cargo generate-lockfile" in read(".github/workflows/ci.yml"), "CI regenerates Cargo.lock before --locked checks") require("validate-release-static.py" in read("scripts/build-release.sh"), "release script runs offline/static release gate") require("validate-release-static.py" in read(".github/workflows/ci.yml"), "CI runs offline/static release gate") -require(read("README.md").startswith("# RustBox 0.2.8"), "README identifies RustBox 0.2.8") -require(read("RELEASE-MANIFEST.txt").startswith("RustBox 0.2.8"), "release manifest identifies RustBox 0.2.8") -require(read("RELEASE-VALIDATION.txt").startswith("RustBox 0.2.8"), "release validation identifies RustBox 0.2.8") -require(read("docs/IMPLEMENTATION_STATUS.md").startswith("# RustBox 0.2.8"), "implementation status identifies RustBox 0.2.8") +require(read("README.md").startswith("# RustBox 0.2.9"), "README identifies RustBox 0.2.9") +require(read("RELEASE-MANIFEST.txt").startswith("RustBox 0.2.9"), "release manifest identifies RustBox 0.2.9") +require(read("RELEASE-VALIDATION.txt").startswith("RustBox 0.2.9"), "release validation identifies RustBox 0.2.9") +require(read("docs/IMPLEMENTATION_STATUS.md").startswith("# RustBox 0.2.9"), "implementation status identifies RustBox 0.2.9") +bootstrap_js = read("src/web/js/00-bootstrap.js") +bindings_js = read("src/web/js/95-bindings-boot.js") +realtime_js = read("src/web/js/80-settings-realtime.js") +mail_actions_js = read("src/web/js/60-mail-actions-search.js") +require("perPage: 300" in bootstrap_js and "messages_per_page: 300" in bootstrap_js, "browser mailbox default is 300 messages") +require(re.search(r"default_per_page\(\)\s*->\s*u32\s*\{\s*300\s*\}", read("src/routes/messages/types.rs")) is not None, "folder message API default page size is 300") +require('1..=300' in read("src/routes/messages/mod.rs"), "folder message API accepts up to 300 messages per page") +require('20..=300' in read("src/db/mail_preferences.rs"), "mailbox preference accepts 300 messages per page") +require('const appShortcut = keyboardIdle && !event.ctrlKey && !event.metaKey && !event.altKey;' in bindings_js, "single-key shortcuts preserve Ctrl/Cmd/Alt browser shortcuts") +require('realtimeNotificationsReady' in realtime_js and 'suppressRealtimeNotificationsForFolder' in realtime_js, "realtime new-mail notification gating is present") +require('suppressRealtimeNotificationsForFolder(notificationDestination)' in mail_actions_js, "bulk destination notifications are suppressed") require("V048" in read("RELEASE-MANIFEST.txt") and "V049" in read("RELEASE-MANIFEST.txt"), "release manifest documents V048 and reserves V049") require("V048" in read("migrations/README.md") and "V049" in read("migrations/README.md"), "migration guide documents V048 and reserves V049") require((ROOT / "docs/STORAGE_SEARCH_OPERATIONS.md").is_file(), "storage/search operations guide exists") diff --git a/src/db/mail_preferences.rs b/src/db/mail_preferences.rs index d48bcc0..876b3b1 100644 --- a/src/db/mail_preferences.rs +++ b/src/db/mail_preferences.rs @@ -57,9 +57,9 @@ pub fn update_preferences( .map_err(|error| format!("Failed to initialize mail preferences: {error}"))?; if let Some(value) = data.messages_per_page - && !(20..=200).contains(&value) + && !(20..=300).contains(&value) { - return Err("messages_per_page must be between 20 and 200".to_string()); + return Err("messages_per_page must be between 20 and 300".to_string()); } if let Some(value) = data.mark_read_delay_ms && value > 30_000 @@ -151,7 +151,7 @@ mod tests { fn defaults_and_update_work() { let conn = open_test_db(); let defaults = get_preferences(&conn).unwrap(); - assert_eq!(defaults.messages_per_page, 50); + assert_eq!(defaults.messages_per_page, 300); assert_eq!(defaults.remote_images, "ask"); let updated = update_preferences( diff --git a/src/db/pool.rs b/src/db/pool.rs index a6f0242..059ccb1 100644 --- a/src/db/pool.rs +++ b/src/db/pool.rs @@ -1923,19 +1923,19 @@ fn tenant_seed_sql(backend: DatabaseBackend, prefix: &str) -> String { DatabaseBackend::Sqlite => format!( "INSERT OR IGNORE INTO {prefix}user_meta (key, value) VALUES ('schema_version', '1');\n\ INSERT OR IGNORE INTO {prefix}notification_preferences (id) VALUES (1);\n\ - INSERT OR IGNORE INTO {prefix}mail_preferences (id) VALUES (1);\n\ + INSERT OR IGNORE INTO {prefix}mail_preferences (id, messages_per_page) VALUES (1, 300);\n\ INSERT OR IGNORE INTO {prefix}pgp_preferences (id) VALUES (1);" ), DatabaseBackend::Mysql => format!( "INSERT IGNORE INTO {prefix}user_meta (`key`, value) VALUES ('schema_version', '1');\n\ INSERT IGNORE INTO {prefix}notification_preferences (id) VALUES (1);\n\ - INSERT IGNORE INTO {prefix}mail_preferences (id) VALUES (1);\n\ + INSERT IGNORE INTO {prefix}mail_preferences (id, messages_per_page) VALUES (1, 300);\n\ INSERT IGNORE INTO {prefix}pgp_preferences (id) VALUES (1);" ), DatabaseBackend::Pgsql => format!( "INSERT INTO {prefix}user_meta (key, value) VALUES ('schema_version', '1') ON CONFLICT (key) DO NOTHING;\n\ INSERT INTO {prefix}notification_preferences (id) VALUES (1) ON CONFLICT (id) DO NOTHING;\n\ - INSERT INTO {prefix}mail_preferences (id) VALUES (1) ON CONFLICT (id) DO NOTHING;\n\ + INSERT INTO {prefix}mail_preferences (id, messages_per_page) VALUES (1, 300) ON CONFLICT (id) DO NOTHING;\n\ INSERT INTO {prefix}pgp_preferences (id) VALUES (1) ON CONFLICT (id) DO NOTHING;" ), } diff --git a/src/db/settings.rs b/src/db/settings.rs index 64525ef..1cb6ad1 100644 --- a/src/db/settings.rs +++ b/src/db/settings.rs @@ -14,7 +14,7 @@ pub fn reset_all(conn: &mut Connection) -> Result<(), String> { "DELETE FROM calendar_settings", "DELETE FROM pgp_preferences", "INSERT INTO display_preferences (id) VALUES (1)", - "INSERT INTO mail_preferences (id) VALUES (1)", + "INSERT INTO mail_preferences (id, messages_per_page) VALUES (1, 300)", "INSERT INTO notification_preferences (id) VALUES (1)", "INSERT INTO calendar_settings (id) VALUES (1)", "INSERT INTO pgp_preferences (id) VALUES (1)", @@ -85,7 +85,7 @@ mod tests { mail_preferences::get_preferences(&conn) .unwrap() .messages_per_page, - 50 + 300 ); assert_eq!( folder_preferences::get_preferences(&conn, "INBOX") diff --git a/src/queries/mysql/queries.rs b/src/queries/mysql/queries.rs index 2373a6e..25d21e1 100644 --- a/src/queries/mysql/queries.rs +++ b/src/queries/mysql/queries.rs @@ -270,7 +270,8 @@ pub mod db_identities { pub mod db_mail_preferences { pub const Q_001: &str = "SELECT messages_per_page, mark_read_delay_ms, remote_images, preferred_body, thread_mode, collapse_quotes, show_snippets, default_sort, flagged_on_top, sync_imap_keywords, updated_at FROM mail_preferences WHERE id = 1"; - pub const Q_002: &str = "INSERT IGNORE INTO mail_preferences (id) VALUES (1)"; + pub const Q_002: &str = + "INSERT IGNORE INTO mail_preferences (id, messages_per_page) VALUES (1, 300)"; pub const Q_003: &str = "UPDATE mail_preferences SET messages_per_page = ?, mark_read_delay_ms = ?, remote_images = ?, preferred_body = ?, thread_mode = ?, collapse_quotes = ?, show_snippets = ?, default_sort = ?, flagged_on_top = ?, sync_imap_keywords = ?, updated_at = CURRENT_TIMESTAMP WHERE id = 1"; } diff --git a/src/queries/pgsql/queries.rs b/src/queries/pgsql/queries.rs index dab21fa..0e9a39d 100644 --- a/src/queries/pgsql/queries.rs +++ b/src/queries/pgsql/queries.rs @@ -270,8 +270,7 @@ pub mod db_identities { pub mod db_mail_preferences { pub const Q_001: &str = "SELECT messages_per_page, mark_read_delay_ms, remote_images, preferred_body, thread_mode, collapse_quotes, show_snippets, default_sort, flagged_on_top, sync_imap_keywords, updated_at FROM mail_preferences WHERE id = 1"; - pub const Q_002: &str = - "INSERT INTO mail_preferences (id) VALUES (1) ON CONFLICT (id) DO NOTHING"; + pub const Q_002: &str = "INSERT INTO mail_preferences (id, messages_per_page) VALUES (1, 300) ON CONFLICT (id) DO NOTHING"; pub const Q_003: &str = "UPDATE mail_preferences SET messages_per_page = $1, mark_read_delay_ms = $2, remote_images = $3, preferred_body = $4, thread_mode = $5, collapse_quotes = $6, show_snippets = $7, default_sort = $8, flagged_on_top = $9, sync_imap_keywords = $10, updated_at = CURRENT_TIMESTAMP::text WHERE id = 1"; } diff --git a/src/queries/sqlite/queries.rs b/src/queries/sqlite/queries.rs index fff6674..71b8572 100644 --- a/src/queries/sqlite/queries.rs +++ b/src/queries/sqlite/queries.rs @@ -269,7 +269,8 @@ pub mod db_identities { pub mod db_mail_preferences { pub const Q_001: &str = "SELECT messages_per_page, mark_read_delay_ms, remote_images, preferred_body, thread_mode, collapse_quotes, show_snippets, default_sort, flagged_on_top, sync_imap_keywords, updated_at FROM mail_preferences WHERE id = 1"; - pub const Q_002: &str = "INSERT OR IGNORE INTO mail_preferences (id) VALUES (1)"; + pub const Q_002: &str = + "INSERT OR IGNORE INTO mail_preferences (id, messages_per_page) VALUES (1, 300)"; pub const Q_003: &str = "UPDATE mail_preferences SET messages_per_page = ?1, mark_read_delay_ms = ?2, remote_images = ?3, preferred_body = ?4, thread_mode = ?5, collapse_quotes = ?6, show_snippets = ?7, default_sort = ?8, flagged_on_top = ?9, sync_imap_keywords = ?10, updated_at = datetime('now') WHERE id = 1"; } diff --git a/src/routes/messages/mod.rs b/src/routes/messages/mod.rs index 9c502b1..ba83da1 100644 --- a/src/routes/messages/mod.rs +++ b/src/routes/messages/mod.rs @@ -63,9 +63,9 @@ pub async fn list_messages( Path(folder): Path, Query(query): Query, ) -> Result { - if !(1..=200).contains(&query.per_page) { + if !(1..=300).contains(&query.per_page) { return Err(AppError::BadRequest( - "per_page must be between 1 and 200".to_string(), + "per_page must be between 1 and 300".to_string(), )); } if !matches!( diff --git a/src/routes/messages/types.rs b/src/routes/messages/types.rs index 558a108..36235f7 100644 --- a/src/routes/messages/types.rs +++ b/src/routes/messages/types.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; pub use crate::email_theme::EmailTheme; pub fn default_per_page() -> u32 { - 50 + 300 } pub fn default_sort() -> String { diff --git a/src/web/i18n/en.json b/src/web/i18n/en.json index 6b8eec7..ca01cce 100644 --- a/src/web/i18n/en.json +++ b/src/web/i18n/en.json @@ -55,5 +55,6 @@ "message_details": "Message details", "move_copy": "Move / copy", "move_to": "Move to…", - "copy_to": "Copy to…" + "copy_to": "Copy to…", + "login_success": "Successfully logged in" } diff --git a/src/web/i18n/pl.json b/src/web/i18n/pl.json index f9476cc..d2d49c5 100644 --- a/src/web/i18n/pl.json +++ b/src/web/i18n/pl.json @@ -640,5 +640,6 @@ "message_details": "Szczegóły", "move_copy": "Przenieś / kopiuj", "move_to": "Przenieś do…", - "copy_to": "Kopiuj do…" + "copy_to": "Kopiuj do…", + "login_success": "Pomyślnie zalogowano" } diff --git a/src/web/index.html b/src/web/index.html index e229697..1849b96 100644 --- a/src/web/index.html +++ b/src/web/index.html @@ -784,6 +784,7 @@ +