Skip to content

Commit b2463b7

Browse files
committed
refactor: rebase and attend to review changes
1 parent a8dccce commit b2463b7

5 files changed

Lines changed: 92 additions & 42 deletions

File tree

feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/LegacyAccountStorageHandler.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import net.thunderbird.core.preference.storage.getEnumOrDefault
1717
import net.thunderbird.feature.account.AccountId
1818
import net.thunderbird.feature.account.storage.legacy.serializer.ServerSettingsDtoSerializer
1919
import net.thunderbird.feature.mail.folder.api.FOLDER_DEFAULT_PATH_DELIMITER
20+
import net.thunderbird.feature.mail.folder.api.ArchiveGranularity
2021
import net.thunderbird.feature.mail.folder.api.SpecialFolderSelection
2122
import net.thunderbird.feature.notification.NotificationLight
2223
import net.thunderbird.feature.notification.NotificationSettings
@@ -115,6 +116,12 @@ class LegacyAccountStorageHandler(
115116
)
116117
setArchiveFolderId(archiveFolderId, archiveFolderSelection)
117118

119+
archiveGranularity = getEnumStringPref(
120+
storage,
121+
keyGen.create(ARCHIVE_GRANULARITY_KEY),
122+
ArchiveGranularity.DEFAULT,
123+
)
124+
118125
val spamFolderId = storage.getStringOrNull(keyGen.create("spamFolderId"))?.toLongOrNull()
119126
val spamFolderSelection = getEnumStringPref<SpecialFolderSelection>(
120127
storage,
@@ -354,6 +361,7 @@ class LegacyAccountStorageHandler(
354361
editor.putString(keyGen.create("archiveFolderId"), archiveFolderId?.toString())
355362
editor.putString(keyGen.create("spamFolderId"), spamFolderId?.toString())
356363
editor.putString(keyGen.create("archiveFolderSelection"), archiveFolderSelection.name)
364+
editor.putString(keyGen.create(ARCHIVE_GRANULARITY_KEY), archiveGranularity.name)
357365
editor.putString(keyGen.create("draftsFolderSelection"), draftsFolderSelection.name)
358366
editor.putString(keyGen.create("sentFolderSelection"), sentFolderSelection.name)
359367
editor.putString(keyGen.create("spamFolderSelection"), spamFolderSelection.name)
@@ -470,6 +478,7 @@ class LegacyAccountStorageHandler(
470478
editor.remove(keyGen.create("archiveFolderName"))
471479
editor.remove(keyGen.create("spamFolderName"))
472480
editor.remove(keyGen.create("archiveFolderSelection"))
481+
editor.remove(keyGen.create(ARCHIVE_GRANULARITY_KEY))
473482
editor.remove(keyGen.create("draftsFolderSelection"))
474483
editor.remove(keyGen.create("sentFolderSelection"))
475484
editor.remove(keyGen.create("spamFolderSelection"))
@@ -607,5 +616,6 @@ class LegacyAccountStorageHandler(
607616
const val IDENTITY_DESCRIPTION_KEY = "description"
608617

609618
const val FOLDER_PATH_DELIMITER_KEY = "folderPathDelimiter"
619+
const val ARCHIVE_GRANULARITY_KEY = "archiveGranularity"
610620
}
611621
}

legacy/core/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ dependencies {
4343
implementation(libs.jsoup)
4444
implementation(libs.moshi)
4545
implementation(libs.timber)
46+
implementation(libs.kotlinx.datetime)
4647
implementation(libs.mime4j.core)
4748
implementation(libs.mime4j.dom)
4849
implementation(libs.uri)

legacy/core/src/main/java/com/fsck/k9/controller/ArchiveFolderResolver.kt

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,21 @@ package com.fsck.k9.controller
22

33
import com.fsck.k9.backend.api.FolderInfo
44
import com.fsck.k9.mailstore.LocalMessage
5-
import java.util.Calendar
6-
import java.util.Date
75
import java.util.Locale
6+
import kotlin.time.Clock
7+
import kotlin.time.ExperimentalTime
8+
import kotlinx.datetime.LocalDate
9+
import kotlinx.datetime.TimeZone
10+
import kotlinx.datetime.toLocalDateTime
811
import net.thunderbird.core.android.account.LegacyAccountDto
912
import net.thunderbird.feature.mail.folder.api.ArchiveGranularity
10-
import net.thunderbird.feature.mail.folder.api.FOLDER_DEFAULT_PATH_DELIMITER
1113
import com.fsck.k9.mail.FolderType as LegacyFolderType
1214

1315
internal class ArchiveFolderResolver(
1416
private val folderIdResolver: FolderIdResolver,
1517
private val folderCreator: ArchiveFolderCreator,
1618
) {
1719

18-
@Suppress("ReturnCount")
1920
fun resolveArchiveFolder(
2021
account: LegacyAccountDto,
2122
message: LocalMessage,
@@ -28,57 +29,52 @@ internal class ArchiveFolderResolver(
2829
}
2930

3031
ArchiveGranularity.PER_YEAR_ARCHIVE_FOLDERS -> {
31-
val year = getYear(getMessageDate(message))
32-
findOrCreateSubfolder(account, baseFolderId, year.toString())
32+
val year = message.messageDate.year
33+
findOrCreateSubfolder(account, baseFolderId, year.toString()) ?: baseFolderId
3334
}
3435

3536
ArchiveGranularity.PER_MONTH_ARCHIVE_FOLDERS -> {
36-
val date = getMessageDate(message)
37-
val year = getYear(date)
38-
val month = String.format(Locale.ROOT, "%02d", getMonth(date))
37+
val date = message.messageDate
38+
val year = date.year
39+
val month = String.format(Locale.ROOT, "%02d", date.monthNumber)
3940

40-
val yearFolderId = findOrCreateSubfolder(account, baseFolderId, year.toString())
41-
?: return baseFolderId
42-
43-
findOrCreateSubfolder(account, yearFolderId, month)
41+
findOrCreateSubfolder(account, baseFolderId, year.toString())?.let { yearFolderId ->
42+
findOrCreateSubfolder(account, yearFolderId, month)
43+
} ?: baseFolderId
4444
}
4545
}
4646
}
4747

48-
@Suppress("ReturnCount")
4948
private fun findOrCreateSubfolder(
5049
account: LegacyAccountDto,
5150
parentFolderId: Long,
5251
subfolderName: String,
5352
): Long? {
5453
val parentServerId = folderIdResolver.getFolderServerId(account, parentFolderId) ?: return null
5554

56-
val delimiter = FOLDER_DEFAULT_PATH_DELIMITER
55+
val delimiter = account.folderPathDelimiter
5756
val subfolderServerId = "$parentServerId$delimiter$subfolderName"
5857

59-
folderIdResolver.getFolderId(account, subfolderServerId)?.let { return it }
60-
61-
val folderInfo = FolderInfo(
62-
serverId = subfolderServerId,
63-
name = subfolderServerId,
64-
type = LegacyFolderType.ARCHIVE,
65-
)
66-
return folderCreator.createFolder(account, folderInfo)
67-
}
68-
69-
private fun getMessageDate(message: LocalMessage): Date {
70-
return message.internalDate ?: message.sentDate ?: Date()
71-
}
72-
73-
private fun getYear(date: Date): Int {
74-
val calendar = Calendar.getInstance()
75-
calendar.time = date
76-
return calendar.get(Calendar.YEAR)
58+
val existingId = folderIdResolver.getFolderId(account, subfolderServerId)
59+
return if (existingId != null) {
60+
existingId
61+
} else {
62+
val folderInfo = FolderInfo(
63+
serverId = subfolderServerId,
64+
name = subfolderServerId,
65+
type = LegacyFolderType.ARCHIVE,
66+
)
67+
folderCreator.createFolder(account, folderInfo)
68+
}
7769
}
7870

79-
private fun getMonth(date: Date): Int {
80-
val calendar = Calendar.getInstance()
81-
calendar.time = date
82-
return calendar.get(Calendar.MONTH) + 1
83-
}
71+
@OptIn(ExperimentalTime::class)
72+
private val LocalMessage.messageDate: LocalDate
73+
get() {
74+
val epochMillis = (internalDate ?: sentDate)?.time
75+
val timeZone = TimeZone.currentSystemDefault()
76+
val instant = epochMillis?.let { kotlin.time.Instant.fromEpochMilliseconds(it) }
77+
?: Clock.System.now()
78+
return instant.toLocalDateTime(timeZone).date
79+
}
8480
}

legacy/core/src/test/java/com/fsck/k9/controller/ArchiveFolderResolverTest.kt

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class ArchiveFolderResolverTest {
5555
val result = testSubject.resolveArchiveFolder(account, message)
5656

5757
assertThat(result).isEqualTo(YEARLY_FOLDER_ID)
58+
assertThat(archiveFolderCreator.createdFolders[0].name).isEqualTo("Archive/2025")
5859
}
5960

6061
@Test
@@ -191,7 +192,7 @@ class ArchiveFolderResolverTest {
191192
}
192193

193194
@Test
194-
fun `return null when yearly subfolder creation fails`() {
195+
fun `fall back to base folder when yearly subfolder creation fails`() {
195196
account.archiveGranularity = ArchiveGranularity.PER_YEAR_ARCHIVE_FOLDERS
196197
val message = createMessage(year = 2025, month = 11)
197198

@@ -206,11 +207,11 @@ class ArchiveFolderResolverTest {
206207

207208
val result = testSubject.resolveArchiveFolder(account, message)
208209

209-
assertThat(result).isNull()
210+
assertThat(result).isEqualTo(BASE_ARCHIVE_FOLDER_ID)
210211
}
211212

212213
@Test
213-
fun `return null when yearly folder creation succeeds but monthly creation fails`() {
214+
fun `fall back to base folder when yearly succeeds but monthly creation fails`() {
214215
account.archiveGranularity = ArchiveGranularity.PER_MONTH_ARCHIVE_FOLDERS
215216
val message = createMessage(year = 2025, month = 11)
216217

@@ -232,7 +233,47 @@ class ArchiveFolderResolverTest {
232233

233234
val result = testSubject.resolveArchiveFolder(account, message)
234235

235-
assertThat(result).isNull()
236+
assertThat(result).isEqualTo(BASE_ARCHIVE_FOLDER_ID)
237+
}
238+
239+
@Test
240+
fun `use current date when both internal and sent dates are null`() {
241+
account.archiveGranularity = ArchiveGranularity.PER_YEAR_ARCHIVE_FOLDERS
242+
val message = mock<LocalMessage>().apply {
243+
whenever(internalDate).thenReturn(null)
244+
whenever(sentDate).thenReturn(null)
245+
}
246+
247+
val currentYear = Calendar.getInstance().get(Calendar.YEAR).toString()
248+
val folderIdResolver = FakeFolderIdResolver(
249+
folderServerIds = mapOf(BASE_ARCHIVE_FOLDER_ID to "Archive"),
250+
folderIds = mapOf("Archive/$currentYear" to YEARLY_FOLDER_ID),
251+
)
252+
val testSubject = createResolver(folderIdResolver)
253+
254+
val result = testSubject.resolveArchiveFolder(account, message)
255+
256+
assertThat(result).isEqualTo(YEARLY_FOLDER_ID)
257+
}
258+
259+
@Test
260+
fun `use account folder delimiter for subfolder paths`() {
261+
val accountWithDotDelimiter = LegacyAccountDto(UUID.randomUUID().toString()).apply {
262+
archiveFolderId = BASE_ARCHIVE_FOLDER_ID
263+
archiveGranularity = ArchiveGranularity.PER_YEAR_ARCHIVE_FOLDERS
264+
folderPathDelimiter = "."
265+
}
266+
val message = createMessage(year = 2025, month = 11)
267+
268+
val folderIdResolver = FakeFolderIdResolver(
269+
folderServerIds = mapOf(BASE_ARCHIVE_FOLDER_ID to "Archive"),
270+
folderIds = mapOf("Archive.2025" to YEARLY_FOLDER_ID),
271+
)
272+
val testSubject = createResolver(folderIdResolver)
273+
274+
val result = testSubject.resolveArchiveFolder(accountWithDotDelimiter, message)
275+
276+
assertThat(result).isEqualTo(YEARLY_FOLDER_ID)
236277
}
237278

238279
private fun createResolver(

legacy/core/src/test/java/com/fsck/k9/controller/FakeArchiveFolderCreator.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ internal class FakeArchiveFolderCreator(
88
private val failAfterCalls: Int = Int.MAX_VALUE,
99
) : ArchiveFolderCreator {
1010
private var callCount = 0
11+
val createdFolders = mutableListOf<FolderInfo>()
1112

1213
override fun createFolder(account: LegacyAccountDto, folderInfo: FolderInfo): Long? {
1314
callCount++
15+
createdFolders.add(folderInfo)
1416
if (callCount > failAfterCalls) {
1517
return null
1618
}

0 commit comments

Comments
 (0)