Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 48 additions & 48 deletions app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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()
Expand All @@ -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()
}
Expand All @@ -89,7 +87,7 @@ class ThreadListViewModel @Inject constructor(
isWebViewOutdated.value = canShowWebViewOutdated && hasOutdatedMajorVersion
}

suspend fun getEmojiReactionsFor(messageUid: String): Map<String, Reaction>? = withContext(ioCoroutineContext) {
suspend fun getEmojiReactionsFor(messageUid: String): Map<String, Reaction>? = withContext(ioDispatcher) {
messageController.getMessage(messageUid)?.let { message ->
message.emojiReactions.associateBy { it.emoji }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand All @@ -47,8 +46,6 @@ class TwoPaneViewModel @Inject constructor(
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
) : ViewModel() {

private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher)

val currentThreadUid: LiveData<String?> = state.getLiveData(CURRENT_THREAD_UID_KEY)

inline val isThreadOpen get() = currentThreadUid.value != null
Expand All @@ -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())
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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<String>(FolderPickerFragmentArgs::sourceFolderId.name)
Expand All @@ -56,7 +53,7 @@ class FolderPickerViewModel @Inject constructor(
var hasAlreadyTrackedSearch = false

init {
viewModelScope.launch(ioCoroutineContext) {
viewModelScope.launch(ioDispatcher) {
sourceFolderIdLiveData.postValue(sourceFolderId)
}
}
Expand Down Expand Up @@ -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()

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
Expand All @@ -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)
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -198,7 +198,7 @@ class SearchViewModel @Inject constructor(
filters: Set<ThreadFilter> = currentFilters,
folder: Folder? = filterFolder,
shouldGetNextPage: Boolean = false,
) = withContext(ioCoroutineContext) {
) = withContext(ioDispatcher) {
cancelSearch()
searchJob = launch {
delay(SEARCH_DEBOUNCE_DURATION)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -39,16 +38,14 @@ class MykSuiteViewModel @Inject constructor(
private val myKSuiteDataUtils: MyKSuiteDataUtils,
) : ViewModel() {

private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher)

val myKSuiteMailboxResult = SingleLiveEvent<Mailbox?>()
val myKSuiteDataResult = SingleLiveEvent<MyKSuiteData?>()

fun getMyKSuiteMailbox(mailboxId: Int) = viewModelScope.launch {
myKSuiteMailboxResult.postValue(mailboxController.getMailbox(userId = AccountUtils.currentUserId, mailboxId = mailboxId))
}

fun refreshMyKSuite() = viewModelScope.launch(ioCoroutineContext) {
fun refreshMyKSuite() = viewModelScope.launch(ioDispatcher) {
myKSuiteDataResult.postValue(myKSuiteDataUtils.fetchData())
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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))
}

Expand Down Expand Up @@ -450,7 +450,7 @@ class ThreadViewModel @Inject constructor(

fun fetchMessagesHeavyData(messages: List<Message>) {
fetchMessagesJob?.cancel()
fetchMessagesJob = viewModelScope.launch(ioCoroutineContext) {
fetchMessagesJob = viewModelScope.launch(ioDispatcher) {
val (deleted, failed) = ThreadController.fetchMessagesHeavyData(messages, mailboxContentRealm())
if (deleted.isNotEmpty() || failed.isNotEmpty()) {

Expand All @@ -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)
Expand All @@ -493,15 +493,15 @@ 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))
}

fun fetchCalendarEvents(items: List<Any>, forceFetch: Boolean = false) {
fetchCalendarEventJob?.cancel()
fetchCalendarEventJob = viewModelScope.launch(ioCoroutineContext) {
fetchCalendarEventJob = viewModelScope.launch(ioDispatcher) {

val results = items.mapNotNull { item ->
fetchCalendarEvent(item, forceFetch)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<String>?
inline get() = savedStateHandle[DownloadMessagesProgressDialogArgs::messageUids.name]

Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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<AiMessage>()
private var conversationContextId: String? = null
Expand All @@ -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 }
Expand Down Expand Up @@ -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()

Expand All @@ -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)
}

Expand Down
Loading