Migrate DataStateExtensions to core module

Move `DataStateExtensions` to the `core` module.

- Deleted `DataStateExtensions.kt` from `authenticator` and `app` modules.
- Updated all imports.
- Added `junit5` and `kotlinx.coroutines.test` test implementation to `core` module.
This commit is contained in:
Patrick Honkonen
2025-03-28 11:20:58 -04:00
parent 5d5bc25a45
commit 9a50399116
12 changed files with 21 additions and 111 deletions

View File

@@ -23,13 +23,13 @@ import com.bitwarden.authenticator.data.platform.manager.imports.model.ImportDat
import com.bitwarden.authenticator.data.platform.manager.imports.model.ImportFileFormat
import com.bitwarden.authenticator.data.platform.manager.model.FlagKey
import com.bitwarden.authenticator.data.platform.repository.SettingsRepository
import com.bitwarden.core.data.repository.model.DataState
import com.bitwarden.authenticator.data.platform.repository.util.map
import com.bitwarden.authenticator.ui.platform.feature.settings.export.model.ExportVaultFormat
import com.bitwarden.authenticator.ui.platform.manager.intent.IntentManager
import com.bitwarden.authenticatorbridge.manager.AuthenticatorBridgeManager
import com.bitwarden.authenticatorbridge.manager.model.AccountSyncState
import com.bitwarden.core.data.repository.model.DataState
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
import com.bitwarden.core.data.repository.util.map
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel

View File

@@ -1,94 +0,0 @@
package com.bitwarden.authenticator.data.platform.repository.util
import com.bitwarden.core.data.repository.model.DataState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.transformWhile
/**
* Maps the data inside a [DataState] with the given [transform].
*/
inline fun <T : Any?, R : Any?> DataState<T>.map(
transform: (T) -> R,
): DataState<R> = when (this) {
is DataState.Loaded -> DataState.Loaded(transform(data))
is DataState.Loading -> DataState.Loading
is DataState.Pending -> DataState.Pending(transform(data))
is DataState.Error -> DataState.Error(error, data?.let(transform))
is DataState.NoNetwork -> DataState.NoNetwork(data?.let(transform))
}
/**
* Emits all values of a [DataState] [Flow] until it emits a [DataState.Loaded].
*/
fun <T : Any?> Flow<DataState<T>>.takeUntilLoaded(): Flow<DataState<T>> = transformWhile {
emit(it)
it !is DataState.Loaded
}
/**
* Combines the [dataState1] and [dataState2] [DataState]s together using the provided [transform].
*
* This function will internally manage the final `DataState` type that is returned. This is done
* by prioritizing each if the states in this order:
*
* - [DataState.Error]
* - [DataState.NoNetwork]
* - [DataState.Loading]
* - [DataState.Pending]
* - [DataState.Loaded]
*
* This priority order ensures that the total state is accurately reflecting the underlying states.
* If one of the `DataState`s has a higher priority than the other, the output will be the highest
* priority. For example, if one `DataState` is `DataState.Loaded` and another is `DataState.Error`
* then the returned `DataState` will be `DataState.Error`.
*
* The payload of the final `DataState` be created by the `transform` lambda which will be invoked
* whenever the payloads of both `dataState1` and `dataState2` are not null. In the scenario where
* one or both payloads are null, the resulting `DataState` will have a null payload.
*/
fun <T1, T2, R> combineDataStates(
dataState1: DataState<T1>,
dataState2: DataState<T2>,
transform: (t1: T1, t2: T2) -> R,
): DataState<R> {
// Wraps the `transform` lambda to allow null data to be passed in. If either of the passed in
// values are null, the regular transform will not be invoked and null is returned.
val nullableTransform: (T1?, T2?) -> R? = { t1, t2 ->
if (t1 != null && t2 != null) transform(t1, t2) else null
}
return when {
// Error states have highest priority, fail fast.
dataState1 is DataState.Error -> {
DataState.Error(
error = dataState1.error,
data = nullableTransform(dataState1.data, dataState2.data),
)
}
dataState2 is DataState.Error -> {
DataState.Error(
error = dataState2.error,
data = nullableTransform(dataState1.data, dataState2.data),
)
}
dataState1 is DataState.NoNetwork || dataState2 is DataState.NoNetwork -> {
DataState.NoNetwork(nullableTransform(dataState1.data, dataState2.data))
}
// Something is still loading, we will wait for all the data.
dataState1 is DataState.Loading || dataState2 is DataState.Loading -> DataState.Loading
// Pending state for everything while any one piece of data is updating.
dataState1 is DataState.Pending || dataState2 is DataState.Pending -> {
@Suppress("UNCHECKED_CAST")
DataState.Pending(transform(dataState1.data as T1, dataState2.data as T2))
}
// Both states are Loaded and have data
else -> {
@Suppress("UNCHECKED_CAST")
DataState.Loaded(transform(dataState1.data as T1, dataState2.data as T2))
}
}
}

View File

@@ -11,9 +11,6 @@ import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.Aut
import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemType
import com.bitwarden.authenticator.data.authenticator.repository.AuthenticatorRepository
import com.bitwarden.authenticator.data.authenticator.repository.model.CreateItemResult
import com.bitwarden.core.data.repository.model.DataState
import com.bitwarden.authenticator.data.platform.repository.util.takeUntilLoaded
import com.bitwarden.authenticator.ui.authenticator.feature.edititem.AuthenticatorRefreshPeriodOption.entries
import com.bitwarden.authenticator.ui.authenticator.feature.edititem.EditItemState.Companion.MAX_ALLOWED_CODE_DIGITS
import com.bitwarden.authenticator.ui.authenticator.feature.edititem.EditItemState.Companion.MIN_ALLOWED_CODE_DIGITS
import com.bitwarden.authenticator.ui.authenticator.feature.edititem.model.EditItemData
@@ -22,6 +19,8 @@ import com.bitwarden.authenticator.ui.platform.base.util.Text
import com.bitwarden.authenticator.ui.platform.base.util.asText
import com.bitwarden.authenticator.ui.platform.base.util.concat
import com.bitwarden.authenticator.ui.platform.base.util.isBase32
import com.bitwarden.core.data.repository.model.DataState
import com.bitwarden.core.data.repository.util.takeUntilLoaded
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map