IMAP UIDs are mailbox-scoped, so MOVE assigns a fresh UID in the destination folder. The flush previously discarded the response from `client.uidMove(...)`, so the local row kept the *source* UID while its `mailbox_path` already pointed at the destination. Two things broke: - Deletion reconciliation, which runs per mailbox and compares local UIDs to the server's `ALL` search result, would not find the source UID in the destination mailbox and wipe the row — taking the cached body and queued undo with it. - `UndoLog` rows kept referencing the old `accountId:mailbox:uid` id, so undo had to fall back to a Message-ID lookup just to rediscover the moved message. The fix captures the RFC 4315 `COPYUID` response code that modern `UIDPLUS` servers attach to `MOVE`/`COPY` (already exposed as `GenericImapResult.responseCodeCopyUid` in `enough_mail`). When that's missing — i.e. the server doesn't support UIDPLUS — we fall back to `UID SEARCH HEADER Message-ID …` in the destination mailbox. Either way the local id is rewritten in place to `accountId:destMailbox:newUid` and the cascading `email_bodies`, `threads`, `pending_changes`, and `undo_actions` references are updated in the same transaction. `_reconcileDeletedImap` now also skips rows whose `move`/`snooze`/`unsnooze` is still queued in `pending_changes`, so the optimistic local move can't be wiped between the optimistic write and the server flush. Closes #539 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
112 lines
2.8 KiB
YAML
112 lines
2.8 KiB
YAML
name: sharedinbox
|
|
description: IMAP email client for Android, iOS, and Desktop.
|
|
publish_to: none
|
|
version: 0.1.1
|
|
|
|
environment:
|
|
sdk: '>=3.3.0 <4.0.0'
|
|
flutter: '>=3.22.0'
|
|
|
|
dependencies:
|
|
flutter:
|
|
sdk: flutter
|
|
|
|
# Not used directly, but required to silence Flutter's "missing CupertinoIcons font" build warning.
|
|
cupertino_icons: ^1.0.8
|
|
|
|
enough_mail: ^2.1.7
|
|
http: ^1.2.0
|
|
|
|
# Local persistence (offline-first)
|
|
drift: ^2.20.3
|
|
sqlite3: ^3.1.5 # used directly in lib/data/db/database.dart (_setupPragmas)
|
|
sqlite3_flutter_libs: ^0.6.0+eol
|
|
path_provider: ^2.1.5
|
|
path: ^1.9.1
|
|
|
|
# State management
|
|
flutter_riverpod: ^3.0.0
|
|
|
|
# Navigation
|
|
go_router: ^17.3.0
|
|
|
|
# Secure credential storage (passwords)
|
|
flutter_secure_storage: ^10.0.0
|
|
|
|
# Date formatting
|
|
intl: ^0.20.2
|
|
|
|
# File picking (compose attachments) and opening downloaded attachments
|
|
file_picker: ^12.0.0-beta.5
|
|
open_filex: ^4.6.0
|
|
mime: ^2.0.0
|
|
|
|
# QR code generation for account sharing
|
|
qr_flutter: ^4.1.0
|
|
|
|
# Public-key encryption for secure account sharing (ECIES: X25519 + AES-256-GCM)
|
|
cryptography: ^2.7.0
|
|
|
|
# QR code scanning (camera) for secure account import
|
|
mobile_scanner: ^7.2.0
|
|
|
|
# HTML rendering for email bodies
|
|
webview_flutter: ^4.0.0
|
|
url_launcher: ^6.3.2
|
|
flutter_markdown_plus: ^1.0.7
|
|
|
|
# Background sync and local notifications
|
|
flutter_local_notifications: ^22.0.0
|
|
workmanager: ^0.9.0
|
|
|
|
# Stack trace chain-to-VM conversion for FlutterError.demangleStackTrace
|
|
stack_trace: ^1.12.1
|
|
|
|
# App version metadata for crash reports
|
|
package_info_plus: ^10.1.0
|
|
share_plus: ^13.1.0
|
|
device_info_plus: ^13.1.0
|
|
|
|
# @visibleForTesting annotation used in lib/data/repositories.
|
|
meta: ^1.16.0
|
|
|
|
dev_dependencies:
|
|
flutter_test:
|
|
sdk: flutter
|
|
integration_test:
|
|
sdk: flutter
|
|
flutter_lints: ^6.0.0
|
|
drift_dev: ^2.20.3
|
|
build_runner: ^2.4.13
|
|
test: ^1.25.0
|
|
mockito: ^5.4.4
|
|
fake_async: ^1.3.1
|
|
path_provider_platform_interface: ^2.1.2
|
|
url_launcher_platform_interface: ^2.3.2
|
|
plugin_platform_interface: ^2.1.8
|
|
flutter_launcher_icons: ^0.14.0
|
|
|
|
flutter_icons:
|
|
android: "ic_launcher"
|
|
ios: false
|
|
image_path: "icon.png"
|
|
linux:
|
|
generate: true
|
|
image_path: "icon.png"
|
|
|
|
flutter:
|
|
uses-material-design: true
|
|
assets:
|
|
- assets/
|
|
|
|
dependency_overrides:
|
|
# path_provider_android 2.2.21 updated to Pigeon 26, which causes a
|
|
# channel-error on startup on some Android devices. 2.3+ uses package:jni
|
|
# (SIGSEGV in libdartjni.so FindClassUnchecked). Pin to 2.2.20 which uses
|
|
# stable Pigeon and is known to work reliably.
|
|
path_provider_android: ">=2.2.0 <2.2.21"
|
|
# url_launcher_android 6.3.25 updated to Pigeon 26, which causes a
|
|
# channel-error on launchUrl on some Android devices (same root cause as
|
|
# path_provider_android). Pin to <6.3.25 which uses stable Pigeon.
|
|
url_launcher_android: ">=6.3.0 <6.3.25"
|