Skip to content
Merged
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
17 changes: 4 additions & 13 deletions app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import androidx.lifecycle.viewModelScope
import com.infomaniak.core.common.utils.DownloadManagerUtils
import com.infomaniak.core.ksuite.data.KSuite
import com.infomaniak.core.legacy.utils.SingleLiveEvent
import com.infomaniak.core.network.NetworkAvailability
import com.infomaniak.core.network.models.ApiResponse
import com.infomaniak.core.network.networking.HttpUtils
import com.infomaniak.core.network.networking.ManualAuthorizationRequired
Expand Down Expand Up @@ -76,6 +75,7 @@ import com.infomaniak.mail.utils.ContactUtils.getPhoneContacts
import com.infomaniak.mail.utils.ContactUtils.mergeApiContactsIntoPhoneContacts
import com.infomaniak.mail.utils.FeatureAvailability
import com.infomaniak.mail.utils.MyKSuiteDataUtils
import com.infomaniak.mail.utils.NetworkManager
import com.infomaniak.mail.utils.NotificationUtils.Companion.cancelNotification
import com.infomaniak.mail.utils.SharedUtils
import com.infomaniak.mail.utils.SharedUtils.Companion.updateSignatures
Expand Down Expand Up @@ -142,6 +142,7 @@ class MainViewModel @Inject constructor(
private val mergedContactController: MergedContactController,
private val messageController: MessageController,
private val myKSuiteDataUtils: MyKSuiteDataUtils,
private val networkManager: NetworkManager,
private val permissionsController: PermissionsController,
private val quotasController: QuotasController,
private val refreshController: RefreshController,
Expand Down Expand Up @@ -267,20 +268,10 @@ class MainViewModel @Inject constructor(

val currentThreadsLive = MutableLiveData<ResultsChange<Thread>>()

val isNetworkAvailable = NetworkAvailability(appContext).isNetworkAvailable
var hasNetwork: Boolean = true
private set

private var currentThreadsLiveJob: Job? = null

init {
viewModelScope.launch {
isNetworkAvailable.collect {
SentryLog.d("Internet availability", if (it) "Available" else "Unavailable")
hasNetwork = it
}
}
}
val isNetworkAvailable = networkManager.isNetworkAvailable
val hasNetwork: Boolean get() = networkManager.hasNetwork

fun reassignCurrentThreadsLive() {
currentThreadsLiveJob?.cancel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,6 @@ class ThreadListFragment : TwoPaneFragment(), PickerEmojiObserver {
emoji = emoji,
messageUid = messageUid,
reactions = reactions,
hasNetwork = mainViewModel.hasNetwork,
mailbox = mainViewModel.currentMailbox.value!!
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,6 @@ class ThreadFragment : Fragment(), PickerEmojiObserver {
emoji = emoji,
messageUid = messageUid,
reactions = reactions,
hasNetwork = mainViewModel.hasNetwork,
mailbox = mainViewModel.currentMailbox.value!!,
onAllowed = {
threadViewModel.fakeEmojiReply(emoji, messageUid)
Expand Down Expand Up @@ -683,7 +682,6 @@ class ThreadFragment : Fragment(), PickerEmojiObserver {
emoji = emoji,
messageUid = messageUid,
reactions = reactions,
hasNetwork = mainViewModel.hasNetwork,
mailbox = mainViewModel.currentMailbox.value!!,
onAllowed = {
threadViewModel.fakeEmojiReply(emoji, messageUid)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.infomaniak.mail.utils.AccountUtils
import com.infomaniak.mail.utils.DraftInitManager
import com.infomaniak.mail.utils.EmojiReactionUtils.hasAvailableReactionSlot
import com.infomaniak.mail.utils.ErrorCode
import com.infomaniak.mail.utils.NetworkManager
import com.infomaniak.mail.utils.Utils
import com.infomaniak.mail.utils.extensions.appContext
import com.infomaniak.mail.workers.DraftsActionsWorker
Expand All @@ -46,6 +47,7 @@ class EmojiReactionsViewModel @Inject constructor(
private val draftInitManager: DraftInitManager,
private val draftsActionsWorkerScheduler: DraftsActionsWorker.Scheduler,
private val messageController: MessageController,
private val networkManager: NetworkManager,
private val snackbarManager: SnackbarManager,
) : AndroidViewModel(application) {
/**
Expand All @@ -55,16 +57,17 @@ class EmojiReactionsViewModel @Inject constructor(
* If sending is allowed, the caller place can fake the emoji reaction locally thanks to [onAllowed].
* If sending is not allowed, it will display the error directly to the user and avoid doing the api call.
*/
val hasNetwork: Boolean get() = networkManager.hasNetwork

fun trySendEmojiReply(
emoji: String,
messageUid: String,
reactions: Map<String, Reaction>,
hasNetwork: Boolean,
mailbox: Mailbox,
onAllowed: () -> Unit = {},
) {
viewModelScope.launch {
when (val status = reactions.getEmojiSendStatus(emoji, hasNetwork)) {
when (val status = reactions.getEmojiSendStatus(emoji)) {
EmojiSendStatus.Allowed -> {
onAllowed()
sendEmojiReply(emoji, messageUid, mailbox)
Expand All @@ -74,7 +77,7 @@ class EmojiReactionsViewModel @Inject constructor(
}
}

private fun Map<String, Reaction>.getEmojiSendStatus(emoji: String, hasNetwork: Boolean): EmojiSendStatus = when {
private fun Map<String, Reaction>.getEmojiSendStatus(emoji: String): EmojiSendStatus = when {
this[emoji]?.hasReacted == true -> EmojiSendStatus.NotAllowed.AlreadyUsed
hasAvailableReactionSlot().not() -> EmojiSendStatus.NotAllowed.MaxReactionReached
hasNetwork.not() -> EmojiSendStatus.NotAllowed.NoInternet
Expand Down
52 changes: 52 additions & 0 deletions app/src/main/java/com/infomaniak/mail/utils/NetworkManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Infomaniak Mail - Android
* Copyright (C) 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.mail.utils

import com.infomaniak.core.network.NetworkAvailability
import com.infomaniak.core.sentry.SentryLog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class NetworkManager @Inject constructor() {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

/**
* A StateFlow that emits the current network availability status.
* It starts collecting immediately and keeps the latest value in memory.
*/
val isNetworkAvailable: StateFlow<Boolean> = NetworkAvailability().isNetworkAvailable
.onEach { available ->
SentryLog.d("NetworkManager", if (available) "Online" else "Offline")
}
.stateIn(
scope = scope,
started = SharingStarted.Eagerly,
initialValue = true
)

val hasNetwork: Boolean
get() = isNetworkAvailable.value
}
Loading