Start migrating Anvil KSP to Metro

This commit is contained in:
Jorge Martín 2025-08-20 15:29:50 +02:00
parent d4d57b1e21
commit b76a71ebf5
703 changed files with 3523 additions and 2820 deletions

View file

@ -10,14 +10,15 @@ package io.element.android.features.verifysession.impl.incoming
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.squareup.anvil.annotations.ContributesBinding
import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.AppScope
import javax.inject.Inject
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
@ContributesBinding(AppScope::class)
class DefaultIncomingVerificationEntryPoint @Inject constructor() : IncomingVerificationEntryPoint {
@Inject
class DefaultIncomingVerificationEntryPoint() : IncomingVerificationEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): IncomingVerificationEntryPoint.NodeBuilder {
val plugins = ArrayList<Plugin>()

View file

@ -13,15 +13,16 @@ import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.Inject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.SessionScope
@ContributesNode(SessionScope::class)
class IncomingVerificationNode @AssistedInject constructor(
@Inject
class IncomingVerificationNode(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
presenterFactory: IncomingVerificationPresenter.Factory,

View file

@ -17,9 +17,9 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import com.freeletics.flowredux.compose.rememberStateAndDispatch
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.Inject
import io.element.android.features.verifysession.impl.incoming.IncomingVerificationState.Step
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.dateformatter.api.DateFormatter
@ -38,7 +38,8 @@ import timber.log.Timber
import io.element.android.features.verifysession.impl.incoming.IncomingVerificationStateMachine.Event as StateMachineEvent
import io.element.android.features.verifysession.impl.incoming.IncomingVerificationStateMachine.State as StateMachineState
class IncomingVerificationPresenter @AssistedInject constructor(
@Inject
class IncomingVerificationPresenter(
@Assisted private val verificationRequest: VerificationRequest.Incoming,
@Assisted private val navigator: IncomingVerificationNavigator,
@SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope,

View file

@ -15,10 +15,11 @@ import io.element.android.features.verifysession.impl.util.logReceivedEvents
import io.element.android.libraries.matrix.api.verification.SessionVerificationData
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import kotlinx.coroutines.ExperimentalCoroutinesApi
import javax.inject.Inject
import dev.zacsweers.metro.Inject
import com.freeletics.flowredux.dsl.State as MachineState
class IncomingVerificationStateMachine @Inject constructor(
@Inject
class IncomingVerificationStateMachine(
private val sessionVerificationService: SessionVerificationService,
) : FlowReduxStateMachine<IncomingVerificationStateMachine.State, IncomingVerificationStateMachine.Event>(
initialState = State.Initial(isCancelled = false)

View file

@ -10,14 +10,15 @@ package io.element.android.features.verifysession.impl.outgoing
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.squareup.anvil.annotations.ContributesBinding
import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.AppScope
import javax.inject.Inject
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
@ContributesBinding(AppScope::class)
class DefaultOutgoingVerificationEntryPoint @Inject constructor() : OutgoingVerificationEntryPoint {
@Inject
class DefaultOutgoingVerificationEntryPoint() : OutgoingVerificationEntryPoint {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): OutgoingVerificationEntryPoint.NodeBuilder {
val plugins = ArrayList<Plugin>()

View file

@ -13,37 +13,38 @@ import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.Inject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.SessionScope
@ContributesNode(SessionScope::class)
class OutgoingVerificationNode @AssistedInject constructor(
@Inject
class OutgoingVerificationNode(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
presenterFactory: OutgoingVerificationPresenter.Factory,
// presenterFactory: OutgoingVerificationPresenter.Factory,
) : Node(buildContext, plugins = plugins) {
private val callback = plugins<OutgoingVerificationEntryPoint.Callback>().first()
private val inputs = inputs<OutgoingVerificationEntryPoint.Params>()
private val presenter = presenterFactory.create(
showDeviceVerifiedScreen = inputs.showDeviceVerifiedScreen,
verificationRequest = inputs.verificationRequest,
)
// private val presenter = presenterFactory.create(
// showDeviceVerifiedScreen = inputs.showDeviceVerifiedScreen,
// verificationRequest = inputs.verificationRequest,
// )
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()
OutgoingVerificationView(
state = state,
modifier = modifier,
onLearnMoreClick = callback::onLearnMoreAboutEncryption,
onFinish = callback::onDone,
onBack = callback::onBack,
)
// val state = presenter.present()
// OutgoingVerificationView(
// state = state,
// modifier = modifier,
// onLearnMoreClick = callback::onLearnMoreAboutEncryption,
// onFinish = callback::onDone,
// onBack = callback::onBack,
// )
}
}

View file

@ -16,9 +16,9 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import com.freeletics.flowredux.compose.rememberStateAndDispatch
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.Inject
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.matrix.api.encryption.EncryptionService
@ -34,146 +34,147 @@ import timber.log.Timber
import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificationStateMachine.Event as StateMachineEvent
import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificationStateMachine.State as StateMachineState
class OutgoingVerificationPresenter @AssistedInject constructor(
@Assisted private val showDeviceVerifiedScreen: Boolean,
@Assisted private val verificationRequest: VerificationRequest.Outgoing,
private val sessionVerificationService: SessionVerificationService,
private val encryptionService: EncryptionService,
) : Presenter<OutgoingVerificationState> {
@AssistedFactory
interface Factory {
fun create(
verificationRequest: VerificationRequest.Outgoing,
showDeviceVerifiedScreen: Boolean,
): OutgoingVerificationPresenter
}
private val stateMachine = OutgoingVerificationStateMachine(
sessionVerificationService = sessionVerificationService,
encryptionService = encryptionService,
)
@Composable
override fun present(): OutgoingVerificationState {
val stateAndDispatch = stateMachine.rememberStateAndDispatch()
val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState()
val step by remember {
derivedStateOf {
when (verificationRequest) {
is VerificationRequest.Outgoing.CurrentSession -> {
when (sessionVerifiedStatus) {
SessionVerifiedStatus.Unknown -> OutgoingVerificationState.Step.Loading
SessionVerifiedStatus.NotVerified -> {
stateAndDispatch.state.value.toVerificationStep()
}
SessionVerifiedStatus.Verified -> {
if (stateAndDispatch.state.value != StateMachineState.Initial || showDeviceVerifiedScreen) {
// The user has verified the session, we need to show the success screen
OutgoingVerificationState.Step.Completed
} else {
// Automatic verification, which can happen on freshly created account, in this case, skip the screen
OutgoingVerificationState.Step.Exit
}
}
}
}
is VerificationRequest.Outgoing.User -> stateAndDispatch.state.value.toVerificationStep()
}
}
}
// Start this after observing state machine
LaunchedEffect(Unit) {
// Force reset, just in case the service was left in a broken state
sessionVerificationService.reset(cancelAnyPendingVerificationAttempt = true)
observeVerificationService()
}
fun handleEvents(event: OutgoingVerificationViewEvents) {
Timber.d("Verification user action: ${event::class.simpleName}")
when (event) {
// Just relay the event to the state machine
OutgoingVerificationViewEvents.RequestVerification -> StateMachineEvent.RequestVerification(verificationRequest)
OutgoingVerificationViewEvents.StartSasVerification -> StateMachineEvent.StartSasVerification
OutgoingVerificationViewEvents.ConfirmVerification -> StateMachineEvent.AcceptChallenge
OutgoingVerificationViewEvents.DeclineVerification -> StateMachineEvent.DeclineChallenge
OutgoingVerificationViewEvents.Cancel -> StateMachineEvent.Cancel
OutgoingVerificationViewEvents.Reset -> StateMachineEvent.Reset
}.let { stateMachineEvent ->
stateAndDispatch.dispatchAction(stateMachineEvent)
}
}
return OutgoingVerificationState(
step = step,
request = verificationRequest,
eventSink = ::handleEvents,
)
}
private fun StateMachineState?.toVerificationStep(): OutgoingVerificationState.Step =
when (val machineState = this) {
StateMachineState.Initial, null -> {
OutgoingVerificationState.Step.Initial
}
is StateMachineState.RequestingVerification,
is StateMachineState.StartingSasVerification,
StateMachineState.SasVerificationStarted -> {
OutgoingVerificationState.Step.AwaitingOtherDeviceResponse
}
StateMachineState.VerificationRequestAccepted -> {
OutgoingVerificationState.Step.Ready
}
is StateMachineState.Canceled -> {
OutgoingVerificationState.Step.Canceled
}
is StateMachineState.Verifying -> {
val async = when (machineState) {
is StateMachineState.Verifying.Replying -> AsyncData.Loading()
else -> AsyncData.Uninitialized
}
OutgoingVerificationState.Step.Verifying(machineState.data, async)
}
StateMachineState.Completed -> {
OutgoingVerificationState.Step.Completed
}
StateMachineState.Exit -> {
OutgoingVerificationState.Step.Exit
}
}
private fun CoroutineScope.observeVerificationService() {
sessionVerificationService.verificationFlowState
.onEach { Timber.d("Verification flow state: ${it::class.simpleName}") }
.onEach { verificationAttemptState ->
when (verificationAttemptState) {
VerificationFlowState.Initial -> stateMachine.dispatch(StateMachineEvent.Reset)
VerificationFlowState.DidAcceptVerificationRequest -> {
stateMachine.dispatch(StateMachineEvent.DidAcceptVerificationRequest)
}
VerificationFlowState.DidStartSasVerification -> {
stateMachine.dispatch(StateMachineEvent.DidStartSasVerification)
}
is VerificationFlowState.DidReceiveVerificationData -> {
stateMachine.dispatch(StateMachineEvent.DidReceiveChallenge(verificationAttemptState.data))
}
VerificationFlowState.DidFinish -> {
stateMachine.dispatch(StateMachineEvent.DidAcceptChallenge)
}
VerificationFlowState.DidCancel -> {
stateMachine.dispatch(StateMachineEvent.DidCancel)
}
VerificationFlowState.DidFail -> {
stateMachine.dispatch(StateMachineEvent.DidFail)
}
}
}
.launchIn(this)
}
}
//@Inject
//class OutgoingVerificationPresenter(
// @Assisted private val showDeviceVerifiedScreen: Boolean,
// @Assisted private val verificationRequest: VerificationRequest.Outgoing,
// private val sessionVerificationService: SessionVerificationService,
// private val encryptionService: EncryptionService,
//) : Presenter<OutgoingVerificationState> {
// @AssistedFactory
// interface Factory {
// fun create(
// verificationRequest: VerificationRequest.Outgoing,
// showDeviceVerifiedScreen: Boolean,
// ): OutgoingVerificationPresenter
// }
//
// private val stateMachine = OutgoingVerificationStateMachine(
// sessionVerificationService = sessionVerificationService,
// encryptionService = encryptionService,
// )
//
// @Composable
// override fun present(): OutgoingVerificationState {
// val stateAndDispatch = stateMachine.rememberStateAndDispatch()
//
// val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState()
// val step by remember {
// derivedStateOf {
// when (verificationRequest) {
// is VerificationRequest.Outgoing.CurrentSession -> {
// when (sessionVerifiedStatus) {
// SessionVerifiedStatus.Unknown -> OutgoingVerificationState.Step.Loading
// SessionVerifiedStatus.NotVerified -> {
// stateAndDispatch.state.value.toVerificationStep()
// }
// SessionVerifiedStatus.Verified -> {
// if (stateAndDispatch.state.value != StateMachineState.Initial || showDeviceVerifiedScreen) {
// // The user has verified the session, we need to show the success screen
// OutgoingVerificationState.Step.Completed
// } else {
// // Automatic verification, which can happen on freshly created account, in this case, skip the screen
// OutgoingVerificationState.Step.Exit
// }
// }
// }
// }
// is VerificationRequest.Outgoing.User -> stateAndDispatch.state.value.toVerificationStep()
// }
// }
// }
//
// // Start this after observing state machine
// LaunchedEffect(Unit) {
// // Force reset, just in case the service was left in a broken state
// sessionVerificationService.reset(cancelAnyPendingVerificationAttempt = true)
//
// observeVerificationService()
// }
//
// fun handleEvents(event: OutgoingVerificationViewEvents) {
// Timber.d("Verification user action: ${event::class.simpleName}")
// when (event) {
// // Just relay the event to the state machine
// OutgoingVerificationViewEvents.RequestVerification -> StateMachineEvent.RequestVerification(verificationRequest)
// OutgoingVerificationViewEvents.StartSasVerification -> StateMachineEvent.StartSasVerification
// OutgoingVerificationViewEvents.ConfirmVerification -> StateMachineEvent.AcceptChallenge
// OutgoingVerificationViewEvents.DeclineVerification -> StateMachineEvent.DeclineChallenge
// OutgoingVerificationViewEvents.Cancel -> StateMachineEvent.Cancel
// OutgoingVerificationViewEvents.Reset -> StateMachineEvent.Reset
// }.let { stateMachineEvent ->
// stateAndDispatch.dispatchAction(stateMachineEvent)
// }
// }
// return OutgoingVerificationState(
// step = step,
// request = verificationRequest,
// eventSink = ::handleEvents,
// )
// }
//
// private fun StateMachineState?.toVerificationStep(): OutgoingVerificationState.Step =
// when (val machineState = this) {
// StateMachineState.Initial, null -> {
// OutgoingVerificationState.Step.Initial
// }
// is StateMachineState.RequestingVerification,
// is StateMachineState.StartingSasVerification,
// StateMachineState.SasVerificationStarted -> {
// OutgoingVerificationState.Step.AwaitingOtherDeviceResponse
// }
//
// StateMachineState.VerificationRequestAccepted -> {
// OutgoingVerificationState.Step.Ready
// }
//
// is StateMachineState.Canceled -> {
// OutgoingVerificationState.Step.Canceled
// }
//
// is StateMachineState.Verifying -> {
// val async = when (machineState) {
// is StateMachineState.Verifying.Replying -> AsyncData.Loading()
// else -> AsyncData.Uninitialized
// }
// OutgoingVerificationState.Step.Verifying(machineState.data, async)
// }
//
// StateMachineState.Completed -> {
// OutgoingVerificationState.Step.Completed
// }
//
// StateMachineState.Exit -> {
// OutgoingVerificationState.Step.Exit
// }
// }
//
// private fun CoroutineScope.observeVerificationService() {
// sessionVerificationService.verificationFlowState
// .onEach { Timber.d("Verification flow state: ${it::class.simpleName}") }
// .onEach { verificationAttemptState ->
// when (verificationAttemptState) {
// VerificationFlowState.Initial -> stateMachine.dispatch(StateMachineEvent.Reset)
// VerificationFlowState.DidAcceptVerificationRequest -> {
// stateMachine.dispatch(StateMachineEvent.DidAcceptVerificationRequest)
// }
// VerificationFlowState.DidStartSasVerification -> {
// stateMachine.dispatch(StateMachineEvent.DidStartSasVerification)
// }
// is VerificationFlowState.DidReceiveVerificationData -> {
// stateMachine.dispatch(StateMachineEvent.DidReceiveChallenge(verificationAttemptState.data))
// }
// VerificationFlowState.DidFinish -> {
// stateMachine.dispatch(StateMachineEvent.DidAcceptChallenge)
// }
// VerificationFlowState.DidCancel -> {
// stateMachine.dispatch(StateMachineEvent.DidCancel)
// }
// VerificationFlowState.DidFail -> {
// stateMachine.dispatch(StateMachineEvent.DidFail)
// }
// }
// }
// .launchIn(this)
// }
//}