Use MutableSharedFlow

This commit is contained in:
Andrew 2024-06-18 12:12:25 +09:30
parent 71e7dfb131
commit 90ddc9805a
10 changed files with 59 additions and 58 deletions

View File

@ -18,7 +18,8 @@ import javax.inject.Inject
@AndroidEntryPoint
class LandingActivity: BaseActionBarActivity() {
@Inject lateinit var prefs: TextSecurePreferences
@Inject
internal lateinit var prefs: TextSecurePreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@ -20,8 +20,10 @@ import javax.inject.Inject
@androidx.annotation.OptIn(ExperimentalGetImage::class)
class LoadAccountActivity : BaseActionBarActivity() {
@Inject lateinit var prefs: TextSecurePreferences
@Inject lateinit var loadingManager: LoadingManager
@Inject
internal lateinit var prefs: TextSecurePreferences
@Inject
internal lateinit var loadingManager: LoadingManager
private val viewModel: LinkDeviceViewModel by viewModels()
@ -34,7 +36,7 @@ class LoadAccountActivity : BaseActionBarActivity() {
prefs.setLastProfileUpdateTime(0)
lifecycleScope.launch {
viewModel.eventFlow.collect {
viewModel.events.collect {
loadingManager.load(it.mnemonic)
startMessageNotificationsActivity()
finish()
@ -43,7 +45,7 @@ class LoadAccountActivity : BaseActionBarActivity() {
setComposeContent {
val state by viewModel.stateFlow.collectAsState()
LoadAccountScreen(state, viewModel.qrErrorsFlow, viewModel::onChange, viewModel::onContinue, viewModel::onScanQrCode)
LoadAccountScreen(state, viewModel.qrErrors, viewModel::onChange, viewModel::onContinue, viewModel::onScanQrCode)
}
}
}

View File

@ -4,12 +4,11 @@ import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import network.loki.messenger.R
@ -20,7 +19,7 @@ import org.session.libsignal.utilities.Hex
import org.thoughtcrime.securesms.crypto.MnemonicUtilities
import javax.inject.Inject
class LinkDeviceEvent(val mnemonic: ByteArray)
class LoadAccountEvent(val mnemonic: ByteArray)
internal data class State(
val recoveryPhrase: String = "",
@ -34,12 +33,11 @@ internal class LinkDeviceViewModel @Inject constructor(
private val state = MutableStateFlow(State())
val stateFlow = state.asStateFlow()
private val event = Channel<LinkDeviceEvent>()
val eventFlow = event.receiveAsFlow().take(1)
private val _events = MutableSharedFlow<LoadAccountEvent>()
val events = _events.asSharedFlow()
private val qrErrors = Channel<Throwable>()
val qrErrorsFlow = qrErrors.receiveAsFlow()
private val _qrErrors = MutableSharedFlow<Throwable>()
val qrErrors = _qrErrors.asSharedFlow()
.mapNotNull { application.getString(R.string.qrNotRecoveryPassword) }
private val codec by lazy { MnemonicCodec { MnemonicUtilities.loadFileContents(getApplication(), it) } }
@ -64,7 +62,7 @@ internal class LinkDeviceViewModel @Inject constructor(
state.value = State(recoveryPhrase)
}
private fun onSuccess(seed: ByteArray) {
viewModelScope.launch { event.send(LinkDeviceEvent(seed)) }
viewModelScope.launch { _events.emit(LoadAccountEvent(seed)) }
}
private fun onFailure(error: Throwable) {
@ -80,7 +78,7 @@ internal class LinkDeviceViewModel @Inject constructor(
}
private fun onQrCodeScanFailure(error: Throwable) {
viewModelScope.launch { qrErrors.send(error) }
viewModelScope.launch { _qrErrors.emit(error) }
}
private fun runDecodeCatching(mnemonic: String) = runCatching {

View File

@ -22,10 +22,10 @@ import javax.inject.Inject
class LoadingActivity: BaseActionBarActivity() {
@Inject
lateinit var configFactory: ConfigFactory
internal lateinit var configFactory: ConfigFactory
@Inject
lateinit var prefs: TextSecurePreferences
internal lateinit var prefs: TextSecurePreferences
private val viewModel: LoadingViewModel by viewModels()
@ -52,14 +52,14 @@ class LoadingActivity: BaseActionBarActivity() {
ApplicationContext.getInstance(this).newAccount = false
setComposeContent {
val state by viewModel.stateFlow.collectAsState()
val state by viewModel.states.collectAsState()
LoadingScreen(state)
}
setUpActionBarSessionLogo(true)
lifecycleScope.launch {
viewModel.eventFlow.collect {
viewModel.events.collect {
when (it) {
Event.TIMEOUT -> register(loadFailed = true)
Event.SUCCESS -> register(loadFailed = false)

View File

@ -17,12 +17,10 @@ import javax.inject.Singleton
@Singleton
class LoadingManager @Inject constructor(
@dagger.hilt.android.qualifiers.ApplicationContext val context: Context,
val configFactory: ConfigFactory,
val prefs: TextSecurePreferences
@dagger.hilt.android.qualifiers.ApplicationContext private val context: Context,
private val configFactory: ConfigFactory,
private val prefs: TextSecurePreferences
) {
val isLoading: Boolean get() = restoreJob?.isActive == true
private val database: LokiAPIDatabaseProtocol
get() = SnodeModule.shared.storage

View File

@ -5,14 +5,14 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.timeout
import kotlinx.coroutines.launch
import org.session.libsession.utilities.TextSecurePreferences
@ -29,15 +29,15 @@ private val TIMEOUT_TIME = 15.seconds
@OptIn(FlowPreview::class)
@HiltViewModel
class LoadingViewModel @Inject constructor(
internal class LoadingViewModel @Inject constructor(
val prefs: TextSecurePreferences
): ViewModel() {
private val state = MutableStateFlow(State(TIMEOUT_TIME))
val stateFlow = state.asStateFlow()
private val _states = MutableStateFlow(State(TIMEOUT_TIME))
val states = _states.asStateFlow()
private val event = Channel<Event>()
val eventFlow = event.receiveAsFlow()
private val _events = MutableSharedFlow<Event>()
val events = _events.asSharedFlow()
init {
viewModelScope.launch(Dispatchers.IO) {
@ -56,13 +56,13 @@ class LoadingViewModel @Inject constructor(
}
private suspend fun onSuccess() {
state.value = State(ANIMATE_TO_DONE_TIME)
_states.value = State(ANIMATE_TO_DONE_TIME)
delay(IDLE_DONE_TIME)
event.send(Event.SUCCESS)
_events.emit(Event.SUCCESS)
}
private fun onFail() {
event.trySend(Event.TIMEOUT)
_events.tryEmit(Event.TIMEOUT)
}
}

View File

@ -39,12 +39,12 @@ class MessageNotificationsActivity : BaseActionBarActivity() {
@Composable
private fun MessageNotificationsScreen() {
val state by viewModel.stateFlow.collectAsState()
val state by viewModel.states.collectAsState()
MessageNotificationsScreen(state, viewModel::setEnabled, ::register)
}
private fun register() {
prefs.setPushEnabled(viewModel.stateFlow.value.pushEnabled)
prefs.setPushEnabled(viewModel.states.value.pushEnabled)
ApplicationContext.getInstance(this).startPollingIfNeeded()
pushRegistry.refresh(true)

View File

@ -8,12 +8,12 @@ import kotlinx.coroutines.flow.update
import javax.inject.Inject
@HiltViewModel
class MessageNotificationsViewModel @Inject constructor(): ViewModel() {
private val state = MutableStateFlow(MessageNotificationsState())
val stateFlow = state.asStateFlow()
internal class MessageNotificationsViewModel @Inject constructor(): ViewModel() {
private val _states = MutableStateFlow(MessageNotificationsState())
val states = _states.asStateFlow()
fun setEnabled(enabled: Boolean) {
state.update { MessageNotificationsState(pushEnabled = enabled) }
_states.update { MessageNotificationsState(pushEnabled = enabled) }
}
}

View File

@ -23,8 +23,10 @@ private const val EXTRA_LOAD_FAILED = "extra_load_failed"
@AndroidEntryPoint
class PickDisplayNameActivity : BaseActionBarActivity() {
@Inject lateinit var viewModelFactory: PickDisplayNameViewModel.AssistedFactory
@Inject lateinit var prefs: TextSecurePreferences
@Inject
internal lateinit var viewModelFactory: PickDisplayNameViewModel.AssistedFactory
@Inject
internal lateinit var prefs: TextSecurePreferences
private val loadFailed get() = intent.getBooleanExtra(EXTRA_LOAD_FAILED, false)
@ -41,7 +43,7 @@ class PickDisplayNameActivity : BaseActionBarActivity() {
if (!loadFailed) prefs.setHasViewedSeed(false)
lifecycleScope.launch {
viewModel.eventFlow.collect {
viewModel.events.collect {
if (loadFailed) startHomeActivity() else startMessageNotificationsActivity()
}
}
@ -49,7 +51,7 @@ class PickDisplayNameActivity : BaseActionBarActivity() {
@Composable
private fun DisplayNameScreen(viewModel: PickDisplayNameViewModel) {
val state = viewModel.stateFlow.collectAsState()
val state = viewModel.states.collectAsState()
DisplayName(state.value, viewModel::onChange) { viewModel.onContinue(this) }
}
}

View File

@ -7,10 +7,10 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import network.loki.messenger.R
@ -23,28 +23,28 @@ import org.session.libsignal.utilities.hexEncodedPublicKey
import org.thoughtcrime.securesms.crypto.KeyPairUtilities
import org.thoughtcrime.securesms.dependencies.ConfigFactory
class PickDisplayNameViewModel(
internal class PickDisplayNameViewModel(
pickNewName: Boolean,
private val prefs: TextSecurePreferences,
private val configFactory: ConfigFactory
): ViewModel() {
private val state = MutableStateFlow(if (pickNewName) pickNewNameState() else State())
val stateFlow = state.asStateFlow()
private val _states = MutableStateFlow(if (pickNewName) pickNewNameState() else State())
val states = _states.asStateFlow()
private val event = Channel<Event>()
val eventFlow = event.receiveAsFlow()
private val _events = MutableSharedFlow<Event>()
val events = _events.asSharedFlow()
private val database: LokiAPIDatabaseProtocol
get() = SnodeModule.shared.storage
fun onContinue(context: Context) {
state.update { it.copy(displayName = it.displayName.trim()) }
_states.update { it.copy(displayName = it.displayName.trim()) }
val displayName = state.value.displayName
val displayName = _states.value.displayName
when {
displayName.isEmpty() -> { state.update { it.copy(error = R.string.displayNameErrorDescription) } }
displayName.length > NAME_PADDED_LENGTH -> { state.update { it.copy(error = R.string.displayNameErrorDescriptionShorter) } }
displayName.isEmpty() -> { _states.update { it.copy(error = R.string.displayNameErrorDescription) } }
displayName.length > NAME_PADDED_LENGTH -> { _states.update { it.copy(error = R.string.displayNameErrorDescriptionShorter) } }
else -> {
prefs.setProfileName(displayName)
@ -66,13 +66,13 @@ class PickDisplayNameViewModel(
prefs.setLocalNumber(userHexEncodedPublicKey)
prefs.setRestorationTime(0)
viewModelScope.launch { event.send(Event.DONE) }
viewModelScope.launch { _events.emit(Event.DONE) }
}
}
}
fun onChange(value: String) {
state.update { state ->
_states.update { state ->
state.copy(
displayName = value,
error = value.takeIf { it.length > NAME_PADDED_LENGTH }?.let { R.string.displayNameErrorDescriptionShorter }