- sqlite3 is now imported in lib/ (production code), so it must be a
regular dependency, not a dev_dependency
- Replace deprecated conn.dispose() with conn.close() in the test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes#451
## What changed
Replaces the default Flutter blue logo with the project's rainbow-rings `icon.svg` on all supported platforms.
**Android** — all five mipmap densities regenerated (`mdpi` 48px through `xxxhdpi` 192px).
**Linux** — `linux/sharedinbox.png` (512×512) added, installed next to the binary via `CMakeLists.txt`, and set as the GTK window icon via `gtk_window_set_icon_from_file` in `my_application.cc`.
**Tooling** — `icon.png` (1024×1024 source raster) committed; `flutter_launcher_icons` added as dev dep with a `flutter_icons` config block; `task generate-icons` added to `Taskfile.yml` for future regeneration; `librsvg` added to `flake.nix` so `rsvg-convert` is available inside `nix develop`.
## How verified
Icons were generated with Inkscape from `icon.svg` and visually confirmed (rainbow-rings design appears correctly at all sizes). The `playstore/icon.png` was already correct and unchanged.
Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/459
When zone errors bubble up through Dart's async machinery the stack
trace is in package:stack_trace chain format (with '===== asynchronous
gap =====' separators). Flutter's StackFrame parser asserts on those
lines. FlutterError.demangleStackTrace strips the chain format back to
a plain VM trace before Flutter tries to parse it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
flutter_markdown 0.7.7+1 has been discontinued in favour of
flutter_markdown_plus. Switch the dependency and update both import
sites.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs caused the crash-at-startup report:
1. CrashScreen used the widget's build context (above its own MaterialApp)
for ScaffoldMessenger.of() in button callbacks. When the screen is the
root widget — the runApp() path after a startup crash — there is no
ScaffoldMessenger above it, so both 'Copy to Clipboard' and 'Report Issue
on Codeberg' crashed with a null check error. Fix: wrap Scaffold.body in
Builder to obtain a context that is a descendant of the Scaffold.
2. path_provider_android 2.2.21 updated to Pigeon 26, which causes a
channel-error on startup for some Android devices. Pin to <2.2.21
(resolves to 2.2.20, which uses the stable pre-Pigeon-26 implementation).
Additionally, make initDatabasePath() catch PlatformException so a
channel error at the very start of main() no longer hard-crashes the app;
_openConnection()'s lazy fallback retries after runApp() completes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pubspec.lock was incorrectly gitignored — this is a Flutter app, not a
package, so the lockfile should be committed for reproducible builds.
Without it, CI resolved drift to its minimum (2.20.3) which constrains
sqlite3 to 2.x, causing dart analyze to disagree on whether
Database.close() exists vs the local environment using 3.3.1.
Also pins sqlite3: ^3.1.5 explicitly in pubspec.yaml as belt-and-
suspenders so the constraint is visible without reading the lockfile.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Save the .eml file to the temporary directory (reliable on all
platforms) and display a Share action in the SnackBar so users can
send the file to any app — including the Files app — which properly
registers it with Android's MediaStore and makes it visible in the
recently-used list.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After a successful download, Navigator.pop is called so the dialog
dismisses without requiring a manual close. Adds a widget test that
verifies this using a fake PathProviderPlatform and IOOverrides so the
entire async chain runs as pure microtasks inside the Flutter test zone.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the insecure plaintext QR export/import flow with an
end-to-end-encrypted account-transfer mechanism:
- Receiver generates an ephemeral X25519 key pair (20-minute lifetime,
stored in the new share_keys DB table at schema v31) and displays it
as a QR code (sharedinbox.de:pubkey:v1:…).
- Sender scans the public-key QR, selects accounts (or auto-selects
when only one exists), encrypts them with ECIES (X25519-ECDH +
HKDF-SHA256 + AES-256-GCM) and displays an encrypted QR
(sharedinbox.de:encrypted-accounts:v1:…).
- Receiver scans the encrypted QR, decrypts, verifies the 20-minute
expiry and MAC authentication tag, then imports the accounts.
New screens: AccountReceiveScreen (/accounts/receive) and
AccountSendScreen (/accounts/send), accessible from the account-list
drawer and per-account popup menu respectively.
Remove the old insecure AccountExportScreen and AccountImportScreen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Export account screen (QR code + copy-to-clipboard) and Import
account screen (paste JSON code) so users can transfer IMAP/JMAP
account configuration to another device without re-entering every field.
- Account list popup: "Export account" opens a QR code with a password
warning and a copy-code button.
- Add Account screen: "Import account" button opens the import flow
where pasting the exported JSON pre-fills the account and one tap
saves it with a fresh generated ID.
- New routes: /accounts/:id/export and /accounts/import.
- Widget tests cover export display, import parsing, validation,
and the happy-path save-and-navigate flow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Swap the flutter_html renderer for a webview_flutter-based widget that
enforces strict security by default: scripts blocked via CSP
(script-src 'none'), remote images opt-in, and every link click routed
through a confirmation dialog that bolds the registered domain for
phishing detection. Links open in the system browser via url_launcher.
On Linux (no webview_flutter platform support) the widget falls back to
plain text extracted via the existing htmlToPlain() utility.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Issue reports now include:
- App version (from package_info_plus)
- OS name and version (non-personal, from dart:io Platform)
- Error and stack trace wrapped in triple-backtick code blocks
so Codeberg renders them as preformatted text
Closes#59
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
## Overview
This PR implements several fixes and enhancements requested in the latest session:
### Fixes
1. **Issue 1: Raw Email Headers**
- Added database support for raw headers.
- Added a new Headers tab in the email detail screen with a zebra-colored table display.
2. **Issue 2: Exception on Undo of Delete**
- Added `toJson` and `fromJson` to `EmailAddress` model to fix serialization during undo.
3. **Issue 3: Crash Reporting**
- Added a button to the Crash Screen to report issues directly on Codeberg.
### Infrastructure
- Added Nix experimental features check to `Taskfile.yml` to ensure a consistent dev environment.
## Verification
- Manually verified the Headers display on Linux.
- Verified Undo for IMAP and JMAP accounts.
- Verified the Crash Screen button.
Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/6
- Added UndoService with 10-action history stack.
- Integrated Undo Snackbar into EmailListScreen and EmailDetailScreen.
- Added EmailRepository.cancelPendingChange to optimize undo by removing
unsynced local mutations.
- Fixed sorting bug in compareMailboxes for unknown roles.
- Increased unit coverage to 83% with new model and utility tests.
- Verified with full test suite (task check).
Email detail screen now renders the message htmlBody with the flutter_html
widget when present, falling back to SelectableText for plain-text-only mail.
Remote http(s) images are blocked by default to defeat tracking pixels; an
opt-in "Load remote images" button reveals them per-screen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Android UI integration test failed at tap(aliceTile) with "0 widgets"
even though pumpUntil had just found the tile. On the slow software-rendered
emulator the route-pop animation finalises during pumpUntil's trailing 300 ms
settle, briefly leaving the tile out of the tree. Re-confirm with a second
pumpUntil before the tap.
Bundles the previously uncommitted infra changes that make task deploy-android
run end-to-end inside nix develop: Linux desktop runtime libs + GL software
rendering env in flake.nix, path_provider_android pin to <2.3 to avoid the
libdartjni SIGSEGV, deferred DB-path resolution after WidgetsFlutterBinding,
+iglx for xvfb-run, platform-tools on PATH, and a single pre-commit script
replacing the dart-format / task-check-fast pair.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Use xdg-open directly on Linux for opening attachments (fixes 'file type not supported' error)
- Add mime package for comprehensive MIME type detection in compose screen
- Show file size and MIME type for attachments in compose screen
- Add open/preview button for attachments in compose screen
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add file_picker and open_file dependencies
- EmailDraft gains attachmentFilePaths; EmailAttachment gains fetchPartId
- sendEmail attaches files via MessageBuilder.addFile()
- downloadAttachment fetches the specific MIME part from IMAP, caches to
local filesystem; subsequent calls return the cached file without a
network round-trip
- ComposeScreen: attach-file button + removable attachment list
- EmailDetailScreen: per-attachment download/open button with spinner
- 3 new unit tests covering send-with-attachment, download, and cache hit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add account wizard: email-first flow with JMAP/IMAP auto-detection
via well-known URLs; falls back to manual type selection
- Fix JMAP connection probe: GET session URL with Basic auth instead
of the API endpoint, so 401 reliably signals bad credentials
- Account list tile: tap → open INBOX directly; popup menu for
all mailboxes / edit / delete (with confirmation dialog)
- Show account type (JMAP/IMAP) and async connection status per tile:
spinner while checking, green check on success, red error on failure
- Add EditAccountScreen: edit name, password, server settings; runs
connection test only when password is changed
- Fix GTK window initialisation order so app starts with correct size
- Fix 42 lint issues (avoid_redundant_argument_values,
unnecessary_non_null_assertion, unawaited_futures)
- 147 tests, 87% coverage, task check green
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- flake.nix: Flutter 3.41.6, Android SDK, Stalwart, GTK3/build
tools for Linux desktop, go-task
- .envrc: copied from sharedinbox — use flake + dotenv_if_exists
- Taskfile.yml: analyze, test, integration, codegen, run tasks
- stalwart-dev/: IMAP+SMTP dev server reused from sharedinbox
- test/integration/imap_sync_test.dart: login, list mailboxes,
send via SMTP and receive via IMAP
- pubspec.yaml: add flutter_secure_storage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
IMAP/SMTP email client with offline-first architecture:
sync engine writes to Drift (SQLite), UI reads reactively
from the local DB. enough_mail vendored under packages/.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>