diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index a7eb7629bc..1dbc8ebfa0 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -310,7 +310,7 @@ class MainViewModel @Inject constructor( fun reassignCurrentThreadsLive() { currentThreadsLiveJob?.cancel() - currentThreadsLiveJob = viewModelScope.launch(ioCoroutineContext) { + currentThreadsLiveJob = viewModelScope.launch(ioDispatcher) { observeFolderAndFilter() .flatMapLatest { (folder, filter) -> folder?.let { threadController.getThreadsAsync(it, filter) } ?: emptyFlow() @@ -353,7 +353,7 @@ class MainViewModel @Inject constructor( val shareThreadUrlResult = _shareThreadUrlResult.shareIn(viewModelScope, SharingStarted.Lazily) //endregion - fun updateUserInfo() = viewModelScope.launch(ioCoroutineContext) { + fun updateUserInfo() = viewModelScope.launch(ioDispatcher) { SentryLog.d(TAG, "Update user info") updateAddressBooks() updateContacts() @@ -387,13 +387,13 @@ class MainViewModel @Inject constructor( return mailbox } - private fun switchToValidMailbox() = viewModelScope.launch(ioCoroutineContext) { + private fun switchToValidMailbox() = viewModelScope.launch(ioDispatcher) { mailboxController.getFirstValidMailbox(AccountUtils.currentUserId)?.let { AccountUtils.switchToMailbox(it.mailboxId) } ?: appContext.launchNoValidMailboxesActivity() } - fun dismissCurrentMailboxNotifications() = viewModelScope.launch(ioCoroutineContext) { + fun dismissCurrentMailboxNotifications() = viewModelScope.launch(ioDispatcher) { currentMailbox.value?.let { appContext.cancelNotification(it.notificationGroupId) } @@ -402,7 +402,7 @@ class MainViewModel @Inject constructor( fun refreshEverything() { refreshEverythingJob?.cancel() - refreshEverythingJob = viewModelScope.launch(ioCoroutineContext) { + refreshEverythingJob = viewModelScope.launch(ioDispatcher) { // Refresh User AccountUtils.updateCurrentUser() @@ -442,7 +442,7 @@ class MainViewModel @Inject constructor( ?.let { folderController.getFolder(it) } ?: folderController.getFolder(DEFAULT_SELECTED_FOLDER))?.let { folder -> selectFolder(folder.id) - viewModelScope.launch(ioCoroutineContext) { + viewModelScope.launch(ioDispatcher) { refreshThreads(mailbox, folder.id) } } @@ -466,7 +466,7 @@ class MainViewModel @Inject constructor( } } - private fun updateQuotas(mailbox: Mailbox) = viewModelScope.launch(ioCoroutineContext) { + private fun updateQuotas(mailbox: Mailbox) = viewModelScope.launch(ioDispatcher) { SentryLog.d(TAG, "Force refresh Quotas") if (mailbox.isLimited) { @@ -485,7 +485,7 @@ class MainViewModel @Inject constructor( } } - private fun updatePermissions(mailbox: Mailbox) = viewModelScope.launch(ioCoroutineContext) { + private fun updatePermissions(mailbox: Mailbox) = viewModelScope.launch(ioDispatcher) { SentryLog.d(TAG, "Force refresh Permissions") with(ApiRepository.getPermissions(mailbox.linkId, mailbox.hostingId)) { if (isSuccess()) { @@ -496,18 +496,18 @@ class MainViewModel @Inject constructor( } } - private fun updateSignatures(mailbox: Mailbox) = viewModelScope.launch(ioCoroutineContext) { + private fun updateSignatures(mailbox: Mailbox) = viewModelScope.launch(ioDispatcher) { SentryLog.d(TAG, "Force refresh Signatures") updateSignatures(mailbox, mailboxInfoRealm) } //region Spam - fun moveToSpamFolder(threadUid: String, messageUid: String) = viewModelScope.launch(ioCoroutineContext) { + fun moveToSpamFolder(threadUid: String, messageUid: String) = viewModelScope.launch(ioDispatcher) { val message = messageController.getMessage(messageUid) ?: return@launch toggleMessageSpamStatus(threadUid, message) } - fun activateSpamFilter() = viewModelScope.launch(ioCoroutineContext) { + fun activateSpamFilter() = viewModelScope.launch(ioDispatcher) { val mailbox = currentMailbox.value ?: return@launch ApiRepository.setSpamFilter( @@ -517,7 +517,7 @@ class MainViewModel @Inject constructor( ) } - fun unblockMail(email: String) = viewModelScope.launch(ioCoroutineContext) { + fun unblockMail(email: String) = viewModelScope.launch(ioDispatcher) { val mailbox = currentMailbox.value ?: return@launch with(ApiRepository.getSendersRestrictions(mailbox.hostingId, mailbox.mailboxName)) { @@ -540,7 +540,7 @@ class MainViewModel @Inject constructor( } } - private fun updateSendersRestrictions(mailbox: Mailbox) = viewModelScope.launch(ioCoroutineContext) { + private fun updateSendersRestrictions(mailbox: Mailbox) = viewModelScope.launch(ioDispatcher) { SentryLog.d(TAG, "Force refresh Senders Restrictions") with(ApiRepository.getSendersRestrictions(mailbox.hostingId, mailbox.mailboxName)) { @@ -553,12 +553,12 @@ class MainViewModel @Inject constructor( } //endregion - private fun updateFeatureFlag(mailbox: Mailbox) = viewModelScope.launch(ioCoroutineContext) { + private fun updateFeatureFlag(mailbox: Mailbox) = viewModelScope.launch(ioDispatcher) { SentryLog.d(TAG, "Force refresh Features flags") sharedUtils.updateFeatureFlags(mailbox.objectId, mailbox.uuid) } - private fun updateExternalMailInfo(mailbox: Mailbox) = viewModelScope.launch(ioCoroutineContext) { + private fun updateExternalMailInfo(mailbox: Mailbox) = viewModelScope.launch(ioDispatcher) { SentryLog.d(TAG, "Force refresh External Mail info") with(ApiRepository.getExternalMailInfo(mailbox.hostingId, mailbox.mailboxName)) { if (!isSuccess()) return@launch @@ -600,7 +600,7 @@ class MainViewModel @Inject constructor( } } - fun openFolder(folderId: String) = viewModelScope.launch(ioCoroutineContext) { + fun openFolder(folderId: String) = viewModelScope.launch(ioDispatcher) { if (folderId == currentFolderId) return@launch if (currentFilter.value != ThreadFilter.ALL) currentFilter.postValue(ThreadFilter.ALL) @@ -609,7 +609,7 @@ class MainViewModel @Inject constructor( refreshThreads(folderId = folderId) } - fun flushFolder() = viewModelScope.launch(ioCoroutineContext) { + fun flushFolder() = viewModelScope.launch(ioDispatcher) { val mailboxUuid = currentMailbox.value?.uuid ?: return@launch val folderId = currentFolderId ?: return@launch @@ -623,12 +623,12 @@ class MainViewModel @Inject constructor( } } - fun forceRefreshThreads(showSwipeRefreshLayout: Boolean = true) = viewModelScope.launch(ioCoroutineContext) { + fun forceRefreshThreads(showSwipeRefreshLayout: Boolean = true) = viewModelScope.launch(ioDispatcher) { SentryLog.d(TAG, "Force refresh threads") refreshThreads(showSwipeRefreshLayout = showSwipeRefreshLayout) } - fun getOnePageOfOldMessages() = viewModelScope.launch(ioCoroutineContext) { + fun getOnePageOfOldMessages() = viewModelScope.launch(ioDispatcher) { if (isDownloadingChanges.value == true) return@launch @@ -688,7 +688,7 @@ class MainViewModel @Inject constructor( private fun deleteThreadsOrMessage( threadsUids: List, message: Message? = null, - ) = viewModelScope.launch(ioCoroutineContext) { + ) = viewModelScope.launch(ioDispatcher) { val threads = threadController.getThreads(threadsUids).ifEmpty { return@launch } val shouldPermanentlyDelete = isPermanentDeleteFolder(folderRoleUtils.getActionFolderRole(threads, message)) @@ -784,7 +784,7 @@ class MainViewModel @Inject constructor( else -> messageController.getMessageAndDuplicates(threads.first(), message) } - fun deleteDraft(targetMailboxUuid: String, remoteDraftUuid: String) = viewModelScope.launch(ioCoroutineContext) { + fun deleteDraft(targetMailboxUuid: String, remoteDraftUuid: String) = viewModelScope.launch(ioDispatcher) { val mailbox = currentMailbox.value!! val apiResponse = ApiRepository.deleteDraft(targetMailboxUuid, remoteDraftUuid) @@ -803,7 +803,7 @@ class MainViewModel @Inject constructor( //endregion //region Scheduled Drafts - fun rescheduleDraft(scheduleDate: Date) = viewModelScope.launch(ioCoroutineContext) { + fun rescheduleDraft(scheduleDate: Date) = viewModelScope.launch(ioDispatcher) { draftResource?.takeIf { it.isNotBlank() }?.let { resource -> with(ApiRepository.rescheduleDraft(resource, scheduleDate)) { if (isSuccess()) { @@ -820,7 +820,7 @@ class MainViewModel @Inject constructor( fun modifyScheduledDraft( unscheduleDraftUrl: String, onSuccess: () -> Unit, - ) = viewModelScope.launch(ioCoroutineContext) { + ) = viewModelScope.launch(ioDispatcher) { val mailbox = currentMailbox.value!! val apiResponse = ApiRepository.unscheduleDraft(unscheduleDraftUrl) @@ -833,7 +833,7 @@ class MainViewModel @Inject constructor( } } - fun unscheduleDraft(unscheduleDraftUrl: String) = viewModelScope.launch(ioCoroutineContext) { + fun unscheduleDraft(unscheduleDraftUrl: String) = viewModelScope.launch(ioDispatcher) { val mailbox = currentMailbox.value!! val apiResponse = ApiRepository.unscheduleDraft(unscheduleDraftUrl) @@ -866,7 +866,7 @@ class MainViewModel @Inject constructor( destinationFolderId: String, threadsUids: List, messageUid: String? = null, - ) = viewModelScope.launch(ioCoroutineContext) { + ) = viewModelScope.launch(ioDispatcher) { val destinationFolder = folderController.getFolder(destinationFolderId)!! val threads = threadController.getThreads(threadsUids).ifEmpty { return@launch } val message = messageUid?.let { messageController.getMessage(it)!! } @@ -1003,7 +1003,7 @@ class MainViewModel @Inject constructor( private fun archiveThreadsOrMessage( threadsUids: List, message: Message? = null, - ) = viewModelScope.launch(ioCoroutineContext) { + ) = viewModelScope.launch(ioDispatcher) { val threads = threadController.getThreads(threadsUids).ifEmpty { return@launch } @@ -1036,7 +1036,7 @@ class MainViewModel @Inject constructor( threadsUids: List, message: Message? = null, shouldRead: Boolean = true, - ) = viewModelScope.launch(ioCoroutineContext) { + ) = viewModelScope.launch(ioDispatcher) { val mailbox = currentMailbox.value!! val threads = threadController.getThreads(threadsUids).ifEmpty { return@launch } @@ -1116,7 +1116,7 @@ class MainViewModel @Inject constructor( threadsUids: List, message: Message? = null, shouldFavorite: Boolean = true, - ) = viewModelScope.launch(ioCoroutineContext) { + ) = viewModelScope.launch(ioDispatcher) { val mailbox = currentMailbox.value!! val threads = threadController.getThreads(threadsUids).ifEmpty { return@launch } @@ -1185,7 +1185,7 @@ class MainViewModel @Inject constructor( threadsUids: List, message: Message? = null, displaySnackbar: Boolean = true, - ) = viewModelScope.launch(ioCoroutineContext) { + ) = viewModelScope.launch(ioDispatcher) { val threads = threadController.getThreads(threadsUids).ifEmpty { return@launch } @@ -1208,7 +1208,7 @@ class MainViewModel @Inject constructor( //endregion //region Phishing - fun reportPhishing(threadUids: List, messages: List) = viewModelScope.launch(ioCoroutineContext) { + fun reportPhishing(threadUids: List, messages: List) = viewModelScope.launch(ioDispatcher) { val mailboxUuid = currentMailbox.value?.uuid!! val messagesUids: List = messages.map { it.uid } @@ -1243,7 +1243,7 @@ class MainViewModel @Inject constructor( //endregion //region Display problem - fun reportDisplayProblem(messageUid: String) = viewModelScope.launch(ioCoroutineContext) { + fun reportDisplayProblem(messageUid: String) = viewModelScope.launch(ioDispatcher) { val message = messageController.getMessage(messageUid) ?: return@launch val mailbox = currentMailbox.value ?: return@launch @@ -1269,7 +1269,7 @@ class MainViewModel @Inject constructor( //endregion //region BlockUser - fun blockUser(folderId: String, shortUid: Int) = viewModelScope.launch(ioCoroutineContext) { + fun blockUser(folderId: String, shortUid: Int) = viewModelScope.launch(ioDispatcher) { val mailboxUuid = currentMailbox.value?.uuid!! with(ApiRepository.blockUser(mailboxUuid, folderId, shortUid)) { @@ -1319,7 +1319,7 @@ class MainViewModel @Inject constructor( suspend fun rescheduleSnoozedThreads(date: Date, threadUids: List): BatchSnoozeResult { var rescheduleResult: BatchSnoozeResult = BatchSnoozeResult.Error.Unknown - viewModelScope.launch(ioCoroutineContext) { + viewModelScope.launch(ioDispatcher) { val snoozedThreadUuids = threadUids.mapNotNull { threadUid -> val thread = threadController.getThread(threadUid) ?: return@mapNotNull null thread.snoozeUuid.takeIf { thread.isSnoozed() } @@ -1372,7 +1372,7 @@ class MainViewModel @Inject constructor( suspend fun unsnoozeThreads(threads: Collection): BatchSnoozeResult { var unsnoozeResult: BatchSnoozeResult = BatchSnoozeResult.Error.Unknown - viewModelScope.launch(ioCoroutineContext) { + viewModelScope.launch(ioDispatcher) { val currentMailbox = currentMailbox.value unsnoozeResult = if (currentMailbox == null) { BatchSnoozeResult.Error.Unknown @@ -1483,7 +1483,7 @@ class MainViewModel @Inject constructor( //endregion //region Undo action - fun undoAction(undoData: UndoData) = viewModelScope.launch(ioCoroutineContext) { + fun undoAction(undoData: UndoData) = viewModelScope.launch(ioDispatcher) { fun List>.getFailedCall() = firstOrNull { it.data != true } @@ -1522,9 +1522,9 @@ class MainViewModel @Inject constructor( return apiResponseIsSuccess(apiResponse, mailbox) } - fun createNewFolder(name: String) = viewModelScope.launch(ioCoroutineContext) { createNewFolderSync(name) } + fun createNewFolder(name: String) = viewModelScope.launch(ioDispatcher) { createNewFolderSync(name) } - fun modifyNameFolder(name: String, folderId: String) = viewModelScope.launch(ioCoroutineContext) { + fun modifyNameFolder(name: String, folderId: String) = viewModelScope.launch(ioDispatcher) { val mailbox = currentMailbox.value ?: return@launch val apiResponse = ApiRepository.renameFolder(mailbox.uuid, folderId, name) @@ -1533,7 +1533,7 @@ class MainViewModel @Inject constructor( apiResponseIsSuccess(apiResponse, mailbox) } - fun deleteFolder(folderId: String) = viewModelScope.launch(ioCoroutineContext) { + fun deleteFolder(folderId: String) = viewModelScope.launch(ioDispatcher) { val mailbox = currentMailbox.value ?: return@launch val apiResponse = ApiRepository.deleteFolder(mailbox.uuid, folderId) @@ -1560,7 +1560,7 @@ class MainViewModel @Inject constructor( name: String, threadsUids: List, messageUid: String?, - ) = viewModelScope.launch(ioCoroutineContext) { + ) = viewModelScope.launch(ioDispatcher) { val newFolderId = createNewFolderSync(name) ?: return@launch moveThreadsOrMessageTo(newFolderId, threadsUids, messageUid) isMovedToNewFolder.postValue(true) @@ -1591,7 +1591,7 @@ class MainViewModel @Inject constructor( messagesFoldersIds: ImpactedFolders, destinationFolderId: String? = null, callbacks: RefreshCallbacks? = null, - ) = viewModelScope.launch(ioCoroutineContext) { + ) = viewModelScope.launch(ioDispatcher) { sharedUtils.refreshFolders(mailbox, messagesFoldersIds, destinationFolderId, currentFolderId, callbacks) } @@ -1599,12 +1599,12 @@ class MainViewModel @Inject constructor( isDownloadingChanges.postValue(true) } - private fun onDownloadStop(threadsUids: List = emptyList()) = viewModelScope.launch(ioCoroutineContext) { + private fun onDownloadStop(threadsUids: List = emptyList()) = viewModelScope.launch(ioDispatcher) { threadController.updateIsLocallyMovedOutStatus(threadsUids, hasBeenMovedOut = false) isDownloadingChanges.postValue(false) } - fun addContact(recipient: Recipient) = viewModelScope.launch(ioCoroutineContext) { + fun addContact(recipient: Recipient) = viewModelScope.launch(ioDispatcher) { with(ApiRepository.addContact(addressBookController.getDefaultAddressBook().id, recipient)) { @@ -1637,7 +1637,7 @@ class MainViewModel @Inject constructor( selectedThreadsLiveData.value = selectedThreads } - fun refreshDraftFolderWhenDraftArrives(scheduledMessageEtop: Long) = viewModelScope.launch(ioCoroutineContext) { + fun refreshDraftFolderWhenDraftArrives(scheduledMessageEtop: Long) = viewModelScope.launch(ioDispatcher) { val folder = folderController.getFolder(FolderRole.DRAFT) if (folder?.cursor != null) { @@ -1655,7 +1655,7 @@ class MainViewModel @Inject constructor( } } - fun handleDeletedMessages(messagesUids: Set) = viewModelScope.launch(ioCoroutineContext) { + fun handleDeletedMessages(messagesUids: Set) = viewModelScope.launch(ioDispatcher) { snackbarManager.postValue(appContext.getString(R.string.snackbarDeletedConversation)) @@ -1677,7 +1677,7 @@ class MainViewModel @Inject constructor( } @OptIn(ManualAuthorizationRequired::class) - fun scheduleDownload(downloadUrl: String, filename: String) = viewModelScope.launch(ioCoroutineContext) { + fun scheduleDownload(downloadUrl: String, filename: String) = viewModelScope.launch(ioDispatcher) { val snackbarTitleRes = if (ApiRepository.ping().isSuccess()) { val userBearerToken = AccountUtils.currentUser?.apiToken?.accessToken DownloadManagerUtils.scheduleDownload( @@ -1698,7 +1698,7 @@ class MainViewModel @Inject constructor( snackbarManager.postValue(appContext.getString(snackbarTitleRes)) } - fun deleteThreadInRealm(threadUid: String) = viewModelScope.launch(ioCoroutineContext) { + fun deleteThreadInRealm(threadUid: String) = viewModelScope.launch(ioDispatcher) { threadController.deleteThread(threadUid) } @@ -1720,7 +1720,7 @@ class MainViewModel @Inject constructor( return@launch } - withContext(ioCoroutineContext) { + withContext(ioDispatcher) { messageController.getMessage(messageUid)?.let { message -> val response = ApiRepository.getShareLink(mailboxUuid, message.folderId, message.shortUid) _shareThreadUrlResult.emit(response.data?.url) @@ -1730,7 +1730,7 @@ class MainViewModel @Inject constructor( } // TODO: Remove this function when the Threads parental issues are fixed - private fun removeThreadsWithParentalIssues() = viewModelScope.launch(ioCoroutineContext) { + private fun removeThreadsWithParentalIssues() = viewModelScope.launch(ioDispatcher) { SentryLog.d(TAG, "Remove Threads with parental issues") threadController.removeThreadsWithParentalIssues() } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListViewModel.kt index 095ac9350c..d2bfcf0be1 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListViewModel.kt @@ -1,6 +1,6 @@ /* * Infomaniak Mail - Android - * Copyright (C) 2022-2024 Infomaniak Network SA + * Copyright (C) 2022-2026 Infomaniak Network SA * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,7 +27,6 @@ import com.infomaniak.mail.data.cache.mailboxContent.MessageController import com.infomaniak.mail.di.IoDispatcher import com.infomaniak.mail.utils.SearchUtils import com.infomaniak.mail.utils.WebViewVersionUtils.getWebViewVersionData -import com.infomaniak.mail.utils.coroutineContext import com.infomaniak.mail.utils.extensions.appContext import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher @@ -46,7 +45,6 @@ class ThreadListViewModel @Inject constructor( @IoDispatcher private val ioDispatcher: CoroutineDispatcher, ) : AndroidViewModel(application) { - private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) private var updatedAtJob: Job? = null val isRecoveringFinished = MutableLiveData(true) @@ -60,7 +58,7 @@ class ThreadListViewModel @Inject constructor( fun startUpdatedAtJob() { updatedAtJob?.cancel() - updatedAtJob = viewModelScope.launch(ioCoroutineContext) { + updatedAtJob = viewModelScope.launch(ioDispatcher) { while (true) { delay(DateUtils.MINUTE_IN_MILLIS) ensureActive() @@ -69,7 +67,7 @@ class ThreadListViewModel @Inject constructor( } } - fun deleteSearchData() = viewModelScope.launch(ioCoroutineContext) { + fun deleteSearchData() = viewModelScope.launch(ioDispatcher) { // Delete Search data in case they couldn't be deleted at the end of the previous Search. searchUtils.deleteRealmSearchData() } @@ -89,7 +87,7 @@ class ThreadListViewModel @Inject constructor( isWebViewOutdated.value = canShowWebViewOutdated && hasOutdatedMajorVersion } - suspend fun getEmojiReactionsFor(messageUid: String): Map? = withContext(ioCoroutineContext) { + suspend fun getEmojiReactionsFor(messageUid: String): Map? = withContext(ioDispatcher) { messageController.getMessage(messageUid)?.let { message -> message.emojiReactions.associateBy { it.emoji } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneViewModel.kt index d2c726017a..60c37740aa 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneViewModel.kt @@ -1,6 +1,6 @@ /* * Infomaniak Mail - Android - * Copyright (C) 2023-2025 Infomaniak Network SA + * Copyright (C) 2023-2026 Infomaniak Network SA * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,7 +34,6 @@ import com.infomaniak.mail.di.IoDispatcher import com.infomaniak.mail.ui.main.thread.ThreadViewModel.SnoozeScheduleType import com.infomaniak.mail.ui.newMessage.NewMessageActivityArgs import com.infomaniak.mail.utils.Utils.runCatchingRealm -import com.infomaniak.mail.utils.coroutineContext import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.launch @@ -47,8 +46,6 @@ class TwoPaneViewModel @Inject constructor( @IoDispatcher private val ioDispatcher: CoroutineDispatcher, ) : ViewModel() { - private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) - val currentThreadUid: LiveData = state.getLiveData(CURRENT_THREAD_UID_KEY) inline val isThreadOpen get() = currentThreadUid.value != null @@ -70,7 +67,7 @@ class TwoPaneViewModel @Inject constructor( state[CURRENT_THREAD_UID_KEY] = null } - fun openDraft(thread: Thread) = viewModelScope.launch(ioCoroutineContext) { + fun openDraft(thread: Thread) = viewModelScope.launch(ioDispatcher) { navigateToSelectedDraft(thread.messages.single()) } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folderPicker/FolderPickerViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folderPicker/FolderPickerViewModel.kt index 6013b44b98..730c2f6d36 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folderPicker/FolderPickerViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folderPicker/FolderPickerViewModel.kt @@ -1,6 +1,6 @@ /* * Infomaniak Mail - Android - * Copyright (C) 2023-2025 Infomaniak Network SA + * Copyright (C) 2023-2026 Infomaniak Network SA * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,7 +25,6 @@ import androidx.lifecycle.viewModelScope import com.infomaniak.mail.data.models.Folder.FolderRole import com.infomaniak.mail.di.IoDispatcher import com.infomaniak.mail.ui.MainViewModel -import com.infomaniak.mail.utils.coroutineContext import com.infomaniak.mail.utils.extensions.appContext import com.infomaniak.mail.utils.extensions.standardize import com.infomaniak.mail.utils.flattenAndAddDividerBeforeFirstCustomFolder @@ -44,8 +43,6 @@ class FolderPickerViewModel @Inject constructor( @IoDispatcher private val ioDispatcher: CoroutineDispatcher, ) : AndroidViewModel(application) { - private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) - private var searchJob: Job? = null private val sourceFolderId inline get() = savedStateHandle.get(FolderPickerFragmentArgs::sourceFolderId.name) @@ -56,7 +53,7 @@ class FolderPickerViewModel @Inject constructor( var hasAlreadyTrackedSearch = false init { - viewModelScope.launch(ioCoroutineContext) { + viewModelScope.launch(ioDispatcher) { sourceFolderIdLiveData.postValue(sourceFolderId) } } @@ -93,7 +90,7 @@ class FolderPickerViewModel @Inject constructor( } } - private fun searchFolders(query: CharSequence, shouldDebounce: Boolean) = viewModelScope.launch(ioCoroutineContext) { + private fun searchFolders(query: CharSequence, shouldDebounce: Boolean) = viewModelScope.launch(ioDispatcher) { cancelSearch() diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuDrawerViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuDrawerViewModel.kt index 351cc7b556..df23122f9c 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuDrawerViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuDrawerViewModel.kt @@ -1,6 +1,6 @@ /* * Infomaniak Mail - Android - * Copyright (C) 2023-2025 Infomaniak Network SA + * Copyright (C) 2023-2026 Infomaniak Network SA * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,7 +22,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.infomaniak.mail.data.cache.mailboxContent.FolderController import com.infomaniak.mail.di.IoDispatcher -import com.infomaniak.mail.utils.coroutineContext import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.launch @@ -34,13 +33,11 @@ class MenuDrawerViewModel @Inject constructor( @IoDispatcher private val ioDispatcher: CoroutineDispatcher, ) : ViewModel() { - private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) - val areMailboxesExpanded = MutableLiveData(false) val areCustomFoldersExpanded = MutableLiveData(true) val areActionsExpanded = MutableLiveData(false) - fun toggleFolderCollapsingState(rootFolderId: String, shouldCollapse: Boolean) = viewModelScope.launch(ioCoroutineContext) { + fun toggleFolderCollapsingState(rootFolderId: String, shouldCollapse: Boolean) = viewModelScope.launch(ioDispatcher) { folderController.updateFolder(rootFolderId) { it.isCollapsed = shouldCollapse } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/search/SearchViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/search/SearchViewModel.kt index 7b9c557e53..0190abed86 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/search/SearchViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/search/SearchViewModel.kt @@ -1,6 +1,6 @@ /* * Infomaniak Mail - Android - * Copyright (C) 2023-2025 Infomaniak Network SA + * Copyright (C) 2023-2026 Infomaniak Network SA * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -110,7 +110,7 @@ class SearchViewModel @Inject constructor( searchJob?.cancel() } - fun executePendingSearch() = viewModelScope.launch(ioCoroutineContext) { + fun executePendingSearch() = viewModelScope.launch(ioDispatcher) { val hasPendingSearch = (lastExecutedSearchQuery != currentSearchQuery) || (lastExecutedFolder != filterFolder) || (lastExecutedFilters != currentFilters) @@ -124,11 +124,11 @@ class SearchViewModel @Inject constructor( unselectAllChipFilters() } - fun refreshSearch() = viewModelScope.launch(ioCoroutineContext) { + fun refreshSearch() = viewModelScope.launch(ioDispatcher) { search() } - fun searchQuery(query: String, saveInHistory: Boolean = false) = viewModelScope.launch(ioCoroutineContext) { + fun searchQuery(query: String, saveInHistory: Boolean = false) = viewModelScope.launch(ioDispatcher) { if (query.isNotBlank() && isLengthTooShort(query)) return@launch search(query.trim().also { currentSearchQuery = it }, saveInHistory) } @@ -139,12 +139,12 @@ class SearchViewModel @Inject constructor( fun selectFolder(folder: Folder?) { filterFolder = folder - viewModelScope.launch(ioCoroutineContext) { + viewModelScope.launch(ioDispatcher) { search(folder = folder) } } - fun setFilter(filter: ThreadFilter, isEnabled: Boolean = true) = viewModelScope.launch(ioCoroutineContext) { + fun setFilter(filter: ThreadFilter, isEnabled: Boolean = true) = viewModelScope.launch(ioDispatcher) { if (isEnabled && currentFilters.contains(filter)) return@launch if (isEnabled) { trackSearchEvent(filter.matomoName) @@ -154,7 +154,7 @@ class SearchViewModel @Inject constructor( } } - fun unselectMutuallyExclusiveFilters() = viewModelScope.launch(ioCoroutineContext) { + fun unselectMutuallyExclusiveFilters() = viewModelScope.launch(ioDispatcher) { currentFilters.removeAll(setOf(ThreadFilter.SEEN, ThreadFilter.UNSEEN, ThreadFilter.STARRED)) search(filters = currentFilters) } @@ -163,7 +163,7 @@ class SearchViewModel @Inject constructor( currentFilters.removeAll(ThreadFilter.entries) } - fun nextPage() = viewModelScope.launch(ioCoroutineContext) { + fun nextPage() = viewModelScope.launch(ioDispatcher) { if (isLastPage) return@launch search(shouldGetNextPage = true) } @@ -198,7 +198,7 @@ class SearchViewModel @Inject constructor( filters: Set = currentFilters, folder: Folder? = filterFolder, shouldGetNextPage: Boolean = false, - ) = withContext(ioCoroutineContext) { + ) = withContext(ioDispatcher) { cancelSearch() searchJob = launch { delay(SEARCH_DEBOUNCE_DURATION) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/settings/MykSuiteViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/settings/MykSuiteViewModel.kt index 34f0a75522..900f568614 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/settings/MykSuiteViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/settings/MykSuiteViewModel.kt @@ -1,6 +1,6 @@ /* * Infomaniak Mail - Android - * Copyright (C) 2025 Infomaniak Network SA + * Copyright (C) 2025-2026 Infomaniak Network SA * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,6 @@ import com.infomaniak.mail.data.models.mailbox.Mailbox import com.infomaniak.mail.di.IoDispatcher import com.infomaniak.mail.utils.AccountUtils import com.infomaniak.mail.utils.MyKSuiteDataUtils -import com.infomaniak.mail.utils.coroutineContext import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.launch @@ -39,8 +38,6 @@ class MykSuiteViewModel @Inject constructor( private val myKSuiteDataUtils: MyKSuiteDataUtils, ) : ViewModel() { - private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) - val myKSuiteMailboxResult = SingleLiveEvent() val myKSuiteDataResult = SingleLiveEvent() @@ -48,7 +45,7 @@ class MykSuiteViewModel @Inject constructor( myKSuiteMailboxResult.postValue(mailboxController.getMailbox(userId = AccountUtils.currentUserId, mailboxId = mailboxId)) } - fun refreshMyKSuite() = viewModelScope.launch(ioCoroutineContext) { + fun refreshMyKSuite() = viewModelScope.launch(ioDispatcher) { myKSuiteDataResult.postValue(myKSuiteDataUtils.fetchData()) } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadViewModel.kt index 478ca23d90..7165761fe1 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadViewModel.kt @@ -1,6 +1,6 @@ /* * Infomaniak Mail - Android - * Copyright (C) 2022-2025 Infomaniak Network SA + * Copyright (C) 2022-2026 Infomaniak Network SA * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -414,7 +414,7 @@ class ThreadViewModel @Inject constructor( return@withContext message } - private fun markThreadAsSeen(thread: Thread) = viewModelScope.launch(ioCoroutineContext) { + private fun markThreadAsSeen(thread: Thread) = viewModelScope.launch(ioDispatcher) { sharedUtils.markAsSeen(mailbox(), listOf(thread)) } @@ -450,7 +450,7 @@ class ThreadViewModel @Inject constructor( fun fetchMessagesHeavyData(messages: List) { fetchMessagesJob?.cancel() - fetchMessagesJob = viewModelScope.launch(ioCoroutineContext) { + fetchMessagesJob = viewModelScope.launch(ioDispatcher) { val (deleted, failed) = ThreadController.fetchMessagesHeavyData(messages, mailboxContentRealm()) if (deleted.isNotEmpty() || failed.isNotEmpty()) { @@ -472,7 +472,7 @@ class ThreadViewModel @Inject constructor( } } - fun deleteDraft(message: Message, mailbox: Mailbox) = viewModelScope.launch(ioCoroutineContext) { + fun deleteDraft(message: Message, mailbox: Mailbox) = viewModelScope.launch(ioDispatcher) { val realm = mailboxContentRealm() val thread = threadLive.value ?: return@launch val messages = messageController.getMessageAndDuplicates(thread, message) @@ -493,7 +493,7 @@ class ThreadViewModel @Inject constructor( } } - fun clickOnQuickActionBar(menuId: Int) = viewModelScope.launch(ioCoroutineContext) { + fun clickOnQuickActionBar(menuId: Int) = viewModelScope.launch(ioDispatcher) { val thread = threadLive.value ?: return@launch val message = messageController.getLastMessageToExecuteAction(thread, featureFlagsFlow.first()) quickActionBarClicks.postValue(QuickActionBarResult(thread.uid, message, menuId)) @@ -501,7 +501,7 @@ class ThreadViewModel @Inject constructor( fun fetchCalendarEvents(items: List, forceFetch: Boolean = false) { fetchCalendarEventJob?.cancel() - fetchCalendarEventJob = viewModelScope.launch(ioCoroutineContext) { + fetchCalendarEventJob = viewModelScope.launch(ioDispatcher) { val results = items.mapNotNull { item -> fetchCalendarEvent(item, forceFetch) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt index ca5c1c0157..a7a5841944 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt @@ -35,7 +35,6 @@ import com.infomaniak.mail.data.models.mailbox.Mailbox import com.infomaniak.mail.data.models.message.Message import com.infomaniak.mail.di.IoDispatcher import com.infomaniak.mail.utils.LocalStorageUtils.getEmlCacheDir -import com.infomaniak.mail.utils.coroutineContext import com.infomaniak.mail.utils.extensions.appContext import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher @@ -53,8 +52,6 @@ class DownloadMessagesViewModel @Inject constructor( @IoDispatcher private val ioDispatcher: CoroutineDispatcher, ) : AndroidViewModel(application) { - private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) - private val messageLocalUids: Array? inline get() = savedStateHandle[DownloadMessagesProgressDialogArgs::messageUids.name] @@ -92,7 +89,7 @@ class DownloadMessagesViewModel @Inject constructor( private fun numberOfMessagesToDownloads(): Int = messageLocalUids?.size ?: 0 - fun downloadMessages(currentMailbox: Mailbox?) = viewModelScope.launch(ioCoroutineContext) { + fun downloadMessages(currentMailbox: Mailbox?) = viewModelScope.launch(ioDispatcher) { val mailbox = currentMailbox ?: return@launch val downloadedThreadUris = runCatching { diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/AiViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/AiViewModel.kt index 47187d2b3d..97bc5c1e95 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/AiViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/AiViewModel.kt @@ -1,6 +1,6 @@ /* * Infomaniak Mail - Android - * Copyright (C) 2023-2025 Infomaniak Network SA + * Copyright (C) 2023-2026 Infomaniak Network SA * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,7 +45,6 @@ import com.infomaniak.mail.utils.ErrorCode import com.infomaniak.mail.utils.ErrorCode.MAX_SYNTAX_TOKENS_REACHED import com.infomaniak.mail.utils.ErrorCode.TOO_MANY_REQUESTS import com.infomaniak.mail.utils.SharedUtils -import com.infomaniak.mail.utils.coroutineContext import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ensureActive @@ -62,8 +61,6 @@ class AiViewModel @Inject constructor( @Inject lateinit var localSettings: LocalSettings - private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) - var aiPrompt = "" private val history = mutableListOf() private var conversationContextId: String? = null @@ -76,7 +73,7 @@ class AiViewModel @Inject constructor( fun generateNewAiProposition( currentMailboxUuid: String, formattedRecipientsString: String?, - ) = viewModelScope.launch(ioCoroutineContext) { + ) = viewModelScope.launch(ioDispatcher) { fun addVars(message: AiMessage) { formattedRecipientsString?.let { message.vars["recipient"] = it } @@ -145,7 +142,7 @@ class AiViewModel @Inject constructor( PROMPT_TOO_LONG } - fun performShortcut(shortcut: Shortcut, currentMailboxUuid: String) = viewModelScope.launch(ioCoroutineContext) { + fun performShortcut(shortcut: Shortcut, currentMailboxUuid: String) = viewModelScope.launch(ioDispatcher) { var apiResponse = ApiRepository.aiShortcutWithContext(conversationContextId!!, shortcut, currentMailboxUuid) ensureActive() @@ -158,7 +155,7 @@ class AiViewModel @Inject constructor( handleAiResult(apiResponse, apiResponse.data?.promptMessage, isUsingPreviousMessageAsContext = false) } - fun updateFeatureFlag(mailboxObjectId: String, mailboxUuid: String) = viewModelScope.launch(ioCoroutineContext) { + fun updateFeatureFlag(mailboxObjectId: String, mailboxUuid: String) = viewModelScope.launch(ioDispatcher) { sharedUtils.updateFeatureFlags(mailboxObjectId, mailboxUuid) } diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt index cb695c3f12..3741947d50 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt @@ -704,7 +704,7 @@ class NewMessageViewModel @Inject constructor( currentAttachments: List, uris: List, completion: (List) -> Unit, - ) = viewModelScope.launch(ioCoroutineContext) { + ) = viewModelScope.launch(ioDispatcher) { completion(importAttachments(currentAttachments, uris)) } @@ -787,7 +787,7 @@ class NewMessageViewModel @Inject constructor( if (recipient.isDisplayedAsExternal) trackExternalEvent(MatomoName.DeleteRecipient) } - fun deleteAttachment(position: Int) = viewModelScope.launch(ioCoroutineContext) { + fun deleteAttachment(position: Int) = viewModelScope.launch(ioDispatcher) { runCatching { val attachments = attachmentsLiveData.valueOrEmpty().toMutableList() val attachment = attachments[position] diff --git a/app/src/main/java/com/infomaniak/mail/ui/sync/SyncAutoConfigViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/sync/SyncAutoConfigViewModel.kt index 98fa21fb64..83344a49bc 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/sync/SyncAutoConfigViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/sync/SyncAutoConfigViewModel.kt @@ -1,6 +1,6 @@ /* * Infomaniak Mail - Android - * Copyright (C) 2023-2024 Infomaniak Network SA + * Copyright (C) 2023-2026 Infomaniak Network SA * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,7 +30,6 @@ import com.infomaniak.mail.di.IoDispatcher import com.infomaniak.mail.ui.main.SnackbarManager import com.infomaniak.mail.utils.AccountUtils import com.infomaniak.mail.utils.SentryDebug -import com.infomaniak.mail.utils.coroutineContext import com.infomaniak.mail.utils.extensions.appContext import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher @@ -48,7 +47,6 @@ class SyncAutoConfigViewModel @Inject constructor( private val snackbarManager: SnackbarManager, ) : AndroidViewModel(application) { - private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) private var credentialsJob: Job? = null fun isSyncAppUpToDate(): Boolean = runCatching { @@ -61,7 +59,7 @@ class SyncAutoConfigViewModel @Inject constructor( fun configureUserAutoSync(launchAutoSyncIntent: (Intent) -> Unit) { credentialsJob?.cancel() - credentialsJob = viewModelScope.launch(ioCoroutineContext) { + credentialsJob = viewModelScope.launch(ioDispatcher) { fetchCredentials(scope = this)?.let { password -> setupAutoSyncIntent(password, launchAutoSyncIntent) }