diff --git a/Core b/Core index b73b931413..c1fe1e93d9 160000 --- a/Core +++ b/Core @@ -1 +1 @@ -Subproject commit b73b9314138d2a6dc181118b5277c71de1f28eae +Subproject commit c1fe1e93d9a364b932f00c2e6b4583e972468802 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 a2371289ca..07db769e4f 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -20,7 +20,6 @@ package com.infomaniak.mail.ui import android.app.Application import androidx.lifecycle.* import com.infomaniak.lib.core.models.ApiResponse -import com.infomaniak.lib.core.networking.HttpClient import com.infomaniak.lib.core.networking.HttpUtils import com.infomaniak.lib.core.networking.NetworkAvailability import com.infomaniak.lib.core.utils.ApiErrorCode.Companion.translateError @@ -76,8 +75,6 @@ import io.sentry.Sentry import io.sentry.SentryLevel import kotlinx.coroutines.* import kotlinx.coroutines.flow.* -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.MissingFieldException import okhttp3.Request import java.util.Date import java.util.UUID @@ -322,7 +319,7 @@ class MainViewModel @Inject constructor( AccountUtils.updateCurrentUser() // Refresh My kSuite asynchronously, because it's not required for the threads list display - launch { updateMyKSuiteData() } + launch { MyKSuiteDataUtils.fetchMyKSuiteData() } // Refresh Mailboxes SentryLog.d(TAG, "Refresh mailboxes from remote") @@ -359,26 +356,6 @@ class MainViewModel @Inject constructor( } } - private suspend fun updateMyKSuiteData() { - runCatching { - MyKSuiteDataUtils.requestKSuiteData() - val apiResponse = ApiRepository.getMyKSuiteData(HttpClient.okHttpClient) - if (apiResponse.data != null) { - MyKSuiteDataUtils.upsertKSuiteData(apiResponse.data!!) - } else { - @OptIn(ExperimentalSerializationApi::class) - apiResponse.error?.exception?.let { - if (it is MissingFieldException || it.message?.contains("Unexpected JSON token") == true) { - SentryLog.e(TAG, "Error decoding the api result MyKSuiteObject", it) - } - } - } - }.onFailure { exception -> - if (exception is CancellationException) throw exception - SentryLog.d(TAG, "Exception during myKSuite data fetch", exception) - } - } - private fun selectMailbox(mailbox: Mailbox) { if (mailbox.objectId != _currentMailboxObjectId.value) { SentryLog.d(TAG, "Select mailbox: ${mailbox.email}") diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/settings/KSuiteDashboardFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/settings/KSuiteDashboardFragment.kt index 27ca294aec..cc92bb616d 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/settings/KSuiteDashboardFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/settings/KSuiteDashboardFragment.kt @@ -18,33 +18,34 @@ package com.infomaniak.mail.ui.main.settings import android.os.Bundle -import android.util.Log import android.view.View -import androidx.lifecycle.lifecycleScope -import com.infomaniak.core.myksuite.ui.data.MyKSuiteData +import androidx.fragment.app.viewModels import com.infomaniak.core.myksuite.ui.views.MyKSuiteDashboardFragment -import com.infomaniak.mail.utils.MyKSuiteDataUtils +import com.infomaniak.mail.utils.AccountUtils +import com.infomaniak.mail.utils.MyKSuiteUiUtils.getKSuiteQuotasApp import com.infomaniak.mail.utils.extensions.setSystemBarsColors -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch +import dagger.hilt.android.AndroidEntryPoint import com.infomaniak.core.myksuite.R as RMyKSuite +@AndroidEntryPoint class KSuiteDashboardFragment : MyKSuiteDashboardFragment() { - private var kSuiteData: MyKSuiteData? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - lifecycleScope.launch(Dispatchers.IO) { - if (kSuiteData == null) MyKSuiteDataUtils.requestKSuiteData(MyKSuiteDataUtils.myKSuiteId) - Log.e("TOTO", "onCreate: $kSuiteData") - } - } + private val myKSuiteViewModel: MykSuiteViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setSystemBarsColors(statusBarColor = RMyKSuite.color.dashboardBackground) + + myKSuiteViewModel.refreshMyKSuite() + myKSuiteViewModel.myKSuiteDataResult.observe(viewLifecycleOwner) { myKSuiteData -> + myKSuiteData?.let { data -> + resetContent( + myKSuiteData = data, + avatarUri = AccountUtils.currentUser?.avatar ?: "", + products = requireContext().getKSuiteQuotasApp(myKSuiteData).toList(), + ) + } + } } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/settings/SettingsViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/settings/MykSuiteViewModel.kt similarity index 72% rename from app/src/main/java/com/infomaniak/mail/ui/main/settings/SettingsViewModel.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/settings/MykSuiteViewModel.kt index 16630a7caa..a43c1f7c47 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/settings/MykSuiteViewModel.kt @@ -19,22 +19,31 @@ package com.infomaniak.mail.ui.main.settings import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.infomaniak.core.myksuite.ui.data.MyKSuiteData import com.infomaniak.lib.core.utils.SingleLiveEvent import com.infomaniak.mail.data.cache.mailboxInfo.MailboxController import com.infomaniak.mail.data.models.mailbox.Mailbox +import com.infomaniak.mail.di.IoDispatcher import com.infomaniak.mail.di.MailboxInfoRealm 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 io.realm.kotlin.Realm +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class SettingsViewModel @Inject constructor( +class MykSuiteViewModel @Inject constructor( @MailboxInfoRealm private val mailboxInfoRealm: Realm, + @IoDispatcher private val ioDispatcher: CoroutineDispatcher, ) : ViewModel() { + private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) + val myKSuiteMailboxResult = SingleLiveEvent() + val myKSuiteDataResult = SingleLiveEvent() fun getMyKSuiteMailbox(mailboxId: Int) = viewModelScope.launch { myKSuiteMailboxResult.postValue( @@ -45,4 +54,8 @@ class SettingsViewModel @Inject constructor( ) ) } + + fun refreshMyKSuite() = viewModelScope.launch(ioCoroutineContext) { + myKSuiteDataResult.postValue(MyKSuiteDataUtils.fetchMyKSuiteData()) + } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/settings/SettingsFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/settings/SettingsFragment.kt index ea30fcceb3..9421a56ade 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/settings/SettingsFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/settings/SettingsFragment.kt @@ -26,13 +26,9 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels -import com.infomaniak.core.myksuite.ui.components.MyKSuiteTier import com.infomaniak.core.myksuite.ui.data.MyKSuiteData -import com.infomaniak.core.myksuite.ui.screens.components.KSuiteProductsWithQuotas -import com.infomaniak.core.myksuite.ui.views.MyKSuiteDashboardFragmentArgs import com.infomaniak.lib.applock.LockActivity import com.infomaniak.lib.applock.Utils.silentlyReverseSwitch -import com.infomaniak.lib.core.utils.FormatterFileSize.formatShortFileSize import com.infomaniak.lib.core.utils.openAppNotificationSettings import com.infomaniak.lib.core.utils.safeBinding import com.infomaniak.lib.core.utils.showToast @@ -44,8 +40,8 @@ import com.infomaniak.mail.data.LocalSettings import com.infomaniak.mail.data.models.FeatureFlag import com.infomaniak.mail.databinding.FragmentSettingsBinding import com.infomaniak.mail.ui.MainViewModel -import com.infomaniak.mail.utils.AccountUtils import com.infomaniak.mail.utils.MyKSuiteDataUtils +import com.infomaniak.mail.utils.MyKSuiteUiUtils.openMyKSuiteDashboard import com.infomaniak.mail.utils.UiUtils.saveFocusWhenNavigatingBack import com.infomaniak.mail.utils.extensions.animatedNavigation import com.infomaniak.mail.utils.extensions.launchSyncAutoConfigActivityForResult @@ -53,14 +49,13 @@ import com.infomaniak.mail.utils.extensions.observeNotNull import com.infomaniak.mail.utils.extensions.setSystemBarsColors import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject -import com.infomaniak.core.myksuite.R as RMyKSuite @AndroidEntryPoint class SettingsFragment : Fragment() { private var binding: FragmentSettingsBinding by safeBinding() private val mainViewModel: MainViewModel by activityViewModels() - private val settingsViewModel: SettingsViewModel by viewModels() + private val myKSuiteViewModel: MykSuiteViewModel by viewModels() @Inject lateinit var localSettings: LocalSettings @@ -84,38 +79,26 @@ class SettingsFragment : Fragment() { setupMyKSuite() observeFeatureFlag() + observeMyKSuiteData() } private fun setupMyKSuite() { binding.myKSuiteLayout.isGone = MyKSuiteDataUtils.myKSuite == null - MyKSuiteDataUtils.myKSuite?.let { setupMyKSuiteLayout(it) } ?: fetchMyKSuite() + MyKSuiteDataUtils.myKSuite?.let { setupMyKSuiteLayout(it) } ?: myKSuiteViewModel.refreshMyKSuite() } - // TODO Manage when My KSuite is null but user has at least one mailbox free (V2 ?) - private fun fetchMyKSuite() {} - private fun setupMyKSuiteLayout(myKSuiteData: MyKSuiteData) = with(binding) { observeMyKSuiteMailbox() myKSuiteData.kSuitePack.type?.displayNameRes?.let(myKSuiteSettingsTitle::setText) - settingsViewModel.getMyKSuiteMailbox(myKSuiteData.mail.mailboxId) - - myKSuiteSubscription.setOnClickListener { - val args = MyKSuiteDashboardFragmentArgs( - myKSuiteTier = if (myKSuiteData.isMyKSuitePlus) MyKSuiteTier.Plus else MyKSuiteTier.Free, - email = myKSuiteData.mail.email, - avatarUri = AccountUtils.currentUser?.avatar ?: "", - dailySendLimit = myKSuiteData.mail.dailyLimitSent.toString(), - kSuiteAppsWithQuotas = getKSuiteQuotasApp(myKSuiteData), - trialExpiryDate = myKSuiteData.trialExpiryDate, - ) - animatedNavigation(resId = R.id.myKSuiteDashboardFragment, args = args.toBundle()) - } + myKSuiteViewModel.getMyKSuiteMailbox(myKSuiteData.mail.mailboxId) + + myKSuiteSubscription.setOnClickListener { openMyKSuiteDashboard(myKSuiteData) } } private fun observeMyKSuiteMailbox() { - settingsViewModel.myKSuiteMailboxResult.observe(viewLifecycleOwner) { mailbox -> + myKSuiteViewModel.myKSuiteMailboxResult.observe(viewLifecycleOwner) { mailbox -> binding.myKSuiteMailAddress.apply { isVisible = mailbox != null @@ -131,25 +114,8 @@ class SettingsFragment : Fragment() { } } - private fun getKSuiteQuotasApp(myKSuite: MyKSuiteData): Array { - - val mailProduct = with(myKSuite.mail) { - KSuiteProductsWithQuotas.Mail( - usedSize = requireContext().formatShortFileSize(usedSize), - maxSize = requireContext().formatShortFileSize(storageSizeLimit), - progress = (usedSize.toDouble() / storageSizeLimit.toDouble()).toFloat(), - ) - } - - val driveProduct = with(myKSuite.drive) { - KSuiteProductsWithQuotas.Drive( - usedSize = requireContext().formatShortFileSize(usedSize), - maxSize = requireContext().formatShortFileSize(size), - progress = (usedSize.toDouble() / size.toDouble()).toFloat(), - ) - } - - return arrayOf(mailProduct, driveProduct) + private fun observeMyKSuiteData() { + myKSuiteViewModel.myKSuiteDataResult.observe(viewLifecycleOwner) { data -> data?.let(::setupMyKSuiteLayout) } } override fun onResume() { diff --git a/app/src/main/java/com/infomaniak/mail/utils/MyKSuiteDataUtils.kt b/app/src/main/java/com/infomaniak/mail/utils/MyKSuiteDataUtils.kt index 1025299944..b896c6a39b 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/MyKSuiteDataUtils.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/MyKSuiteDataUtils.kt @@ -19,11 +19,19 @@ package com.infomaniak.mail.utils import com.infomaniak.core.myksuite.ui.data.MyKSuiteData import com.infomaniak.core.myksuite.ui.data.MyKSuiteDataManager +import com.infomaniak.lib.core.networking.HttpClient +import com.infomaniak.lib.core.utils.SentryLog +import com.infomaniak.mail.data.api.ApiRepository import com.infomaniak.mail.data.cache.appSettings.AppSettingsController import com.infomaniak.mail.data.models.AppSettings +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.MissingFieldException +import kotlin.coroutines.cancellation.CancellationException object MyKSuiteDataUtils : MyKSuiteDataManager() { + private val TAG = MyKSuiteDataUtils::class.simpleName.toString() + override val userId get() = AccountUtils.currentUserId override var myKSuiteId: Int = AppSettingsController.getAppSettings().myKSuiteId @@ -37,4 +45,25 @@ object MyKSuiteDataUtils : MyKSuiteDataManager() { field = myKSuiteData myKSuiteId = myKSuiteData?.id ?: AppSettings.DEFAULT_ID } + + suspend fun fetchMyKSuiteData(): MyKSuiteData? = runCatching { + MyKSuiteDataUtils.requestKSuiteData() + val apiResponse = ApiRepository.getMyKSuiteData(HttpClient.okHttpClient) + if (apiResponse.data != null) { + MyKSuiteDataUtils.upsertKSuiteData(apiResponse.data!!) + } else { + @OptIn(ExperimentalSerializationApi::class) + apiResponse.error?.exception?.let { + if (it is MissingFieldException || it.message?.contains("Unexpected JSON token") == true) { + SentryLog.e(TAG, "Error decoding the api result MyKSuiteObject", it) + } + } + } + + return@runCatching apiResponse.data + }.getOrElse { exception -> + if (exception is CancellationException) throw exception + SentryLog.d(TAG, "Exception during myKSuite data fetch", exception) + null + } } diff --git a/app/src/main/java/com/infomaniak/mail/utils/MyKSuiteUiUtils.kt b/app/src/main/java/com/infomaniak/mail/utils/MyKSuiteUiUtils.kt index 708d4a01b7..da003644a5 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/MyKSuiteUiUtils.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/MyKSuiteUiUtils.kt @@ -17,12 +17,19 @@ */ package com.infomaniak.mail.utils +import android.content.Context import androidx.fragment.app.Fragment import androidx.navigation.NavController +import com.infomaniak.core.myksuite.ui.components.MyKSuiteTier +import com.infomaniak.core.myksuite.ui.data.MyKSuiteData import com.infomaniak.core.myksuite.ui.screens.KSuiteApp +import com.infomaniak.core.myksuite.ui.screens.components.KSuiteProductsWithQuotas +import com.infomaniak.core.myksuite.ui.views.MyKSuiteDashboardFragmentArgs import com.infomaniak.core.myksuite.ui.views.MyKSuiteUpgradeBottomSheetDialogArgs +import com.infomaniak.lib.core.utils.FormatterFileSize.formatShortFileSize import com.infomaniak.lib.core.utils.safeNavigate import com.infomaniak.mail.R +import com.infomaniak.mail.utils.extensions.animatedNavigation object MyKSuiteUiUtils { @@ -35,4 +42,37 @@ object MyKSuiteUiUtils { val args = MyKSuiteUpgradeBottomSheetDialogArgs(kSuiteApp = KSuiteApp.Mail) navController.navigate(R.id.myKSuiteUpgradeBottomSheet, args.toBundle()) } + + fun Fragment.openMyKSuiteDashboard(myKSuiteData: MyKSuiteData) { + val args = MyKSuiteDashboardFragmentArgs( + myKSuiteTier = if (myKSuiteData.isMyKSuitePlus) MyKSuiteTier.Plus else MyKSuiteTier.Free, + email = myKSuiteData.mail.email, + avatarUri = AccountUtils.currentUser?.avatar ?: "", + dailySendLimit = myKSuiteData.mail.dailyLimitSent.toString(), + kSuiteAppsWithQuotas = requireContext().getKSuiteQuotasApp(myKSuiteData), + trialExpiryDate = myKSuiteData.trialExpiryDate, + ) + animatedNavigation(resId = R.id.myKSuiteDashboardFragment, args = args.toBundle()) + } + + fun Context.getKSuiteQuotasApp(myKSuite: MyKSuiteData): Array { + + val mailProduct = with(myKSuite.mail) { + KSuiteProductsWithQuotas.Mail( + usedSize = formatShortFileSize(usedSize), + maxSize = formatShortFileSize(storageSizeLimit), + progress = (usedSize.toDouble() / storageSizeLimit.toDouble()).toFloat(), + ) + } + + val driveProduct = with(myKSuite.drive) { + KSuiteProductsWithQuotas.Drive( + usedSize = formatShortFileSize(usedSize), + maxSize = formatShortFileSize(size), + progress = (usedSize.toDouble() / size.toDouble()).toFloat(), + ) + } + + return arrayOf(mailProduct, driveProduct) + } }