Skip to content

Commit

Permalink
feat(DynamicDashboard): First updating version of dashboard
Browse files Browse the repository at this point in the history
Update dashboard data by totally resetting the Compose content of the compose view
  • Loading branch information
FabianDevel committed Feb 20, 2025
1 parent 1599fc1 commit a4df8ec
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 86 deletions.
25 changes: 1 addition & 24 deletions app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Mailbox?>()
val myKSuiteDataResult = SingleLiveEvent<MyKSuiteData?>()

fun getMyKSuiteMailbox(mailboxId: Int) = viewModelScope.launch {
myKSuiteMailboxResult.postValue(
Expand All @@ -45,4 +54,8 @@ class SettingsViewModel @Inject constructor(
)
)
}

fun refreshMyKSuite() = viewModelScope.launch(ioCoroutineContext) {
myKSuiteDataResult.postValue(MyKSuiteDataUtils.fetchMyKSuiteData())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -44,23 +40,22 @@ 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
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
Expand All @@ -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

Expand All @@ -131,25 +114,8 @@ class SettingsFragment : Fragment() {
}
}

private fun getKSuiteQuotasApp(myKSuite: MyKSuiteData): Array<KSuiteProductsWithQuotas> {

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() {
Expand Down
29 changes: 29 additions & 0 deletions app/src/main/java/com/infomaniak/mail/utils/MyKSuiteDataUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
}
40 changes: 40 additions & 0 deletions app/src/main/java/com/infomaniak/mail/utils/MyKSuiteUiUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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<KSuiteProductsWithQuotas> {

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)
}
}

0 comments on commit a4df8ec

Please sign in to comment.