feat(archive): add yearly/monthly archive folder options#10113
feat(archive): add yearly/monthly archive folder options#10113fnune wants to merge 20 commits intothunderbird:mainfrom
Conversation
| @@ -343,6 +343,11 @@ | |||
| <string name="sent_folder_label">Sent folder</string> | |||
| <string name="trash_folder_label">Trash folder</string> | |||
| <string name="archive_folder_label">Archive folder</string> | |||
| <string name="archive_granularity_label">Archive options</string> | |||
There was a problem hiding this comment.
I tried to match Thunderbird Desktop here but really there's only one option, so maybe this copy should be thought through once more. This is more stable if the area does get more features, and in that case "Archive options" would be fine, but currently it's a bit meh.
|
Hi @rafaeltonholo! Anything I can do to help get this chugging along? I keep refreshing this PR to see if anything's happened 😄 |
Hi there! I started the review, but I didn't have time to finish it yet, and I forgot to send the comments I have already made. Sorry about that! I'll try to finish the review this week, but here are the initial comments |
ccfd4a6 to
ea8584d
Compare
|
Hi @rafaeltonholo, sorry it took me over a month to come back to this. I've been busy. I've addressed the review feedback:
I also rebased onto main and bumped the migration to A couple of design questions I'd appreciate input on before finalizing:
|
ea8584d to
8b1e479
Compare
|
Regarding the I tried using The rest of the codebase uses @OptIn(ExperimentalTime::class)
private val LocalMessage.messageDate: LocalDate
get() {
val epochMillis = (internalDate ?: sentDate)?.time
val timeZone = TimeZone.currentSystemDefault()
val instant = epochMillis?.let { kotlin.time.Instant.fromEpochMilliseconds(it) }
?: Clock.System.now()
return instant.toLocalDateTime(timeZone).date
}The logic is the same as your suggestion, just using |
8b1e479 to
b2463b7
Compare
|
Any chance we can review this and merge so it doesn't go stale? @jbott-tbird |
I will review this PR again later this week. |
|
We were waiting to request any changes until after the code freeze. It does look like there's a Spotless issue. You can run |
- Add ArchiveGranularity enum with DEFAULT and MIGRATION_DEFAULT - Update LegacyAccountDto with archiveGranularity property - Add database migration v28->v29 for backward compatibility - Add UI preference with Desktop-matching strings - Archive options disabled until archive folder selected
Added ArchiveFolderResolver skeleton with date-based routing stubs. Updated ArchiveOperations to group messages by destination folder. Messages are now batched by their resolved archive targets.
Resolver handles all destination logic internally, so the parameter is no longer needed in archiveMessages and archiveThreads methods.
Resolver now creates date-based subfolders on demand. Messages are routed to yearly (Archive/YYYY) or monthly (Archive/YYYY/MM) subfolders based on their internal/sent date and account granularity setting.
Use BackendStorage.updateFolders() instead of MessageStore.createFolders() to ensure folders are properly created both locally and remotely. This matches the pattern used in CreateArchiveFolder use case and ensures IMAP folder creation is queued for sync.
Add LegacyAccountDtoBackendStorageFactory mock to test to match updated MessagingController constructor signature.
Test coverage includes: - Single folder granularity (no folder creation) - Yearly folder creation and reuse - Monthly folder creation and reuse - Date handling (internal date vs sent date) - Error handling for folder creation failures - Month formatting with leading zeros
Applied Interface Segregation Principle by extracting two thin interfaces from thick dependencies: - FolderIdResolver (2 methods) replaces MessageStoreManager (60+ methods) - ArchiveFolderCreator (1 method) replaces BackendStorageFactory Added adapter implementations for production use and created simple fakes for testing. Tests now use 15-line fakes instead of 200+ line fakes that implemented entire thick interfaces.
Resolved detekt warnings by adding explicit locale to String.format, suppressing intentional generic exception catching, and suppressing early return count warnings for null safety checks.
Add getString/putString handlers in AccountSettingsDataStore to properly save and load archive_granularity preference changes.
The `kotlin.time.Instant` API is no longer experimental, so the `@OptIn(ExperimentalTime::class)` annotation and explicit package prefix are no longer necessary.
…ndStorageFactory`
b2463b7 to
56d57bd
Compare
|
Missing report label. Set exactly one of: |
|
@fnune, @ryanleesipes, I have rebased this branch onto @dani-zilla, please review at your convenience. |
|
I think this looks good. Seems to work as expected. To answer your questions:
I do like the idea of showing a toast message, similar to the one we'd do if a user tries to move a message that is not yet synced with the server. Small an unobtrusive that tells them there was an issue with the archival process if they selected yearly or monthly, just because they may be upset to find it wasn't going into the expected folder once they finally figure it out.
This I think we should probably have set up to be a single archive folder by default for all new users as well. Just because it's unexpected behavior for most users to start creating archive folders, I would think. Perhaps only the default if they set a specific archive folder, as we do on desktop. |
Fixes #900.
Resolves #8390.
Adds yearly and monthly archive folder organization matching Desktop's behavior. Eliminates the need to manually switch archive folders each year. Does this with an "Archive options" preference with three modes: Single folder, Yearly folders (
Archive/2025), or Monthly folders (Archive/2025/03). Matches Desktop's two-setting approach and uses the samearchiveGranularityproperty. Year/month subfolders are created automatically based on message date. Existing accounts default to single folder mode for backward compatibility.Design decisions
Two settings: matches Desktop's "which folder?" + "how to organize?" rather than a flat list of "Archive-Yearly", "CustomFolder-Monthly", etc. Avoids combinatorial explosion and works with any base folder.
Automatic subfolder creation: year/month folders are created automatically based on granularity setting, not manually selected. This avoids yearly updates and matches Desktop behavior.
Defaults: new accounts default to yearly (matching Desktop). Existing accounts migrated to single folder to preserve current behavior.
Graceful degradation: if subfolder creation fails, messages are archived to the base archive folder rather than failing the operation entirely.
Uses account's folder path delimiter rather than assuming
/, since some IMAP servers use.or other delimiters.Potential follow-up
Issue #905 folder structure preservation, automatic yearly (or yearly + monthly) detection, translations...
Questions for reviewers
PER_YEAR_ARCHIVE_FOLDERSthe right default for new accounts (matching Desktop), withSINGLE_ARCHIVE_FOLDERfor migrated accounts to preserve existing behavior?Screenshots