Skip to content

Commit d45b17e

Browse files
committed
refactor(mailstore): extract remote folder queries to dedicated repositories
- Add RemoteFolderQueryRepository interface with getAllByAccountId method - Add RemoteFolderDetailsRepository interface with getAllByAccountId method - Add DefaultRemoteFolderQueryRepository and DefaultRemoteFolderDetailsRepository implementations - Remove getRemoteFolders and getRemoteFolderDetails from FolderRepository - Update GetAccountFolders, AccountSettingsViewModel, DefaultDisplayFolderRepository, DefaultSpecialFolderUpdater, and FolderSettingsProvider to use new repositories - Make SettingsExporter.exportPreferences and related methods suspend functions - Make FolderSettingsChangedListener.onFolderSettingsChanged suspend function and notify on main immediate dispatcher - Migrate RemoteFolderDetails from data class to typealias referencing API module
1 parent 5b1741f commit d45b17e

36 files changed

Lines changed: 903 additions & 341 deletions

File tree

app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/FeatureMailModule.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ internal val appCommonFeatureMailModule = module {
1717
factory<SpecialFolderUpdater.Factory> {
1818
DefaultSpecialFolderUpdater.Factory(
1919
accountManager = get(),
20-
folderRepository = get(),
20+
remoteFolderQueryRepository = get(),
2121
folderDetailsRepository = get(),
2222
specialFolderSelectionStrategy = get(),
2323
coroutineScope = get(named("AppCoroutineScope")),
24+
logger = get(),
2425
)
2526
}
2627

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,31 @@
11
package net.thunderbird.feature.mail.folder.api
22

33
data class FolderDetails(
4-
val folder: Folder,
5-
val isInTopGroup: Boolean,
6-
val isIntegrate: Boolean,
7-
val isSyncEnabled: Boolean,
8-
val isVisible: Boolean,
9-
val isNotificationsEnabled: Boolean,
10-
val isPushEnabled: Boolean,
11-
)
4+
override val folder: Folder,
5+
override val isInTopGroup: Boolean,
6+
override val isIntegrate: Boolean,
7+
override val isSyncEnabled: Boolean,
8+
override val isVisible: Boolean,
9+
override val isNotificationsEnabled: Boolean,
10+
override val isPushEnabled: Boolean,
11+
) : BaseFolderDetails<Folder>
12+
13+
data class RemoteFolderDetails(
14+
override val folder: RemoteFolder,
15+
override val isInTopGroup: Boolean,
16+
override val isIntegrate: Boolean,
17+
override val isSyncEnabled: Boolean,
18+
override val isVisible: Boolean,
19+
override val isNotificationsEnabled: Boolean,
20+
override val isPushEnabled: Boolean,
21+
) : BaseFolderDetails<RemoteFolder>
22+
23+
sealed interface BaseFolderDetails<TFolder> {
24+
val folder: TFolder
25+
val isInTopGroup: Boolean
26+
val isIntegrate: Boolean
27+
val isSyncEnabled: Boolean
28+
val isVisible: Boolean
29+
val isNotificationsEnabled: Boolean
30+
val isPushEnabled: Boolean
31+
}

feature/mail/folder/api/src/commonMain/kotlin/net/thunderbird/feature/mail/folder/api/data/FolderError.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ sealed interface FolderError {
55
data object NotFound : FolderError
66
data object Unavailable : FolderError
77
data class FailedPrecondition(val message: String, val throwable: Throwable? = null) : FolderError
8+
data class FailedToQueryDatabase(val message: String, val throwable: Throwable) : FolderError
89
}

feature/mail/folder/api/src/commonMain/kotlin/net/thunderbird/feature/mail/folder/api/data/repository/PushFoldersQueryRepository.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ interface PushFoldersQueryRepository {
1919
*
2020
* @param accountId The account identifier.
2121
*/
22-
fun getAllByAccountId(accountId: AccountId): Outcome<List<RemoteFolder>, FolderError>
22+
suspend fun getAllByAccountId(accountId: AccountId): Outcome<List<RemoteFolder>, FolderError>
2323
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package net.thunderbird.feature.mail.folder.api.data.repository
2+
3+
import net.thunderbird.core.outcome.Outcome
4+
import net.thunderbird.feature.account.AccountId
5+
import net.thunderbird.feature.mail.folder.api.FolderDetails
6+
import net.thunderbird.feature.mail.folder.api.RemoteFolder
7+
import net.thunderbird.feature.mail.folder.api.RemoteFolderDetails
8+
import net.thunderbird.feature.mail.folder.api.data.FolderError
9+
10+
interface RemoteFolderDetailsRepository {
11+
/**
12+
* Returns a list of [FolderDetails] of a [RemoteFolder] for the given [accountId].
13+
*
14+
* @param accountId The account identifier.
15+
*/
16+
suspend fun getAllByAccountId(accountId: AccountId): Outcome<List<RemoteFolderDetails>, FolderError>
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package net.thunderbird.feature.mail.folder.api.data.repository
2+
3+
import net.thunderbird.core.common.exception.MessagingException
4+
import net.thunderbird.core.outcome.Outcome
5+
import net.thunderbird.feature.account.AccountId
6+
import net.thunderbird.feature.mail.folder.api.RemoteFolder
7+
import net.thunderbird.feature.mail.folder.api.data.FolderError
8+
9+
interface RemoteFolderQueryRepository {
10+
/**
11+
* Returns a list of [RemoteFolder]s for the given [accountId].
12+
*
13+
* @param accountId The account identifier.
14+
* @throws MessagingException if there's a problem accessing the folders.
15+
*/
16+
suspend fun getAllByAccountId(accountId: AccountId): Outcome<List<RemoteFolder>, FolderError>
17+
}

feature/mail/message/list/api/src/main/kotlin/net/thunderbird/feature/mail/message/list/domain/DomainContract.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ interface DomainContract {
5555
}
5656
}
5757

58-
data class AccountFolderError(val exception: Exception)
58+
data class AccountFolderError(val exception: Throwable)
5959

6060
sealed interface SetAccountFolderOutcome {
6161
data object Success : SetAccountFolderOutcome

feature/mail/message/list/internal/src/main/kotlin/net/thunderbird/feature/mail/message/list/internal/FeatureMessageListModule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import org.koin.dsl.module
2424

2525
val featureMessageListModule = module {
2626
includes(messageListSideEffectsModule)
27-
factory<DomainContract.UseCase.GetAccountFolders> { GetAccountFolders(folderRepository = get()) }
27+
factory<DomainContract.UseCase.GetAccountFolders> { GetAccountFolders(remoteFolderQueryRepository = get()) }
2828
factory<DomainContract.UseCase.CreateArchiveFolder> {
2929
CreateArchiveFolder(
3030
accountManager = get(),
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,39 @@
11
package net.thunderbird.feature.mail.message.list.internal.domain.usecase
22

3-
import app.k9mail.legacy.mailstore.FolderRepository
43
import kotlinx.coroutines.CoroutineDispatcher
54
import kotlinx.coroutines.Dispatchers
65
import kotlinx.coroutines.withContext
76
import net.thunderbird.core.common.exception.MessagingException
87
import net.thunderbird.core.outcome.Outcome
8+
import net.thunderbird.core.outcome.map
99
import net.thunderbird.feature.account.AccountId
1010
import net.thunderbird.feature.mail.folder.api.FolderType
1111
import net.thunderbird.feature.mail.folder.api.RemoteFolder
12+
import net.thunderbird.feature.mail.folder.api.data.FolderError
13+
import net.thunderbird.feature.mail.folder.api.data.repository.RemoteFolderQueryRepository
1214
import net.thunderbird.feature.mail.message.list.domain.AccountFolderError
1315
import net.thunderbird.feature.mail.message.list.domain.DomainContract
1416

1517
internal class GetAccountFolders(
16-
private val folderRepository: FolderRepository,
18+
private val remoteFolderQueryRepository: RemoteFolderQueryRepository,
1719
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
1820
) : DomainContract.UseCase.GetAccountFolders {
1921
override suspend fun invoke(accountId: AccountId): Outcome<List<RemoteFolder>, AccountFolderError> =
2022
withContext(ioDispatcher) {
21-
try {
22-
Outcome.success(
23-
data = folderRepository
24-
.getRemoteFolders(accountId)
25-
.filter { it.type == FolderType.REGULAR || it.type == FolderType.ARCHIVE },
23+
remoteFolderQueryRepository
24+
.getAllByAccountId(accountId)
25+
.map(
26+
transformSuccess = { remoteFolders ->
27+
remoteFolders.filter { it.type == FolderType.REGULAR || it.type == FolderType.ARCHIVE }
28+
},
29+
transformFailure = { error, _ ->
30+
AccountFolderError(
31+
exception = when (error) {
32+
is FolderError.FailedToQueryDatabase -> error.throwable
33+
else -> MessagingException("Failed to get account folders. Folder error = $error")
34+
},
35+
)
36+
},
2637
)
27-
} catch (e: MessagingException) {
28-
Outcome.failure(error = AccountFolderError(exception = e))
29-
}
3038
}
3139
}

feature/mail/message/list/internal/src/main/kotlin/net/thunderbird/feature/mail/message/list/internal/ui/state/sideeffect/LoadFolderInformationSideEffect.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import app.k9mail.legacy.mailstore.FolderRepository
55
import kotlinx.coroutines.CoroutineScope
66
import kotlinx.coroutines.flow.first
77
import net.thunderbird.core.logging.Logger
8+
import net.thunderbird.core.outcome.fold
89
import net.thunderbird.feature.account.AccountId
910
import net.thunderbird.feature.account.UnifiedAccountId
1011
import net.thunderbird.feature.account.profile.AccountProfileRepository
1112
import net.thunderbird.feature.mail.folder.api.FolderType
13+
import net.thunderbird.feature.mail.folder.api.data.repository.RemoteFolderQueryRepository
1214
import net.thunderbird.feature.mail.message.list.ui.effect.MessageListEffect
1315
import net.thunderbird.feature.mail.message.list.ui.event.FolderEvent
1416
import net.thunderbird.feature.mail.message.list.ui.event.MessageListEvent
@@ -26,6 +28,7 @@ internal class LoadFolderInformationSideEffect(
2628
dispatch: suspend (MessageListEvent) -> Unit,
2729
private val logger: Logger,
2830
private val folderRepository: FolderRepository,
31+
private val remoteFolderQueryRepository: RemoteFolderQueryRepository,
2932
private val profileRepository: AccountProfileRepository,
3033
) : MessageListStateSideEffectHandler(logger, dispatch) {
3134
override fun accept(event: MessageListEvent, oldState: MessageListState, newState: MessageListState): Boolean =
@@ -64,7 +67,10 @@ internal class LoadFolderInformationSideEffect(
6467
val folder = folderRepository.getFolder(accountId, folderId)
6568
return if (folder != null) {
6669
val remoteFolder = if (!folder.isLocalOnly) {
67-
folderRepository.getRemoteFolders(accountId).first { it.id == folderId }
70+
remoteFolderQueryRepository
71+
.getAllByAccountId(accountId)
72+
.fold(onSuccess = { it }, onFailure = { emptyList() })
73+
.firstOrNull { it.id == folderId }
6874
} else {
6975
null
7076
}
@@ -92,6 +98,7 @@ internal class LoadFolderInformationSideEffect(
9298
private val folderId: Long?,
9399
private val logger: Logger,
94100
private val folderRepository: FolderRepository,
101+
private val remoteFolderQueryRepository: RemoteFolderQueryRepository,
95102
private val profileRepository: AccountProfileRepository,
96103
) : MessageListStateSideEffectHandlerFactory {
97104
override fun create(
@@ -104,6 +111,7 @@ internal class LoadFolderInformationSideEffect(
104111
dispatch = dispatch,
105112
logger = logger,
106113
folderRepository = folderRepository,
114+
remoteFolderQueryRepository = remoteFolderQueryRepository,
107115
profileRepository = profileRepository,
108116
)
109117
}

0 commit comments

Comments
 (0)