Use EventSink lambda in state instead of Flow in Presenter

This commit is contained in:
ganfra 2023-01-11 15:53:52 +01:00
parent 56e54bb172
commit 1a0c9df1da
43 changed files with 277 additions and 490 deletions

View file

@ -22,14 +22,6 @@ class ChangeServerNode @AssistedInject constructor(
private val presenterConnector = presenterConnector(presenter)
private fun onChangeServer(server: String) {
presenterConnector.emitEvent(ChangeServerEvents.SetServer(server))
}
private fun onSubmit() {
presenterConnector.emitEvent(ChangeServerEvents.Submit)
}
private fun onSuccess() {
navigateUp()
}
@ -39,8 +31,6 @@ class ChangeServerNode @AssistedInject constructor(
val state by presenterConnector.stateFlow.collectAsState()
ChangeServerView(
state = state,
onChangeServer = this::onChangeServer,
onChangeServerSubmit = this::onSubmit,
onChangeServerSuccess = this::onSuccess,
)
}

View file

@ -1,41 +1,42 @@
package io.element.android.x.features.login.changeserver
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.x.architecture.Async
import io.element.android.x.architecture.Presenter
import io.element.android.x.architecture.execute
import io.element.android.x.matrix.Matrix
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import javax.inject.Inject
class ChangeServerPresenter @Inject constructor(private val matrix: Matrix) : Presenter<ChangeServerState, ChangeServerEvents> {
class ChangeServerPresenter @Inject constructor(private val matrix: Matrix) : Presenter<ChangeServerState> {
@Composable
override fun present(events: Flow<ChangeServerEvents>): ChangeServerState {
override fun present(): ChangeServerState {
val localCoroutineScope = rememberCoroutineScope()
val homeserver = rememberSaveable {
mutableStateOf(matrix.getHomeserverOrDefault())
}
val changeServerAction: MutableState<Async<Unit>> = remember {
mutableStateOf(Async.Uninitialized)
}
LaunchedEffect(Unit) {
events.collect { event ->
when (event) {
is ChangeServerEvents.SetServer -> homeserver.value = event.server
ChangeServerEvents.Submit -> submit(homeserver.value, changeServerAction)
}
fun handleEvents(event: ChangeServerEvents) {
when (event) {
is ChangeServerEvents.SetServer -> homeserver.value = event.server
ChangeServerEvents.Submit -> localCoroutineScope.submit(homeserver.value, changeServerAction)
}
}
return ChangeServerState(
homeserver = homeserver.value,
changeServerAction = changeServerAction.value
changeServerAction = changeServerAction.value,
eventSink = ::handleEvents
)
}

View file

@ -5,6 +5,7 @@ import io.element.android.x.architecture.Async
data class ChangeServerState(
val homeserver: String = "",
val changeServerAction: Async<Unit> = Async.Uninitialized,
val eventSink: (ChangeServerEvents) -> Unit = {},
) {
val submitEnabled = homeserver.isNotEmpty() && changeServerAction !is Async.Loading
}

View file

@ -64,14 +64,13 @@ import io.element.android.x.features.login.error.changeServerError
fun ChangeServerView(
state: ChangeServerState,
modifier: Modifier = Modifier,
onChangeServer: (String) -> Unit = {},
onChangeServerSubmit: () -> Unit = {},
onChangeServerSuccess: () -> Unit = {},
) {
Surface(
modifier = modifier,
color = MaterialTheme.colorScheme.background,
) {
val eventSink = state.eventSink
val scrollState = rememberScrollState()
Box(
modifier = Modifier
@ -135,7 +134,7 @@ fun ChangeServerView(
.padding(top = 200.dp),
onValueChange = {
homeserverFieldState = it
onChangeServer(it)
eventSink(ChangeServerEvents.SetServer(it))
},
label = {
Text(text = "Server")
@ -146,7 +145,7 @@ fun ChangeServerView(
imeAction = ImeAction.Done,
),
keyboardActions = KeyboardActions(
onDone = { onChangeServerSubmit() }
onDone = { eventSink(ChangeServerEvents.Submit) }
)
)
if (state.changeServerAction is Async.Failure) {
@ -161,7 +160,7 @@ fun ChangeServerView(
)
}
Button(
onClick = onChangeServerSubmit,
onClick = { eventSink(ChangeServerEvents.Submit) },
enabled = state.submitEnabled,
modifier = Modifier
.fillMaxWidth()

View file

@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.Lifecycle
import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
@ -13,6 +14,7 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.x.anvilannotations.ContributesNode
import io.element.android.x.architecture.presenterConnector
import io.element.android.x.core.compose.OnLifecycleEvent
import io.element.android.x.di.AppScope
@ContributesNode(AppScope::class)
@ -24,12 +26,6 @@ class LoginRootNode @AssistedInject constructor(
private val presenterConnector = presenterConnector(presenter)
init {
lifecycle.subscribe(
onResume = { presenterConnector.emitEvent(LoginRootEvents.RefreshHomeServer) }
)
}
interface Callback : Plugin {
fun onChangeHomeServer()
}
@ -38,27 +34,18 @@ class LoginRootNode @AssistedInject constructor(
plugins<Callback>().forEach { it.onChangeHomeServer() }
}
private fun onLoginChanged(login: String) {
presenterConnector.emitEvent(LoginRootEvents.SetLogin(login))
}
private fun onPasswordChanged(password: String) {
presenterConnector.emitEvent(LoginRootEvents.SetPassword(password))
}
private fun onSubmit() {
presenterConnector.emitEvent(LoginRootEvents.Submit)
}
@Composable
override fun View(modifier: Modifier) {
val state by presenterConnector.stateFlow.collectAsState()
OnLifecycleEvent { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> state.eventSink(LoginRootEvents.RefreshHomeServer)
else -> Unit
}
}
LoginRootScreen(
state = state,
onChangeServer = this::onChangeHomeServer,
onLoginChanged = this::onLoginChanged,
onPasswordChanged = this::onPasswordChanged,
onSubmitClicked = this::onSubmit
)
}
}

View file

@ -5,6 +5,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.x.architecture.Presenter
import io.element.android.x.matrix.Matrix
@ -13,10 +14,11 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import javax.inject.Inject
class LoginRootPresenter @Inject constructor(private val matrix: Matrix) : Presenter<LoginRootState, LoginRootEvents> {
class LoginRootPresenter @Inject constructor(private val matrix: Matrix) : Presenter<LoginRootState> {
@Composable
override fun present(events: Flow<LoginRootEvents>): LoginRootState {
override fun present(): LoginRootState {
val localCoroutineScope = rememberCoroutineScope()
val homeserver = rememberSaveable {
mutableStateOf(matrix.getHomeserverOrDefault())
}
@ -27,24 +29,24 @@ class LoginRootPresenter @Inject constructor(private val matrix: Matrix) : Prese
mutableStateOf(LoginFormState.Default)
}
LaunchedEffect(Unit) {
events.collect { event ->
when (event) {
LoginRootEvents.RefreshHomeServer -> refreshHomeServer(homeserver)
is LoginRootEvents.SetLogin -> updateFormState(formState) {
copy(login = event.login)
}
is LoginRootEvents.SetPassword -> updateFormState(formState) {
copy(password = event.password)
}
LoginRootEvents.Submit -> submit(homeserver.value, formState.value, loggedInState)
fun handleEvents(event: LoginRootEvents){
when (event) {
LoginRootEvents.RefreshHomeServer -> refreshHomeServer(homeserver)
is LoginRootEvents.SetLogin -> updateFormState(formState) {
copy(login = event.login)
}
is LoginRootEvents.SetPassword -> updateFormState(formState) {
copy(password = event.password)
}
LoginRootEvents.Submit -> localCoroutineScope.submit(homeserver.value, formState.value, loggedInState)
}
}
return LoginRootState(
homeserver = homeserver.value,
loggedInState = loggedInState.value,
formState = formState.value
formState = formState.value,
eventSink = ::handleEvents
)
}

View file

@ -68,11 +68,9 @@ fun LoginRootScreen(
state: LoginRootState,
modifier: Modifier = Modifier,
onChangeServer: () -> Unit = {},
onLoginChanged: (String) -> Unit = {},
onPasswordChanged: (String) -> Unit = {},
onSubmitClicked: () -> Unit = {},
onLoginWithSuccess: (SessionId) -> Unit = {},
) {
val eventSink = state.eventSink
Surface(
modifier = modifier,
color = MaterialTheme.colorScheme.background,
@ -144,7 +142,7 @@ fun LoginRootScreen(
},
onValueChange = {
loginFieldState = it
onLoginChanged(it)
eventSink(LoginRootEvents.SetLogin(it))
},
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Email,
@ -163,7 +161,7 @@ fun LoginRootScreen(
.padding(top = 24.dp),
onValueChange = {
passwordFieldState = it
onPasswordChanged(it)
eventSink(LoginRootEvents.SetPassword(it))
},
label = {
Text(text = "Password")
@ -185,7 +183,7 @@ fun LoginRootScreen(
imeAction = ImeAction.Done,
),
keyboardActions = KeyboardActions(
onDone = { onSubmitClicked() }
onDone = { eventSink(LoginRootEvents.Submit) }
),
)
if (state.loggedInState is LoggedInState.ErrorLoggingIn) {
@ -199,7 +197,7 @@ fun LoginRootScreen(
}
// Submit
Button(
onClick = onSubmitClicked,
onClick = { eventSink(LoginRootEvents.Submit) },
enabled = state.submitEnabled,
modifier = Modifier
.fillMaxWidth()

View file

@ -8,6 +8,7 @@ data class LoginRootState(
val homeserver: String = "",
val loggedInState: LoggedInState = LoggedInState.NotLoggedIn,
val formState: LoginFormState = LoginFormState.Default,
val eventSink: (LoginRootEvents) -> Unit = {}
) {
val submitEnabled =
formState.login.isNotEmpty() && formState.password.isNotEmpty() && loggedInState != LoggedInState.LoggingIn