Merge branch 'develop' into feature/fga/space_list_join_action
This commit is contained in:
commit
0390fde615
591 changed files with 6155 additions and 2140 deletions
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="2.2.10" />
|
<option name="version" value="2.2.20" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -8,6 +8,6 @@ appId: ${MAESTRO_APP_ID}
|
||||||
- hideKeyboard
|
- hideKeyboard
|
||||||
- tapOn: "Continue"
|
- tapOn: "Continue"
|
||||||
- extendedWaitUntil:
|
- extendedWaitUntil:
|
||||||
visible: "Verification complete"
|
visible: "Device verified"
|
||||||
timeout: 30000
|
timeout: 30000
|
||||||
- tapOn: "Continue"
|
- tapOn: "Continue"
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ import io.element.android.x.BuildConfig
|
||||||
import io.element.android.x.R
|
import io.element.android.x.R
|
||||||
import kotlinx.coroutines.CoroutineName
|
import kotlinx.coroutines.CoroutineName
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.MainScope
|
import kotlinx.coroutines.MainScope
|
||||||
import kotlinx.coroutines.plus
|
import kotlinx.coroutines.plus
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
@ -107,11 +106,7 @@ object AppModule {
|
||||||
@Provides
|
@Provides
|
||||||
@SingleIn(AppScope::class)
|
@SingleIn(AppScope::class)
|
||||||
fun providesCoroutineDispatchers(): CoroutineDispatchers {
|
fun providesCoroutineDispatchers(): CoroutineDispatchers {
|
||||||
return CoroutineDispatchers(
|
return CoroutineDispatchers.Default
|
||||||
io = Dispatchers.IO,
|
|
||||||
computation = Dispatchers.Default,
|
|
||||||
main = Dispatchers.Main,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,11 @@ dependencies {
|
||||||
allFeaturesApi(project)
|
allFeaturesApi(project)
|
||||||
|
|
||||||
implementation(projects.libraries.core)
|
implementation(projects.libraries.core)
|
||||||
|
implementation(projects.libraries.accountselect.api)
|
||||||
implementation(projects.libraries.androidutils)
|
implementation(projects.libraries.androidutils)
|
||||||
implementation(projects.libraries.architecture)
|
implementation(projects.libraries.architecture)
|
||||||
implementation(projects.libraries.deeplink.api)
|
implementation(projects.libraries.deeplink.api)
|
||||||
|
implementation(projects.libraries.featureflag.api)
|
||||||
implementation(projects.libraries.matrix.api)
|
implementation(projects.libraries.matrix.api)
|
||||||
implementation(projects.libraries.oidc.api)
|
implementation(projects.libraries.oidc.api)
|
||||||
implementation(projects.libraries.preferences.api)
|
implementation(projects.libraries.preferences.api)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.appnav.di.SessionGraphFactory
|
import io.element.android.appnav.di.SessionGraphFactory
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
|
|
@ -41,7 +41,7 @@ import kotlinx.parcelize.Parcelize
|
||||||
* This allow to inject objects with SessionScope in the constructor of [LoggedInFlowNode].
|
* This allow to inject objects with SessionScope in the constructor of [LoggedInFlowNode].
|
||||||
*/
|
*/
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class LoggedInAppScopeFlowNode(
|
class LoggedInAppScopeFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
@ -57,6 +57,7 @@ class LoggedInAppScopeFlowNode(
|
||||||
), DependencyInjectionGraphOwner {
|
), DependencyInjectionGraphOwner {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onOpenBugReport()
|
fun onOpenBugReport()
|
||||||
|
fun onAddAccount()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
|
@ -83,6 +84,10 @@ class LoggedInAppScopeFlowNode(
|
||||||
override fun onOpenBugReport() {
|
override fun onOpenBugReport() {
|
||||||
plugins<Callback>().forEach { it.onOpenBugReport() }
|
plugins<Callback>().forEach { it.onOpenBugReport() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onAddAccount() {
|
||||||
|
plugins<Callback>().forEach { it.onAddAccount() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return createNode<LoggedInFlowNode>(buildContext, listOf(callback))
|
return createNode<LoggedInFlowNode>(buildContext, listOf(callback))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,17 @@ class LoggedInEventProcessor(
|
||||||
observingJob = roomMembershipObserver.updates
|
observingJob = roomMembershipObserver.updates
|
||||||
.filter { !it.isUserInRoom }
|
.filter { !it.isUserInRoom }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.onEach {
|
.onEach { roomMemberShipUpdate ->
|
||||||
when (it.change) {
|
when (roomMemberShipUpdate.change) {
|
||||||
MembershipChange.LEFT -> displayMessage(CommonStrings.common_current_user_left_room)
|
MembershipChange.LEFT -> {
|
||||||
|
displayMessage(
|
||||||
|
if (roomMemberShipUpdate.isSpace) {
|
||||||
|
CommonStrings.common_current_user_left_space
|
||||||
|
} else {
|
||||||
|
CommonStrings.common_current_user_left_room
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
MembershipChange.INVITATION_REJECTED -> displayMessage(CommonStrings.common_current_user_rejected_invite)
|
MembershipChange.INVITATION_REJECTED -> displayMessage(CommonStrings.common_current_user_rejected_invite)
|
||||||
MembershipChange.KNOCK_RETRACTED -> displayMessage(CommonStrings.common_current_user_canceled_knock)
|
MembershipChange.KNOCK_RETRACTED -> displayMessage(CommonStrings.common_current_user_canceled_knock)
|
||||||
else -> Unit
|
else -> Unit
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import com.bumble.appyx.navmodel.backstack.operation.replace
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.singleTop
|
import com.bumble.appyx.navmodel.backstack.operation.singleTop
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
|
|
@ -75,7 +76,6 @@ import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.MAIN_SPACE
|
import io.element.android.libraries.matrix.api.core.MAIN_SPACE
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||||
|
|
@ -100,7 +100,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlin.time.toKotlinDuration
|
import kotlin.time.toKotlinDuration
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class LoggedInFlowNode(
|
class LoggedInFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
@ -138,6 +138,7 @@ class LoggedInFlowNode(
|
||||||
) {
|
) {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onOpenBugReport()
|
fun onOpenBugReport()
|
||||||
|
fun onAddAccount()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val loggedInFlowProcessor = LoggedInEventProcessor(
|
private val loggedInFlowProcessor = LoggedInEventProcessor(
|
||||||
|
|
@ -392,6 +393,10 @@ class LoggedInFlowNode(
|
||||||
}
|
}
|
||||||
is NavTarget.Settings -> {
|
is NavTarget.Settings -> {
|
||||||
val callback = object : PreferencesEntryPoint.Callback {
|
val callback = object : PreferencesEntryPoint.Callback {
|
||||||
|
override fun onAddAccount() {
|
||||||
|
plugins<Callback>().forEach { it.onAddAccount() }
|
||||||
|
}
|
||||||
|
|
||||||
override fun onOpenBugReport() {
|
override fun onOpenBugReport() {
|
||||||
plugins<Callback>().forEach { it.onOpenBugReport() }
|
plugins<Callback>().forEach { it.onOpenBugReport() }
|
||||||
}
|
}
|
||||||
|
|
@ -404,11 +409,7 @@ class LoggedInFlowNode(
|
||||||
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.NotificationSettings))
|
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.NotificationSettings))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun navigateTo(sessionId: SessionId, roomId: RoomId, eventId: EventId) {
|
override fun navigateTo(roomId: RoomId, eventId: EventId) {
|
||||||
// We do not check the sessionId, but it will have to be done at some point (multi account)
|
|
||||||
if (sessionId != matrixClient.sessionId) {
|
|
||||||
Timber.e("SessionId mismatch, expected ${matrixClient.sessionId} but got $sessionId")
|
|
||||||
}
|
|
||||||
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Messages(eventId)))
|
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Messages(eventId)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import com.bumble.appyx.core.plugin.plugins
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.login.api.LoginEntryPoint
|
import io.element.android.features.login.api.LoginEntryPoint
|
||||||
import io.element.android.features.login.api.LoginParams
|
import io.element.android.features.login.api.LoginParams
|
||||||
|
|
@ -36,7 +36,7 @@ import io.element.android.libraries.matrix.ui.media.NotLoggedInImageLoaderFactor
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class NotLoggedInFlowNode(
|
class NotLoggedInFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ package io.element.android.appnav
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import androidx.compose.animation.core.Spring
|
||||||
|
import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -23,9 +25,11 @@ import com.bumble.appyx.core.state.MutableSavedStateMap
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.pop
|
import com.bumble.appyx.navmodel.backstack.operation.pop
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
|
import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackFader
|
||||||
|
import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackSlider
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.appnav.di.MatrixSessionCache
|
import io.element.android.appnav.di.MatrixSessionCache
|
||||||
|
|
@ -39,13 +43,17 @@ import io.element.android.features.login.api.accesscontrol.AccountProviderAccess
|
||||||
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
|
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
|
||||||
import io.element.android.features.rageshake.api.reporter.BugReporter
|
import io.element.android.features.rageshake.api.reporter.BugReporter
|
||||||
import io.element.android.features.signedout.api.SignedOutEntryPoint
|
import io.element.android.features.signedout.api.SignedOutEntryPoint
|
||||||
|
import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint
|
||||||
import io.element.android.libraries.architecture.BackstackView
|
import io.element.android.libraries.architecture.BackstackView
|
||||||
import io.element.android.libraries.architecture.BaseFlowNode
|
import io.element.android.libraries.architecture.BaseFlowNode
|
||||||
|
import io.element.android.libraries.architecture.appyx.rememberDelegateTransitionHandler
|
||||||
import io.element.android.libraries.architecture.createNode
|
import io.element.android.libraries.architecture.createNode
|
||||||
import io.element.android.libraries.architecture.waitForChildAttached
|
import io.element.android.libraries.architecture.waitForChildAttached
|
||||||
import io.element.android.libraries.core.uri.ensureProtocol
|
import io.element.android.libraries.core.uri.ensureProtocol
|
||||||
import io.element.android.libraries.deeplink.api.DeeplinkData
|
import io.element.android.libraries.deeplink.api.DeeplinkData
|
||||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||||
|
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||||
|
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||||
|
|
@ -56,12 +64,11 @@ import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class) @AssistedInject class RootFlowNode(
|
||||||
@Inject
|
|
||||||
class RootFlowNode(
|
|
||||||
@Assisted val buildContext: BuildContext,
|
@Assisted val buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
private val sessionStore: SessionStore,
|
private val sessionStore: SessionStore,
|
||||||
|
|
@ -71,9 +78,11 @@ class RootFlowNode(
|
||||||
private val presenter: RootPresenter,
|
private val presenter: RootPresenter,
|
||||||
private val bugReportEntryPoint: BugReportEntryPoint,
|
private val bugReportEntryPoint: BugReportEntryPoint,
|
||||||
private val signedOutEntryPoint: SignedOutEntryPoint,
|
private val signedOutEntryPoint: SignedOutEntryPoint,
|
||||||
|
private val accountSelectEntryPoint: AccountSelectEntryPoint,
|
||||||
private val intentResolver: IntentResolver,
|
private val intentResolver: IntentResolver,
|
||||||
private val oidcActionFlow: OidcActionFlow,
|
private val oidcActionFlow: OidcActionFlow,
|
||||||
private val bugReporter: BugReporter,
|
private val bugReporter: BugReporter,
|
||||||
|
private val featureFlagService: FeatureFlagService,
|
||||||
) : BaseFlowNode<RootFlowNode.NavTarget>(
|
) : BaseFlowNode<RootFlowNode.NavTarget>(
|
||||||
backstack = BackStack(
|
backstack = BackStack(
|
||||||
initialElement = NavTarget.SplashScreen,
|
initialElement = NavTarget.SplashScreen,
|
||||||
|
|
@ -95,27 +104,24 @@ class RootFlowNode(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeNavState() {
|
private fun observeNavState() {
|
||||||
navStateFlowFactory.create(buildContext.savedStateMap)
|
navStateFlowFactory.create(buildContext.savedStateMap).distinctUntilChanged().onEach { navState ->
|
||||||
.distinctUntilChanged()
|
Timber.v("navState=$navState")
|
||||||
.onEach { navState ->
|
when (navState.loggedInState) {
|
||||||
Timber.v("navState=$navState")
|
is LoggedInState.LoggedIn -> {
|
||||||
when (navState.loggedInState) {
|
if (navState.loggedInState.isTokenValid) {
|
||||||
is LoggedInState.LoggedIn -> {
|
tryToRestoreLatestSession(
|
||||||
if (navState.loggedInState.isTokenValid) {
|
onSuccess = { sessionId -> switchToLoggedInFlow(sessionId, navState.cacheIndex) },
|
||||||
tryToRestoreLatestSession(
|
onFailure = { switchToNotLoggedInFlow(null) }
|
||||||
onSuccess = { sessionId -> switchToLoggedInFlow(sessionId, navState.cacheIndex) },
|
)
|
||||||
onFailure = { switchToNotLoggedInFlow(null) }
|
} else {
|
||||||
)
|
switchToSignedOutFlow(SessionId(navState.loggedInState.sessionId))
|
||||||
} else {
|
|
||||||
switchToSignedOutFlow(SessionId(navState.loggedInState.sessionId))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LoggedInState.NotLoggedIn -> {
|
|
||||||
switchToNotLoggedInFlow(null)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LoggedInState.NotLoggedIn -> {
|
||||||
|
switchToNotLoggedInFlow(null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.launchIn(lifecycleScope)
|
}.launchIn(lifecycleScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun switchToLoggedInFlow(sessionId: SessionId, navId: Int) {
|
private fun switchToLoggedInFlow(sessionId: SessionId, navId: Int) {
|
||||||
|
|
@ -137,20 +143,17 @@ class RootFlowNode(
|
||||||
onFailure: () -> Unit,
|
onFailure: () -> Unit,
|
||||||
onSuccess: (SessionId) -> Unit,
|
onSuccess: (SessionId) -> Unit,
|
||||||
) {
|
) {
|
||||||
matrixSessionCache.getOrRestore(sessionId)
|
matrixSessionCache.getOrRestore(sessionId).onSuccess {
|
||||||
.onSuccess {
|
Timber.v("Succeed to restore session $sessionId")
|
||||||
Timber.v("Succeed to restore session $sessionId")
|
onSuccess(sessionId)
|
||||||
onSuccess(sessionId)
|
}.onFailure {
|
||||||
}
|
Timber.e(it, "Failed to restore session $sessionId")
|
||||||
.onFailure {
|
onFailure()
|
||||||
Timber.e(it, "Failed to restore session $sessionId")
|
}
|
||||||
onFailure()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun tryToRestoreLatestSession(
|
private suspend fun tryToRestoreLatestSession(
|
||||||
onSuccess: (SessionId) -> Unit,
|
onSuccess: (SessionId) -> Unit, onFailure: () -> Unit
|
||||||
onFailure: () -> Unit
|
|
||||||
) {
|
) {
|
||||||
val latestSessionId = sessionStore.getLatestSessionId()
|
val latestSessionId = sessionStore.getLatestSessionId()
|
||||||
if (latestSessionId == null) {
|
if (latestSessionId == null) {
|
||||||
|
|
@ -172,32 +175,45 @@ class RootFlowNode(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onOpenBugReport = this::onOpenBugReport,
|
onOpenBugReport = this::onOpenBugReport,
|
||||||
) {
|
) {
|
||||||
BackstackView()
|
val backstackSlider = rememberBackstackSlider<NavTarget>(
|
||||||
|
transitionSpec = { spring(stiffness = Spring.StiffnessMediumLow) },
|
||||||
|
)
|
||||||
|
val backstackFader = rememberBackstackFader<NavTarget>(
|
||||||
|
transitionSpec = { spring(stiffness = Spring.StiffnessMediumLow) },
|
||||||
|
)
|
||||||
|
val transitionHandler = rememberDelegateTransitionHandler<NavTarget, BackStack.State> { navTarget ->
|
||||||
|
when (navTarget) {
|
||||||
|
is NavTarget.SplashScreen,
|
||||||
|
is NavTarget.LoggedInFlow -> backstackFader
|
||||||
|
else -> backstackSlider
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BackstackView(transitionHandler = transitionHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface NavTarget : Parcelable {
|
sealed interface NavTarget : Parcelable {
|
||||||
@Parcelize
|
@Parcelize data object SplashScreen : NavTarget
|
||||||
data object SplashScreen : NavTarget
|
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize data class AccountSelect(
|
||||||
data class NotLoggedInFlow(
|
val currentSessionId: SessionId,
|
||||||
|
val intent: Intent?,
|
||||||
|
val permalinkData: PermalinkData?,
|
||||||
|
) : NavTarget
|
||||||
|
|
||||||
|
@Parcelize data class NotLoggedInFlow(
|
||||||
val params: LoginParams?
|
val params: LoginParams?
|
||||||
) : NavTarget
|
) : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize data class LoggedInFlow(
|
||||||
data class LoggedInFlow(
|
val sessionId: SessionId, val navId: Int
|
||||||
val sessionId: SessionId,
|
|
||||||
val navId: Int
|
|
||||||
) : NavTarget
|
) : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize data class SignedOutFlow(
|
||||||
data class SignedOutFlow(
|
|
||||||
val sessionId: SessionId
|
val sessionId: SessionId
|
||||||
) : NavTarget
|
) : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize data object BugReport : NavTarget
|
||||||
data object BugReport : NavTarget
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
|
|
@ -211,6 +227,10 @@ class RootFlowNode(
|
||||||
override fun onOpenBugReport() {
|
override fun onOpenBugReport() {
|
||||||
backstack.push(NavTarget.BugReport)
|
backstack.push(NavTarget.BugReport)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onAddAccount() {
|
||||||
|
backstack.push(NavTarget.NotLoggedInFlow(null))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
createNode<LoggedInAppScopeFlowNode>(buildContext, plugins = listOf(inputs, callback))
|
createNode<LoggedInAppScopeFlowNode>(buildContext, plugins = listOf(inputs, callback))
|
||||||
}
|
}
|
||||||
|
|
@ -226,13 +246,11 @@ class RootFlowNode(
|
||||||
createNode<NotLoggedInFlowNode>(buildContext, plugins = listOf(params, callback))
|
createNode<NotLoggedInFlowNode>(buildContext, plugins = listOf(params, callback))
|
||||||
}
|
}
|
||||||
is NavTarget.SignedOutFlow -> {
|
is NavTarget.SignedOutFlow -> {
|
||||||
signedOutEntryPoint.nodeBuilder(this, buildContext)
|
signedOutEntryPoint.nodeBuilder(this, buildContext).params(
|
||||||
.params(
|
SignedOutEntryPoint.Params(
|
||||||
SignedOutEntryPoint.Params(
|
sessionId = navTarget.sessionId
|
||||||
sessionId = navTarget.sessionId
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.build()
|
).build()
|
||||||
}
|
}
|
||||||
NavTarget.SplashScreen -> splashNode(buildContext)
|
NavTarget.SplashScreen -> splashNode(buildContext)
|
||||||
NavTarget.BugReport -> {
|
NavTarget.BugReport -> {
|
||||||
|
|
@ -241,10 +259,32 @@ class RootFlowNode(
|
||||||
backstack.pop()
|
backstack.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bugReportEntryPoint
|
bugReportEntryPoint.nodeBuilder(this, buildContext).callback(callback).build()
|
||||||
.nodeBuilder(this, buildContext)
|
}
|
||||||
.callback(callback)
|
is NavTarget.AccountSelect -> {
|
||||||
.build()
|
val callback: AccountSelectEntryPoint.Callback = object : AccountSelectEntryPoint.Callback {
|
||||||
|
override fun onSelectAccount(sessionId: SessionId) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
if (sessionId == navTarget.currentSessionId) {
|
||||||
|
// Ensure that the account selection Node is removed from the backstack
|
||||||
|
// Do not pop when the account is changed to avoid a UI flicker.
|
||||||
|
backstack.pop()
|
||||||
|
}
|
||||||
|
attachSession(sessionId).apply {
|
||||||
|
if (navTarget.intent != null) {
|
||||||
|
attachIncomingShare(navTarget.intent)
|
||||||
|
} else if (navTarget.permalinkData != null) {
|
||||||
|
attachPermalinkData(navTarget.permalinkData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCancel() {
|
||||||
|
backstack.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accountSelectEntryPoint.nodeBuilder(this, buildContext).callback(callback).build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -267,19 +307,29 @@ class RootFlowNode(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun onLoginLink(params: LoginParams) {
|
private suspend fun onLoginLink(params: LoginParams) {
|
||||||
// Is there a session already?
|
if (accountProviderAccessControl.isAllowedToConnectToAccountProvider(params.accountProvider.ensureProtocol())) {
|
||||||
val latestSessionId = sessionStore.getLatestSessionId()
|
// Is there a session already?
|
||||||
if (latestSessionId == null) {
|
val sessions = sessionStore.getAllSessions()
|
||||||
// No session, open login
|
if (sessions.isNotEmpty()) {
|
||||||
if (accountProviderAccessControl.isAllowedToConnectToAccountProvider(params.accountProvider.ensureProtocol())) {
|
if (featureFlagService.isFeatureEnabled(FeatureFlags.MultiAccount)) {
|
||||||
switchToNotLoggedInFlow(params)
|
val loginHintMatrixId = params.loginHint?.removePrefix("mxid:")
|
||||||
|
val existingAccount = sessions.find { it.userId == loginHintMatrixId }
|
||||||
|
if (existingAccount != null) {
|
||||||
|
// We have an existing account matching the login hint, ensure this is the current session
|
||||||
|
sessionStore.setLatestSession(existingAccount.userId)
|
||||||
|
} else {
|
||||||
|
val latestSessionId = sessions.maxBy { it.lastUsageIndex }.userId
|
||||||
|
attachSession(SessionId(latestSessionId))
|
||||||
|
backstack.push(NavTarget.NotLoggedInFlow(params))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Timber.w("Login link ignored, multi account is disabled")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.w("Login link ignored, we are not allowed to connect to the homeserver")
|
switchToNotLoggedInFlow(params)
|
||||||
switchToNotLoggedInFlow(null)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Just ignore the login link if we already have a session
|
Timber.w("Login link ignored, we are not allowed to connect to the homeserver")
|
||||||
Timber.w("Login link ignored, we already have a session")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,56 +340,95 @@ class RootFlowNode(
|
||||||
// No session, open login
|
// No session, open login
|
||||||
switchToNotLoggedInFlow(null)
|
switchToNotLoggedInFlow(null)
|
||||||
} else {
|
} else {
|
||||||
attachSession(latestSessionId)
|
// wait for the current session to be restored
|
||||||
.attachIncomingShare(intent)
|
val loggedInFlowNode = attachSession(latestSessionId)
|
||||||
|
if (sessionStore.getAllSessions().size > 1) {
|
||||||
|
// Several accounts, let the user choose which one to use
|
||||||
|
backstack.push(
|
||||||
|
NavTarget.AccountSelect(
|
||||||
|
currentSessionId = latestSessionId,
|
||||||
|
intent = intent,
|
||||||
|
permalinkData = null,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Only one account, directly attach the incoming share node.
|
||||||
|
loggedInFlowNode.attachIncomingShare(intent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun navigateTo(permalinkData: PermalinkData) {
|
private suspend fun navigateTo(permalinkData: PermalinkData) {
|
||||||
Timber.d("Navigating to $permalinkData")
|
Timber.d("Navigating to $permalinkData")
|
||||||
attachSession(null)
|
// Is there a session already?
|
||||||
.apply {
|
val latestSessionId = sessionStore.getLatestSessionId()
|
||||||
when (permalinkData) {
|
if (latestSessionId == null) {
|
||||||
is PermalinkData.FallbackLink -> Unit
|
// No session, open login
|
||||||
is PermalinkData.RoomEmailInviteLink -> Unit
|
switchToNotLoggedInFlow(null)
|
||||||
is PermalinkData.RoomLink -> {
|
} else {
|
||||||
attachRoom(
|
// wait for the current session to be restored
|
||||||
roomIdOrAlias = permalinkData.roomIdOrAlias,
|
val loggedInFlowNode = attachSession(latestSessionId)
|
||||||
trigger = JoinedRoom.Trigger.MobilePermalink,
|
when (permalinkData) {
|
||||||
serverNames = permalinkData.viaParameters,
|
is PermalinkData.FallbackLink -> Unit
|
||||||
eventId = permalinkData.eventId,
|
is PermalinkData.RoomEmailInviteLink -> Unit
|
||||||
clearBackstack = true
|
else -> {
|
||||||
|
if (sessionStore.getAllSessions().size > 1) {
|
||||||
|
// Several accounts, let the user choose which one to use
|
||||||
|
backstack.push(
|
||||||
|
NavTarget.AccountSelect(
|
||||||
|
currentSessionId = latestSessionId,
|
||||||
|
intent = null,
|
||||||
|
permalinkData = permalinkData,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
} else {
|
||||||
is PermalinkData.UserLink -> {
|
// Only one account, directly attach the room or the user node.
|
||||||
attachUser(permalinkData.userId)
|
loggedInFlowNode.attachPermalinkData(permalinkData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun LoggedInFlowNode.attachPermalinkData(permalinkData: PermalinkData) {
|
||||||
|
when (permalinkData) {
|
||||||
|
is PermalinkData.FallbackLink -> Unit
|
||||||
|
is PermalinkData.RoomEmailInviteLink -> Unit
|
||||||
|
is PermalinkData.RoomLink -> {
|
||||||
|
attachRoom(
|
||||||
|
roomIdOrAlias = permalinkData.roomIdOrAlias,
|
||||||
|
trigger = JoinedRoom.Trigger.MobilePermalink,
|
||||||
|
serverNames = permalinkData.viaParameters,
|
||||||
|
eventId = permalinkData.eventId,
|
||||||
|
clearBackstack = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is PermalinkData.UserLink -> {
|
||||||
|
attachUser(permalinkData.userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun navigateTo(deeplinkData: DeeplinkData) {
|
private suspend fun navigateTo(deeplinkData: DeeplinkData) {
|
||||||
Timber.d("Navigating to $deeplinkData")
|
Timber.d("Navigating to $deeplinkData")
|
||||||
attachSession(deeplinkData.sessionId)
|
attachSession(deeplinkData.sessionId).apply {
|
||||||
.apply {
|
when (deeplinkData) {
|
||||||
when (deeplinkData) {
|
is DeeplinkData.Root -> Unit // The room list will always be shown, observing FtueState
|
||||||
is DeeplinkData.Root -> Unit // The room list will always be shown, observing FtueState
|
is DeeplinkData.Room -> attachRoom(deeplinkData.roomId.toRoomIdOrAlias(), clearBackstack = true)
|
||||||
is DeeplinkData.Room -> attachRoom(deeplinkData.roomId.toRoomIdOrAlias(), clearBackstack = true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onOidcAction(oidcAction: OidcAction) {
|
private fun onOidcAction(oidcAction: OidcAction) {
|
||||||
oidcActionFlow.post(oidcAction)
|
oidcActionFlow.post(oidcAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
// [sessionId] will be null for permalink.
|
private suspend fun attachSession(sessionId: SessionId): LoggedInFlowNode {
|
||||||
private suspend fun attachSession(sessionId: SessionId?): LoggedInFlowNode {
|
// Ensure that the session is the latest one
|
||||||
// TODO handle multi-session
|
sessionStore.setLatestSession(sessionId.value)
|
||||||
return waitForChildAttached<LoggedInAppScopeFlowNode, NavTarget> { navTarget ->
|
return waitForChildAttached<LoggedInAppScopeFlowNode, NavTarget> { navTarget ->
|
||||||
navTarget is NavTarget.LoggedInFlow && (sessionId == null || navTarget.sessionId == sessionId)
|
navTarget is NavTarget.LoggedInFlow && navTarget.sessionId == sessionId
|
||||||
}
|
}.attachSession()
|
||||||
.attachSession()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ package io.element.android.appnav.di
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
|
|
@ -30,7 +30,7 @@ import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@Inject
|
@AssistedInject
|
||||||
class SyncOrchestrator(
|
class SyncOrchestrator(
|
||||||
@Assisted matrixClient: MatrixClient,
|
@Assisted matrixClient: MatrixClient,
|
||||||
private val appForegroundStateService: AppForegroundStateService,
|
private val appForegroundStateService: AppForegroundStateService,
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,12 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class LoggedInNode(
|
class LoggedInNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import com.bumble.appyx.core.plugin.plugins
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.newRoot
|
import com.bumble.appyx.navmodel.backstack.operation.newRoot
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.appnav.room.joined.JoinedRoomFlowNode
|
import io.element.android.appnav.room.joined.JoinedRoomFlowNode
|
||||||
|
|
@ -64,7 +64,7 @@ import java.util.Optional
|
||||||
import kotlin.jvm.optionals.getOrNull
|
import kotlin.jvm.optionals.getOrNull
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class RoomFlowNode(
|
class RoomFlowNode(
|
||||||
@Assisted val buildContext: BuildContext,
|
@Assisted val buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import com.bumble.appyx.core.plugin.plugins
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.newRoot
|
import com.bumble.appyx.navmodel.backstack.operation.newRoot
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.appnav.room.RoomNavigationTarget
|
import io.element.android.appnav.room.RoomNavigationTarget
|
||||||
import io.element.android.libraries.architecture.BackstackView
|
import io.element.android.libraries.architecture.BackstackView
|
||||||
|
|
@ -45,7 +45,7 @@ import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class JoinedRoomFlowNode(
|
class JoinedRoomFlowNode(
|
||||||
@Assisted val buildContext: BuildContext,
|
@Assisted val buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.appnav.di.RoomComponentFactory
|
import io.element.android.appnav.di.RoomComponentFactory
|
||||||
import io.element.android.appnav.room.RoomNavigationTarget
|
import io.element.android.appnav.room.RoomNavigationTarget
|
||||||
|
|
@ -45,7 +45,7 @@ import kotlinx.parcelize.Parcelize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class JoinedRoomLoadedFlowNode(
|
class JoinedRoomLoadedFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ class IntentResolverTest {
|
||||||
@Test
|
@Test
|
||||||
fun `test resolve oidc`() {
|
fun `test resolve oidc`() {
|
||||||
val sut = createIntentResolver(
|
val sut = createIntentResolver(
|
||||||
oidcIntentResolverResult = { OidcAction.GoBack },
|
oidcIntentResolverResult = { OidcAction.GoBack() },
|
||||||
)
|
)
|
||||||
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
|
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
|
||||||
action = Intent.ACTION_VIEW
|
action = Intent.ACTION_VIEW
|
||||||
|
|
@ -120,7 +120,7 @@ class IntentResolverTest {
|
||||||
val result = sut.resolve(intent)
|
val result = sut.resolve(intent)
|
||||||
assertThat(result).isEqualTo(
|
assertThat(result).isEqualTo(
|
||||||
ResolvedIntent.Oidc(
|
ResolvedIntent.Oidc(
|
||||||
oidcAction = OidcAction.GoBack
|
oidcAction = OidcAction.GoBack()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import dev.zacsweers.metro.BindingContainer
|
||||||
import dev.zacsweers.metro.Binds
|
import dev.zacsweers.metro.Binds
|
||||||
import dev.zacsweers.metro.ContributesTo
|
import dev.zacsweers.metro.ContributesTo
|
||||||
import dev.zacsweers.metro.IntoMap
|
import dev.zacsweers.metro.IntoMap
|
||||||
|
import dev.zacsweers.metro.Origin
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
|
||||||
|
|
@ -71,14 +72,16 @@ class ContributesNodeProcessor(
|
||||||
val scope = annotation.arguments.find { it.name?.asString() == "scope" }!!.value as KSType
|
val scope = annotation.arguments.find { it.name?.asString() == "scope" }!!.value as KSType
|
||||||
val modulePackage = ksClass.packageName.asString()
|
val modulePackage = ksClass.packageName.asString()
|
||||||
val moduleClassName = "${ksClass.simpleName.asString()}_Module"
|
val moduleClassName = "${ksClass.simpleName.asString()}_Module"
|
||||||
|
val nodeClassName = ClassName.bestGuess(ksClass.qualifiedName!!.asString())
|
||||||
val content = FileSpec.builder(
|
val content = FileSpec.builder(
|
||||||
packageName = modulePackage,
|
packageName = modulePackage,
|
||||||
fileName = moduleClassName,
|
fileName = moduleClassName,
|
||||||
)
|
)
|
||||||
.addType(
|
.addType(
|
||||||
TypeSpec.interfaceBuilder(moduleClassName)
|
TypeSpec.interfaceBuilder(moduleClassName)
|
||||||
|
.addAnnotation(AnnotationSpec.builder(Origin::class).addMember(CLASS_PLACEHOLDER, nodeClassName).build())
|
||||||
.addAnnotation(BindingContainer::class)
|
.addAnnotation(BindingContainer::class)
|
||||||
.addAnnotation(AnnotationSpec.builder(ContributesTo::class).addMember("%T::class", scope.toTypeName()).build())
|
.addAnnotation(AnnotationSpec.builder(ContributesTo::class).addMember(CLASS_PLACEHOLDER, scope.toTypeName()).build())
|
||||||
.addFunction(
|
.addFunction(
|
||||||
FunSpec.builder("bind${ksClass.simpleName.asString()}Factory")
|
FunSpec.builder("bind${ksClass.simpleName.asString()}Factory")
|
||||||
.addModifiers(KModifier.ABSTRACT)
|
.addModifiers(KModifier.ABSTRACT)
|
||||||
|
|
@ -88,7 +91,7 @@ class ContributesNodeProcessor(
|
||||||
.addAnnotation(IntoMap::class)
|
.addAnnotation(IntoMap::class)
|
||||||
.addAnnotation(
|
.addAnnotation(
|
||||||
AnnotationSpec.Companion.builder(ClassName.bestGuess(nodeKeyFqName.asString())).addMember(
|
AnnotationSpec.Companion.builder(ClassName.bestGuess(nodeKeyFqName.asString())).addMember(
|
||||||
"%T::class",
|
CLASS_PLACEHOLDER,
|
||||||
ClassName.bestGuess(ksClass.qualifiedName!!.asString())
|
ClassName.bestGuess(ksClass.qualifiedName!!.asString())
|
||||||
).build()
|
).build()
|
||||||
)
|
)
|
||||||
|
|
@ -115,7 +118,7 @@ class ContributesNodeProcessor(
|
||||||
val assistedParameters = constructor.parameters.filter { it.isAnnotationPresent(Assisted::class) }
|
val assistedParameters = constructor.parameters.filter { it.isAnnotationPresent(Assisted::class) }
|
||||||
if (assistedParameters.size != 2) {
|
if (assistedParameters.size != 2) {
|
||||||
error(
|
error(
|
||||||
"${ksClass.qualifiedName?.asString()} must have an @Inject constructor with 2 @Assisted parameters. Found: ${assistedParameters.size}",
|
"${ksClass.qualifiedName?.asString()} must have a constructor with 2 @Assisted parameters. Found: ${assistedParameters.size}",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val contextAssistedParam = assistedParameters[0]
|
val contextAssistedParam = assistedParameters[0]
|
||||||
|
|
@ -138,6 +141,7 @@ class ContributesNodeProcessor(
|
||||||
.addType(
|
.addType(
|
||||||
TypeSpec.interfaceBuilder(assistedFactoryClassName)
|
TypeSpec.interfaceBuilder(assistedFactoryClassName)
|
||||||
.addSuperinterface(ClassName.bestGuess(assistedNodeFactoryFqName.asString()).parameterizedBy(nodeClassName))
|
.addSuperinterface(ClassName.bestGuess(assistedNodeFactoryFqName.asString()).parameterizedBy(nodeClassName))
|
||||||
|
.addAnnotation(AnnotationSpec.builder(Origin::class).addMember("%T::class", nodeClassName).build())
|
||||||
.addAnnotation(AssistedFactory::class)
|
.addAnnotation(AssistedFactory::class)
|
||||||
.addFunction(
|
.addFunction(
|
||||||
FunSpec.builder("create")
|
FunSpec.builder("create")
|
||||||
|
|
@ -161,6 +165,7 @@ class ContributesNodeProcessor(
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val CLASS_PLACEHOLDER = "%T::class"
|
||||||
private val assistedNodeFactoryFqName = FqName("io.element.android.libraries.architecture.AssistedNodeFactory")
|
private val assistedNodeFactoryFqName = FqName("io.element.android.libraries.architecture.AssistedNodeFactory")
|
||||||
private val nodeKeyFqName = FqName("io.element.android.libraries.architecture.NodeKey")
|
private val nodeKeyFqName = FqName("io.element.android.libraries.architecture.NodeKey")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,14 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.appconfig.AnalyticsConfig
|
import io.element.android.appconfig.AnalyticsConfig
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
|
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class AnalyticsOptInNode(
|
class AnalyticsOptInNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.MobileScreen
|
import im.vector.app.features.analytics.plan.MobileScreen
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.call.api.CallType
|
import io.element.android.features.call.api.CallType
|
||||||
|
|
@ -49,7 +49,7 @@ import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@Inject
|
@AssistedInject
|
||||||
class CallScreenPresenter(
|
class CallScreenPresenter(
|
||||||
@Assisted private val callType: CallType,
|
@Assisted private val callType: CallType,
|
||||||
@Assisted private val navigator: CallScreenNavigator,
|
@Assisted private val navigator: CallScreenNavigator,
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,7 @@ class WebViewWidgetMessageInterceptor(
|
||||||
return assetLoader.shouldInterceptRequest(request.url)
|
return assetLoader.shouldInterceptRequest(request.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION")
|
||||||
override fun shouldInterceptRequest(view: WebView?, url: String): WebResourceResponse? {
|
override fun shouldInterceptRequest(view: WebView?, url: String): WebResourceResponse? {
|
||||||
return assetLoader.shouldInterceptRequest(url.toUri())
|
return assetLoader.shouldInterceptRequest(url.toUri())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType
|
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
|
|
@ -26,7 +26,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
||||||
@ContributesNode(RoomScope::class)
|
@ContributesNode(RoomScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class ChangeRolesNode(
|
class ChangeRolesNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.RoomModeration
|
import im.vector.app.features.analytics.plan.RoomModeration
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
|
@ -47,7 +47,7 @@ import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Inject
|
@AssistedInject
|
||||||
class ChangeRolesPresenter(
|
class ChangeRolesPresenter(
|
||||||
@Assisted private val role: RoomMember.Role,
|
@Assisted private val role: RoomMember.Role,
|
||||||
private val room: JoinedRoom,
|
private val room: JoinedRoom,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.node.ParentNode
|
import com.bumble.appyx.core.node.ParentNode
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.appnav.di.RoomComponentFactory
|
import io.element.android.appnav.di.RoomComponentFactory
|
||||||
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint
|
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint
|
||||||
|
|
@ -32,7 +32,7 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class ChangeRoomMemberRolesRootNode(
|
class ChangeRoomMemberRolesRootNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import com.bumble.appyx.core.plugin.plugins
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.replace
|
import com.bumble.appyx.navmodel.backstack.operation.replace
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||||
import io.element.android.features.createroom.impl.addpeople.AddPeopleNode
|
import io.element.android.features.createroom.impl.addpeople.AddPeopleNode
|
||||||
|
|
@ -30,7 +30,7 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class CreateRoomFlowNode(
|
class CreateRoomFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.invitepeople.api.InvitePeoplePresenter
|
import io.element.android.features.invitepeople.api.InvitePeoplePresenter
|
||||||
import io.element.android.features.invitepeople.api.InvitePeopleRenderer
|
import io.element.android.features.invitepeople.api.InvitePeopleRenderer
|
||||||
|
|
@ -24,7 +24,7 @@ import io.element.android.libraries.di.SessionScope
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class AddPeopleNode(
|
class AddPeopleNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.MobileScreen
|
import im.vector.app.features.analytics.plan.MobileScreen
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
|
@ -23,7 +23,7 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class ConfigureRoomNode(
|
class ConfigureRoomNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,12 @@ import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class AccountDeactivationNode(
|
class AccountDeactivationNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
<string name="screen_deactivate_account_list_item_1">"%1$s a fiókját (nem fog tudni újra bejelentkezni, és az azonosítója nem használható újra)."</string>
|
<string name="screen_deactivate_account_list_item_1">"%1$s a fiókját (nem fog tudni újra bejelentkezni, és az azonosítója nem használható újra)."</string>
|
||||||
<string name="screen_deactivate_account_list_item_1_bold_part">"Véglegesen letiltja"</string>
|
<string name="screen_deactivate_account_list_item_1_bold_part">"Véglegesen letiltja"</string>
|
||||||
<string name="screen_deactivate_account_list_item_2">"Eltávolításra kerül az összes csevegőszobából."</string>
|
<string name="screen_deactivate_account_list_item_2">"Eltávolításra kerül az összes csevegőszobából."</string>
|
||||||
<string name="screen_deactivate_account_list_item_3">"Törlésre kerülnek a fiókadatai a személyazonosító kiszolgálónkról."</string>
|
<string name="screen_deactivate_account_list_item_3">"Törlésre kerülnek a fiókadatai az azonosítási kiszolgálónkról."</string>
|
||||||
<string name="screen_deactivate_account_list_item_4">"Üzenetei továbbra is láthatóak maradnak a regisztrált felhasználók számára, de nem lesznek elérhetőek az új vagy nem regisztrált felhasználók számára, ha úgy dönt, hogy törli őket."</string>
|
<string name="screen_deactivate_account_list_item_4">"Üzenetei továbbra is láthatóak maradnak a regisztrált felhasználók számára, de nem lesznek elérhetőek az új vagy nem regisztrált felhasználók számára, ha úgy dönt, hogy törli őket."</string>
|
||||||
<string name="screen_deactivate_account_title">"Fiók deaktiválása"</string>
|
<string name="screen_deactivate_account_title">"Fiók deaktiválása"</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import com.bumble.appyx.navmodel.backstack.operation.newRoot
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.replace
|
import com.bumble.appyx.navmodel.backstack.operation.replace
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.analytics.api.AnalyticsEntryPoint
|
import io.element.android.features.analytics.api.AnalyticsEntryPoint
|
||||||
|
|
@ -42,7 +43,7 @@ import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class FtueFlowNode(
|
class FtueFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,13 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
import io.element.android.libraries.architecture.inputs
|
import io.element.android.libraries.architecture.inputs
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class NotificationsOptInNode(
|
class NotificationsOptInNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||||
import io.element.android.libraries.permissions.api.PermissionStateProvider
|
import io.element.android.libraries.permissions.api.PermissionStateProvider
|
||||||
|
|
@ -25,7 +25,7 @@ import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Inject
|
@AssistedInject
|
||||||
class NotificationsOptInPresenter(
|
class NotificationsOptInPresenter(
|
||||||
permissionsPresenterFactory: PermissionsPresenter.Factory,
|
permissionsPresenterFactory: PermissionsPresenter.Factory,
|
||||||
@Assisted private val callback: NotificationsOptInNode.Callback,
|
@Assisted private val callback: NotificationsOptInNode.Callback,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import com.bumble.appyx.navmodel.backstack.operation.newRoot
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.pop
|
import com.bumble.appyx.navmodel.backstack.operation.pop
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.appconfig.LearnMoreConfig
|
import io.element.android.appconfig.LearnMoreConfig
|
||||||
import io.element.android.features.ftue.impl.sessionverification.choosemode.ChooseSelfVerificationModeNode
|
import io.element.android.features.ftue.impl.sessionverification.choosemode.ChooseSelfVerificationModeNode
|
||||||
|
|
@ -37,7 +37,7 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class FtueSessionVerificationFlowNode(
|
class FtueSessionVerificationFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,14 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.logout.api.direct.DirectLogoutView
|
import io.element.android.features.logout.api.direct.DirectLogoutView
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class ChooseSelfVerificationModeNode(
|
class ChooseSelfVerificationModeNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ dependencies {
|
||||||
testImplementation(projects.libraries.permissions.noop)
|
testImplementation(projects.libraries.permissions.noop)
|
||||||
testImplementation(projects.libraries.permissions.test)
|
testImplementation(projects.libraries.permissions.test)
|
||||||
testImplementation(projects.libraries.preferences.test)
|
testImplementation(projects.libraries.preferences.test)
|
||||||
|
testImplementation(projects.libraries.sessionStorage.test)
|
||||||
testImplementation(projects.libraries.push.test)
|
testImplementation(projects.libraries.push.test)
|
||||||
testImplementation(projects.services.analytics.test)
|
testImplementation(projects.services.analytics.test)
|
||||||
testImplementation(projects.services.toolbox.test)
|
testImplementation(projects.services.toolbox.test)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.home.impl
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
|
||||||
|
class CurrentUserWithNeighborsBuilder {
|
||||||
|
/**
|
||||||
|
* Build a list of [MatrixUser] containing the current user. If there are other sessions, the list
|
||||||
|
* will contain 3 users, with the current user in the middle.
|
||||||
|
* If there is only one other session, the list will contain twice the other user, to allow cycling.
|
||||||
|
*/
|
||||||
|
fun build(
|
||||||
|
matrixUser: MatrixUser,
|
||||||
|
sessions: List<SessionData>,
|
||||||
|
): ImmutableList<MatrixUser> {
|
||||||
|
// Sort by position to always have the same order (not depending on last account usage)
|
||||||
|
return sessions.sortedBy { it.position }
|
||||||
|
.map {
|
||||||
|
if (it.userId == matrixUser.userId.value) {
|
||||||
|
// Always use the freshest profile for the current user
|
||||||
|
matrixUser
|
||||||
|
} else {
|
||||||
|
// Use the data from the DB
|
||||||
|
MatrixUser(
|
||||||
|
userId = UserId(it.userId),
|
||||||
|
displayName = it.userDisplayName,
|
||||||
|
avatarUrl = it.userAvatarUrl,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.let { sessionList ->
|
||||||
|
// If the list has one item, there is no other session, return the list
|
||||||
|
when (sessionList.size) {
|
||||||
|
// Can happen when the user signs out (?)
|
||||||
|
0 -> listOf(matrixUser)
|
||||||
|
1 -> sessionList
|
||||||
|
else -> {
|
||||||
|
// Create a list with extra item at the start and end if necessary to have the current user in the middle
|
||||||
|
// If the list is [A, B, C, D] and the current user is A we want to return [D, A, B]
|
||||||
|
// If the current user is B, we want to return [A, B, C]
|
||||||
|
// If the current user is C, we want to return [B, C, D]
|
||||||
|
// If the current user is D, we want to return [C, D, A]
|
||||||
|
// Special case: if there are only two users, we want to return [B, A, B] or [A, B, A] to allows cycling
|
||||||
|
// between the two users.
|
||||||
|
val currentUserIndex = sessionList.indexOfFirst { it.userId == matrixUser.userId }
|
||||||
|
when (currentUserIndex) {
|
||||||
|
// This can happen when the user signs out.
|
||||||
|
// In this case, just return a singleton list with the current user.
|
||||||
|
-1 -> listOf(matrixUser)
|
||||||
|
0 -> listOf(sessionList.last()) + sessionList.take(2)
|
||||||
|
sessionList.lastIndex -> sessionList.takeLast(2) + sessionList.first()
|
||||||
|
else -> sessionList.slice(currentUserIndex - 1..currentUserIndex + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toPersistentList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,9 @@
|
||||||
|
|
||||||
package io.element.android.features.home.impl
|
package io.element.android.features.home.impl
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
|
||||||
sealed interface HomeEvents {
|
sealed interface HomeEvents {
|
||||||
data class SelectHomeNavigationBarItem(val item: HomeNavigationBarItem) : HomeEvents
|
data class SelectHomeNavigationBarItem(val item: HomeNavigationBarItem) : HomeEvents
|
||||||
|
data class SwitchToAccount(val sessionId: SessionId) : HomeEvents
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.pop
|
import com.bumble.appyx.navmodel.backstack.operation.pop
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.MobileScreen
|
import im.vector.app.features.analytics.plan.MobileScreen
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint
|
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint
|
||||||
|
|
@ -56,7 +56,7 @@ import kotlinx.coroutines.withContext
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class HomeFlowNode(
|
class HomeFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
|
|
@ -29,6 +30,10 @@ import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.indicator.api.IndicatorService
|
import io.element.android.libraries.indicator.api.IndicatorService
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||||
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
class HomePresenter(
|
class HomePresenter(
|
||||||
|
|
@ -41,10 +46,21 @@ class HomePresenter(
|
||||||
private val logoutPresenter: Presenter<DirectLogoutState>,
|
private val logoutPresenter: Presenter<DirectLogoutState>,
|
||||||
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
|
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
|
||||||
private val featureFlagService: FeatureFlagService,
|
private val featureFlagService: FeatureFlagService,
|
||||||
|
private val sessionStore: SessionStore,
|
||||||
) : Presenter<HomeState> {
|
) : Presenter<HomeState> {
|
||||||
|
private val currentUserWithNeighborsBuilder = CurrentUserWithNeighborsBuilder()
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): HomeState {
|
override fun present(): HomeState {
|
||||||
val matrixUser = client.userProfile.collectAsState()
|
val coroutineState = rememberCoroutineScope()
|
||||||
|
val matrixUser by client.userProfile.collectAsState()
|
||||||
|
val currentUserAndNeighbors by remember {
|
||||||
|
combine(
|
||||||
|
client.userProfile,
|
||||||
|
sessionStore.sessionsFlow(),
|
||||||
|
currentUserWithNeighborsBuilder::build,
|
||||||
|
)
|
||||||
|
}.collectAsState(initial = persistentListOf(matrixUser))
|
||||||
val isOnline by syncService.isOnline.collectAsState()
|
val isOnline by syncService.isOnline.collectAsState()
|
||||||
val canReportBug by remember { rageshakeFeatureAvailability.isAvailable() }.collectAsState(false)
|
val canReportBug by remember { rageshakeFeatureAvailability.isAvailable() }.collectAsState(false)
|
||||||
val roomListState = roomListPresenter.present()
|
val roomListState = roomListPresenter.present()
|
||||||
|
|
@ -71,6 +87,9 @@ class HomePresenter(
|
||||||
is HomeEvents.SelectHomeNavigationBarItem -> {
|
is HomeEvents.SelectHomeNavigationBarItem -> {
|
||||||
currentHomeNavigationBarItemOrdinal = event.item.ordinal
|
currentHomeNavigationBarItemOrdinal = event.item.ordinal
|
||||||
}
|
}
|
||||||
|
is HomeEvents.SwitchToAccount -> coroutineState.launch {
|
||||||
|
sessionStore.setLatestSession(event.sessionId.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,7 +101,7 @@ class HomePresenter(
|
||||||
}
|
}
|
||||||
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
||||||
return HomeState(
|
return HomeState(
|
||||||
matrixUser = matrixUser.value,
|
currentUserAndNeighbors = currentUserAndNeighbors,
|
||||||
showAvatarIndicator = showAvatarIndicator,
|
showAvatarIndicator = showAvatarIndicator,
|
||||||
hasNetworkConnection = isOnline,
|
hasNetworkConnection = isOnline,
|
||||||
currentHomeNavigationBarItem = currentHomeNavigationBarItem,
|
currentHomeNavigationBarItem = currentHomeNavigationBarItem,
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,15 @@ import io.element.android.features.home.impl.spaces.HomeSpacesState
|
||||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class HomeState(
|
data class HomeState(
|
||||||
val matrixUser: MatrixUser,
|
/**
|
||||||
|
* The current user of this session, in case of multiple accounts, will contains 3 items, with the
|
||||||
|
* current user in the middle.
|
||||||
|
*/
|
||||||
|
val currentUserAndNeighbors: ImmutableList<MatrixUser>,
|
||||||
val showAvatarIndicator: Boolean,
|
val showAvatarIndicator: Boolean,
|
||||||
val hasNetworkConnection: Boolean,
|
val hasNetworkConnection: Boolean,
|
||||||
val currentHomeNavigationBarItem: HomeNavigationBarItem,
|
val currentHomeNavigationBarItem: HomeNavigationBarItem,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
|
||||||
open class HomeStateProvider : PreviewParameterProvider<HomeState> {
|
open class HomeStateProvider : PreviewParameterProvider<HomeState> {
|
||||||
override val values: Sequence<HomeState>
|
override val values: Sequence<HomeState>
|
||||||
|
|
@ -50,6 +51,7 @@ open class HomeStateProvider : PreviewParameterProvider<HomeState> {
|
||||||
|
|
||||||
internal fun aHomeState(
|
internal fun aHomeState(
|
||||||
matrixUser: MatrixUser = MatrixUser(userId = UserId("@id:domain"), displayName = "User#1"),
|
matrixUser: MatrixUser = MatrixUser(userId = UserId("@id:domain"), displayName = "User#1"),
|
||||||
|
currentUserAndNeighbors: List<MatrixUser> = listOf(matrixUser),
|
||||||
showAvatarIndicator: Boolean = false,
|
showAvatarIndicator: Boolean = false,
|
||||||
hasNetworkConnection: Boolean = true,
|
hasNetworkConnection: Boolean = true,
|
||||||
snackbarMessage: SnackbarMessage? = null,
|
snackbarMessage: SnackbarMessage? = null,
|
||||||
|
|
@ -61,7 +63,7 @@ internal fun aHomeState(
|
||||||
directLogoutState: DirectLogoutState = aDirectLogoutState(),
|
directLogoutState: DirectLogoutState = aDirectLogoutState(),
|
||||||
eventSink: (HomeEvents) -> Unit = {}
|
eventSink: (HomeEvents) -> Unit = {}
|
||||||
) = HomeState(
|
) = HomeState(
|
||||||
matrixUser = matrixUser,
|
currentUserAndNeighbors = currentUserAndNeighbors.toPersistentList(),
|
||||||
showAvatarIndicator = showAvatarIndicator,
|
showAvatarIndicator = showAvatarIndicator,
|
||||||
hasNetworkConnection = hasNetworkConnection,
|
hasNetworkConnection = hasNetworkConnection,
|
||||||
snackbarMessage = snackbarMessage,
|
snackbarMessage = snackbarMessage,
|
||||||
|
|
|
||||||
|
|
@ -171,12 +171,15 @@ private fun HomeScaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
RoomListTopBar(
|
RoomListTopBar(
|
||||||
title = stringResource(state.currentHomeNavigationBarItem.labelRes),
|
title = stringResource(state.currentHomeNavigationBarItem.labelRes),
|
||||||
matrixUser = state.matrixUser,
|
currentUserAndNeighbors = state.currentUserAndNeighbors,
|
||||||
showAvatarIndicator = state.showAvatarIndicator,
|
showAvatarIndicator = state.showAvatarIndicator,
|
||||||
areSearchResultsDisplayed = roomListState.searchState.isSearchActive,
|
areSearchResultsDisplayed = roomListState.searchState.isSearchActive,
|
||||||
onToggleSearch = { roomListState.eventSink(RoomListEvents.ToggleSearchResults) },
|
onToggleSearch = { roomListState.eventSink(RoomListEvents.ToggleSearchResults) },
|
||||||
onMenuActionClick = onMenuActionClick,
|
onMenuActionClick = onMenuActionClick,
|
||||||
onOpenSettings = onOpenSettings,
|
onOpenSettings = onOpenSettings,
|
||||||
|
onAccountSwitch = {
|
||||||
|
state.eventSink(HomeEvents.SwitchToAccount(it))
|
||||||
|
},
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
displayMenuItems = state.displayActions,
|
displayMenuItems = state.displayActions,
|
||||||
displayFilters = roomListState.displayFilters && state.currentHomeNavigationBarItem == HomeNavigationBarItem.Chats,
|
displayFilters = roomListState.displayFilters && state.currentHomeNavigationBarItem == HomeNavigationBarItem.Chats,
|
||||||
|
|
|
||||||
|
|
@ -11,19 +11,25 @@ import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.statusBarsPadding
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
|
import androidx.compose.foundation.pager.VerticalPager
|
||||||
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
|
|
@ -41,7 +47,6 @@ import io.element.android.features.home.impl.filters.RoomListFiltersView
|
||||||
import io.element.android.features.home.impl.filters.aRoomListFiltersState
|
import io.element.android.features.home.impl.filters.aRoomListFiltersState
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom
|
import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom
|
||||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
||||||
import io.element.android.libraries.designsystem.modifiers.backgroundVerticalGradient
|
import io.element.android.libraries.designsystem.modifiers.backgroundVerticalGradient
|
||||||
|
|
@ -57,23 +62,29 @@ import io.element.android.libraries.designsystem.theme.components.Icon
|
||||||
import io.element.android.libraries.designsystem.theme.components.IconButton
|
import io.element.android.libraries.designsystem.theme.components.IconButton
|
||||||
import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar
|
import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
|
||||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||||
import io.element.android.libraries.testtags.TestTags
|
import io.element.android.libraries.testtags.TestTags
|
||||||
import io.element.android.libraries.testtags.testTag
|
import io.element.android.libraries.testtags.testTag
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun RoomListTopBar(
|
fun RoomListTopBar(
|
||||||
title: String,
|
title: String,
|
||||||
matrixUser: MatrixUser,
|
currentUserAndNeighbors: ImmutableList<MatrixUser>,
|
||||||
showAvatarIndicator: Boolean,
|
showAvatarIndicator: Boolean,
|
||||||
areSearchResultsDisplayed: Boolean,
|
areSearchResultsDisplayed: Boolean,
|
||||||
onToggleSearch: () -> Unit,
|
onToggleSearch: () -> Unit,
|
||||||
onMenuActionClick: (RoomListMenuAction) -> Unit,
|
onMenuActionClick: (RoomListMenuAction) -> Unit,
|
||||||
onOpenSettings: () -> Unit,
|
onOpenSettings: () -> Unit,
|
||||||
|
onAccountSwitch: (SessionId) -> Unit,
|
||||||
scrollBehavior: TopAppBarScrollBehavior,
|
scrollBehavior: TopAppBarScrollBehavior,
|
||||||
displayMenuItems: Boolean,
|
displayMenuItems: Boolean,
|
||||||
displayFilters: Boolean,
|
displayFilters: Boolean,
|
||||||
|
|
@ -83,10 +94,11 @@ fun RoomListTopBar(
|
||||||
) {
|
) {
|
||||||
DefaultRoomListTopBar(
|
DefaultRoomListTopBar(
|
||||||
title = title,
|
title = title,
|
||||||
matrixUser = matrixUser,
|
currentUserAndNeighbors = currentUserAndNeighbors,
|
||||||
showAvatarIndicator = showAvatarIndicator,
|
showAvatarIndicator = showAvatarIndicator,
|
||||||
areSearchResultsDisplayed = areSearchResultsDisplayed,
|
areSearchResultsDisplayed = areSearchResultsDisplayed,
|
||||||
onOpenSettings = onOpenSettings,
|
onOpenSettings = onOpenSettings,
|
||||||
|
onAccountSwitch = onAccountSwitch,
|
||||||
onSearchClick = onToggleSearch,
|
onSearchClick = onToggleSearch,
|
||||||
onMenuActionClick = onMenuActionClick,
|
onMenuActionClick = onMenuActionClick,
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
|
|
@ -102,11 +114,12 @@ fun RoomListTopBar(
|
||||||
@Composable
|
@Composable
|
||||||
private fun DefaultRoomListTopBar(
|
private fun DefaultRoomListTopBar(
|
||||||
title: String,
|
title: String,
|
||||||
matrixUser: MatrixUser,
|
currentUserAndNeighbors: ImmutableList<MatrixUser>,
|
||||||
showAvatarIndicator: Boolean,
|
showAvatarIndicator: Boolean,
|
||||||
areSearchResultsDisplayed: Boolean,
|
areSearchResultsDisplayed: Boolean,
|
||||||
scrollBehavior: TopAppBarScrollBehavior,
|
scrollBehavior: TopAppBarScrollBehavior,
|
||||||
onOpenSettings: () -> Unit,
|
onOpenSettings: () -> Unit,
|
||||||
|
onAccountSwitch: (SessionId) -> Unit,
|
||||||
onSearchClick: () -> Unit,
|
onSearchClick: () -> Unit,
|
||||||
onMenuActionClick: (RoomListMenuAction) -> Unit,
|
onMenuActionClick: (RoomListMenuAction) -> Unit,
|
||||||
displayMenuItems: Boolean,
|
displayMenuItems: Boolean,
|
||||||
|
|
@ -116,12 +129,6 @@ private fun DefaultRoomListTopBar(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val collapsedFraction = scrollBehavior.state.collapsedFraction
|
val collapsedFraction = scrollBehavior.state.collapsedFraction
|
||||||
val avatarData by remember(matrixUser) {
|
|
||||||
derivedStateOf {
|
|
||||||
matrixUser.getAvatarData(size = AvatarSize.CurrentUserTopBar)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
val collapsedTitleTextStyle = ElementTheme.typography.aliasScreenTitle
|
val collapsedTitleTextStyle = ElementTheme.typography.aliasScreenTitle
|
||||||
val expandedTitleTextStyle = ElementTheme.typography.fontHeadingLgBold.copy(
|
val expandedTitleTextStyle = ElementTheme.typography.fontHeadingLgBold.copy(
|
||||||
|
|
@ -158,8 +165,9 @@ private fun DefaultRoomListTopBar(
|
||||||
},
|
},
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
NavigationIcon(
|
NavigationIcon(
|
||||||
avatarData = avatarData,
|
currentUserAndNeighbors = currentUserAndNeighbors,
|
||||||
showAvatarIndicator = showAvatarIndicator,
|
showAvatarIndicator = showAvatarIndicator,
|
||||||
|
onAccountSwitch = onAccountSwitch,
|
||||||
onClick = onOpenSettings,
|
onClick = onOpenSettings,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -247,19 +255,67 @@ private fun DefaultRoomListTopBar(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun NavigationIcon(
|
private fun NavigationIcon(
|
||||||
avatarData: AvatarData,
|
currentUserAndNeighbors: ImmutableList<MatrixUser>,
|
||||||
|
showAvatarIndicator: Boolean,
|
||||||
|
onAccountSwitch: (SessionId) -> Unit,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
if (currentUserAndNeighbors.size == 1) {
|
||||||
|
AccountIcon(
|
||||||
|
matrixUser = currentUserAndNeighbors.single(),
|
||||||
|
isCurrentAccount = true,
|
||||||
|
showAvatarIndicator = showAvatarIndicator,
|
||||||
|
onClick = onClick,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Render a vertical pager
|
||||||
|
val pagerState = rememberPagerState(initialPage = 1) { currentUserAndNeighbors.size }
|
||||||
|
// Listen to page changes and switch account if needed
|
||||||
|
val latestOnAccountSwitch by rememberUpdatedState(onAccountSwitch)
|
||||||
|
LaunchedEffect(pagerState) {
|
||||||
|
snapshotFlow { pagerState.settledPage }.collect { page ->
|
||||||
|
latestOnAccountSwitch(SessionId(currentUserAndNeighbors[page].userId.value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VerticalPager(
|
||||||
|
state = pagerState,
|
||||||
|
modifier = Modifier.height(48.dp),
|
||||||
|
) { page ->
|
||||||
|
AccountIcon(
|
||||||
|
matrixUser = currentUserAndNeighbors[page],
|
||||||
|
isCurrentAccount = page == 1,
|
||||||
|
showAvatarIndicator = page == 1 && showAvatarIndicator,
|
||||||
|
onClick = if (page == 1) {
|
||||||
|
onClick
|
||||||
|
} else {
|
||||||
|
{}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun AccountIcon(
|
||||||
|
matrixUser: MatrixUser,
|
||||||
|
isCurrentAccount: Boolean,
|
||||||
showAvatarIndicator: Boolean,
|
showAvatarIndicator: Boolean,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.testTag(TestTags.homeScreenSettings),
|
modifier = if (isCurrentAccount) Modifier.testTag(TestTags.homeScreenSettings) else Modifier,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
) {
|
) {
|
||||||
Box {
|
Box {
|
||||||
|
val avatarData by remember(matrixUser) {
|
||||||
|
derivedStateOf {
|
||||||
|
matrixUser.getAvatarData(size = AvatarSize.CurrentUserTopBar)
|
||||||
|
}
|
||||||
|
}
|
||||||
Avatar(
|
Avatar(
|
||||||
avatarData = avatarData,
|
avatarData = avatarData,
|
||||||
avatarType = AvatarType.User,
|
avatarType = AvatarType.User,
|
||||||
contentDescription = stringResource(CommonStrings.common_settings),
|
contentDescription = if (isCurrentAccount) stringResource(CommonStrings.common_settings) else null,
|
||||||
)
|
)
|
||||||
if (showAvatarIndicator) {
|
if (showAvatarIndicator) {
|
||||||
RedIndicatorAtom(
|
RedIndicatorAtom(
|
||||||
|
|
@ -276,11 +332,12 @@ private fun NavigationIcon(
|
||||||
internal fun DefaultRoomListTopBarPreview() = ElementPreview {
|
internal fun DefaultRoomListTopBarPreview() = ElementPreview {
|
||||||
DefaultRoomListTopBar(
|
DefaultRoomListTopBar(
|
||||||
title = stringResource(R.string.screen_roomlist_main_space_title),
|
title = stringResource(R.string.screen_roomlist_main_space_title),
|
||||||
matrixUser = MatrixUser(UserId("@id:domain"), "Alice"),
|
currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")),
|
||||||
showAvatarIndicator = false,
|
showAvatarIndicator = false,
|
||||||
areSearchResultsDisplayed = false,
|
areSearchResultsDisplayed = false,
|
||||||
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()),
|
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()),
|
||||||
onOpenSettings = {},
|
onOpenSettings = {},
|
||||||
|
onAccountSwitch = {},
|
||||||
onSearchClick = {},
|
onSearchClick = {},
|
||||||
displayMenuItems = true,
|
displayMenuItems = true,
|
||||||
displayFilters = true,
|
displayFilters = true,
|
||||||
|
|
@ -296,11 +353,33 @@ internal fun DefaultRoomListTopBarPreview() = ElementPreview {
|
||||||
internal fun DefaultRoomListTopBarWithIndicatorPreview() = ElementPreview {
|
internal fun DefaultRoomListTopBarWithIndicatorPreview() = ElementPreview {
|
||||||
DefaultRoomListTopBar(
|
DefaultRoomListTopBar(
|
||||||
title = stringResource(R.string.screen_roomlist_main_space_title),
|
title = stringResource(R.string.screen_roomlist_main_space_title),
|
||||||
matrixUser = MatrixUser(UserId("@id:domain"), "Alice"),
|
currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")),
|
||||||
showAvatarIndicator = true,
|
showAvatarIndicator = true,
|
||||||
areSearchResultsDisplayed = false,
|
areSearchResultsDisplayed = false,
|
||||||
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()),
|
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()),
|
||||||
onOpenSettings = {},
|
onOpenSettings = {},
|
||||||
|
onAccountSwitch = {},
|
||||||
|
onSearchClick = {},
|
||||||
|
displayMenuItems = true,
|
||||||
|
displayFilters = true,
|
||||||
|
filtersState = aRoomListFiltersState(),
|
||||||
|
canReportBug = true,
|
||||||
|
onMenuActionClick = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@PreviewsDayNight
|
||||||
|
@Composable
|
||||||
|
internal fun DefaultRoomListTopBarMultiAccountPreview() = ElementPreview {
|
||||||
|
DefaultRoomListTopBar(
|
||||||
|
title = stringResource(R.string.screen_roomlist_main_space_title),
|
||||||
|
currentUserAndNeighbors = aMatrixUserList().take(3).toPersistentList(),
|
||||||
|
showAvatarIndicator = false,
|
||||||
|
areSearchResultsDisplayed = false,
|
||||||
|
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()),
|
||||||
|
onOpenSettings = {},
|
||||||
|
onAccountSwitch = {},
|
||||||
onSearchClick = {},
|
onSearchClick = {},
|
||||||
displayMenuItems = true,
|
displayMenuItems = true,
|
||||||
displayFilters = true,
|
displayFilters = true,
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,12 @@
|
||||||
<string name="screen_home_tab_chats">"Всички чатове"</string>
|
<string name="screen_home_tab_chats">"Всички чатове"</string>
|
||||||
<string name="screen_invites_decline_chat_message">"Сигурни ли сте, че искате да отхвърлите поканата за присъединяване в %1$s?"</string>
|
<string name="screen_invites_decline_chat_message">"Сигурни ли сте, че искате да отхвърлите поканата за присъединяване в %1$s?"</string>
|
||||||
<string name="screen_invites_decline_chat_title">"Отказване на покана"</string>
|
<string name="screen_invites_decline_chat_title">"Отказване на покана"</string>
|
||||||
|
<string name="screen_invites_decline_direct_chat_message">"Сигурни ли сте, че искате да откажете този личен чат с %1$s?"</string>
|
||||||
|
<string name="screen_invites_decline_direct_chat_title">"Отказване на чат"</string>
|
||||||
<string name="screen_invites_empty_list">"Няма покани"</string>
|
<string name="screen_invites_empty_list">"Няма покани"</string>
|
||||||
<string name="screen_invites_invited_you">"%1$s (%2$s) ви покани"</string>
|
<string name="screen_invites_invited_you">"%1$s (%2$s) ви покани"</string>
|
||||||
|
<string name="screen_migration_message">"Това е еднократен процес, благодаря, че изчакахте."</string>
|
||||||
|
<string name="screen_migration_title">"Настройване на вашия акаунт."</string>
|
||||||
<string name="screen_roomlist_a11y_create_message">"Създаване на нов разговор или стая"</string>
|
<string name="screen_roomlist_a11y_create_message">"Създаване на нов разговор или стая"</string>
|
||||||
<string name="screen_roomlist_empty_message">"Започнете, като изпратите съобщение на някого."</string>
|
<string name="screen_roomlist_empty_message">"Започнете, като изпратите съобщение на някого."</string>
|
||||||
<string name="screen_roomlist_empty_title">"Все още няма чатове."</string>
|
<string name="screen_roomlist_empty_title">"Все още няма чатове."</string>
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,5 @@
|
||||||
<string name="confirm_recovery_key_banner_primary_button_title">"Enter your backup password"</string>
|
<string name="confirm_recovery_key_banner_primary_button_title">"Enter your backup password"</string>
|
||||||
<string name="confirm_recovery_key_banner_secondary_button_title">"Forgot your backup password?"</string>
|
<string name="confirm_recovery_key_banner_secondary_button_title">"Forgot your backup password?"</string>
|
||||||
<string name="confirm_recovery_key_banner_title">"Your message backup is out of sync"</string>
|
<string name="confirm_recovery_key_banner_title">"Your message backup is out of sync"</string>
|
||||||
<string name="session_verification_banner_message">"Looks like you\'re using a new device. Confirm it with another connected device to access your encrypted messages."</string>
|
<string name="session_verification_banner_message">"Looks like you\'re using a new device. Confirm it with another linked device to access your encrypted messages."</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
<string name="banner_battery_optimization_content_android">"Kapcsolja ki az alkalmazás akkumulátor-optimalizálását, hogy biztosan megkapja az összes értesítést."</string>
|
<string name="banner_battery_optimization_content_android">"Kapcsolja ki az alkalmazás akkumulátoroptimalizálását, hogy biztosan megkapja az összes értesítést."</string>
|
||||||
<string name="banner_battery_optimization_submit_android">"Optimalizálás letiltása"</string>
|
<string name="banner_battery_optimization_submit_android">"Optimalizálás letiltása"</string>
|
||||||
<string name="banner_battery_optimization_title_android">"Nem érkeznek meg az értesítések?"</string>
|
<string name="banner_battery_optimization_title_android">"Nem érkeznek meg az értesítések?"</string>
|
||||||
<string name="banner_set_up_recovery_content">"Hozzon létre egy új helyreállítási kulcsot, amellyel visszaállíthatja a titkosított üzenetek előzményeit, ha elveszíti az eszközökhöz való hozzáférést."</string>
|
<string name="banner_set_up_recovery_content">"Hozzon létre egy új helyreállítási kulcsot, amellyel visszaállíthatja a titkosított üzenetek előzményeit, ha elveszíti az eszközökhöz való hozzáférést."</string>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,222 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.home.impl
|
||||||
|
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
|
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||||
|
import io.element.android.libraries.matrix.test.A_USER_ID_3
|
||||||
|
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||||
|
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||||
|
import io.element.android.libraries.sessionstorage.test.aSessionData
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class CurrentUserWithNeighborsBuilderTest {
|
||||||
|
@Test
|
||||||
|
fun `build on empty list returns current user`() {
|
||||||
|
val sut = CurrentUserWithNeighborsBuilder()
|
||||||
|
val matrixUser = aMatrixUser()
|
||||||
|
val list = listOf<SessionData>()
|
||||||
|
val result = sut.build(matrixUser, list)
|
||||||
|
assertThat(result).containsExactly(matrixUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ensure that account are sorted by position`() {
|
||||||
|
val sut = CurrentUserWithNeighborsBuilder()
|
||||||
|
val matrixUser = aMatrixUser(id = A_USER_ID.value)
|
||||||
|
val list = listOf(
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID.value,
|
||||||
|
position = 3,
|
||||||
|
),
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID_2.value,
|
||||||
|
position = 2,
|
||||||
|
),
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID_3.value,
|
||||||
|
position = 1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val result = sut.build(matrixUser, list)
|
||||||
|
assertThat(result.map { it.userId }).containsExactly(
|
||||||
|
A_USER_ID_3,
|
||||||
|
A_USER_ID_2,
|
||||||
|
A_USER_ID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `if current user is not found, return a singleton with current user`() {
|
||||||
|
val sut = CurrentUserWithNeighborsBuilder()
|
||||||
|
val matrixUser = aMatrixUser(id = A_USER_ID.value)
|
||||||
|
val list = listOf(
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID_2.value,
|
||||||
|
),
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID_3.value,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val result = sut.build(matrixUser, list)
|
||||||
|
assertThat(result.map { it.userId }).containsExactly(
|
||||||
|
A_USER_ID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `one account, will return a singleton`() {
|
||||||
|
val sut = CurrentUserWithNeighborsBuilder()
|
||||||
|
val matrixUser = aMatrixUser(id = A_USER_ID.value)
|
||||||
|
val list = listOf(
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID.value,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val result = sut.build(matrixUser, list)
|
||||||
|
assertThat(result.map { it.userId }).containsExactly(
|
||||||
|
A_USER_ID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `two accounts, first is current, will return 3 items`() {
|
||||||
|
val sut = CurrentUserWithNeighborsBuilder()
|
||||||
|
val matrixUser = aMatrixUser(id = A_USER_ID.value)
|
||||||
|
val list = listOf(
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID.value,
|
||||||
|
),
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID_2.value,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val result = sut.build(matrixUser, list)
|
||||||
|
assertThat(result.map { it.userId }).containsExactly(
|
||||||
|
A_USER_ID_2,
|
||||||
|
A_USER_ID,
|
||||||
|
A_USER_ID_2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `two accounts, second is current, will return 3 items`() {
|
||||||
|
val sut = CurrentUserWithNeighborsBuilder()
|
||||||
|
val matrixUser = aMatrixUser(id = A_USER_ID_2.value)
|
||||||
|
val list = listOf(
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID.value,
|
||||||
|
),
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID_2.value,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val result = sut.build(matrixUser, list)
|
||||||
|
assertThat(result.map { it.userId }).containsExactly(
|
||||||
|
A_USER_ID,
|
||||||
|
A_USER_ID_2,
|
||||||
|
A_USER_ID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `three accounts, first is current, will return last current and next`() {
|
||||||
|
val sut = CurrentUserWithNeighborsBuilder()
|
||||||
|
val matrixUser = aMatrixUser(id = A_USER_ID.value)
|
||||||
|
val list = listOf(
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID.value,
|
||||||
|
),
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID_2.value,
|
||||||
|
),
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID_3.value,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val result = sut.build(matrixUser, list)
|
||||||
|
assertThat(result.map { it.userId }).containsExactly(
|
||||||
|
A_USER_ID_3,
|
||||||
|
A_USER_ID,
|
||||||
|
A_USER_ID_2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `three accounts, second is current, will return first current and last`() {
|
||||||
|
val sut = CurrentUserWithNeighborsBuilder()
|
||||||
|
val matrixUser = aMatrixUser(id = A_USER_ID_2.value)
|
||||||
|
val list = listOf(
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID.value,
|
||||||
|
),
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID_2.value,
|
||||||
|
),
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID_3.value,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val result = sut.build(matrixUser, list)
|
||||||
|
assertThat(result.map { it.userId }).containsExactly(
|
||||||
|
A_USER_ID,
|
||||||
|
A_USER_ID_2,
|
||||||
|
A_USER_ID_3,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `three accounts, current is last, will return middle, current and first`() {
|
||||||
|
val sut = CurrentUserWithNeighborsBuilder()
|
||||||
|
val matrixUser = aMatrixUser(id = A_USER_ID_3.value)
|
||||||
|
val list = listOf(
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID_2.value,
|
||||||
|
),
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID_3.value,
|
||||||
|
),
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID.value,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val result = sut.build(matrixUser, list)
|
||||||
|
assertThat(result.map { it.userId }).containsExactly(
|
||||||
|
A_USER_ID,
|
||||||
|
A_USER_ID_2,
|
||||||
|
A_USER_ID_3,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `one account, will return data from matrix user and not from db`() {
|
||||||
|
val sut = CurrentUserWithNeighborsBuilder()
|
||||||
|
val matrixUser = aMatrixUser(
|
||||||
|
id = A_USER_ID.value,
|
||||||
|
displayName = "Bob",
|
||||||
|
avatarUrl = "avatarUrl",
|
||||||
|
)
|
||||||
|
val list = listOf(
|
||||||
|
aSessionData(
|
||||||
|
sessionId = A_USER_ID.value,
|
||||||
|
userDisplayName = "Outdated Bob",
|
||||||
|
userAvatarUrl = "outdatedAvatarUrl",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val result = sut.build(matrixUser, list)
|
||||||
|
assertThat(result).containsExactly(
|
||||||
|
MatrixUser(
|
||||||
|
userId = A_USER_ID,
|
||||||
|
displayName = "Bob",
|
||||||
|
avatarUrl = "avatarUrl",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,9 @@ import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.sync.FakeSyncService
|
import io.element.android.libraries.matrix.test.sync.FakeSyncService
|
||||||
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
|
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
|
||||||
|
import io.element.android.libraries.sessionstorage.test.aSessionData
|
||||||
import io.element.android.tests.testutils.MutablePresenter
|
import io.element.android.tests.testutils.MutablePresenter
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.test
|
import io.element.android.tests.testutils.test
|
||||||
|
|
@ -54,17 +57,29 @@ class HomePresenterTest {
|
||||||
val presenter = createHomePresenter(
|
val presenter = createHomePresenter(
|
||||||
client = matrixClient,
|
client = matrixClient,
|
||||||
rageshakeFeatureAvailability = { flowOf(false) },
|
rageshakeFeatureAvailability = { flowOf(false) },
|
||||||
|
sessionStore = InMemorySessionStore(
|
||||||
|
initialList = listOf(
|
||||||
|
aSessionData(
|
||||||
|
sessionId = matrixClient.sessionId.value,
|
||||||
|
userDisplayName = null,
|
||||||
|
userAvatarUrl = null,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.matrixUser).isEqualTo(MatrixUser(A_USER_ID))
|
assertThat(initialState.currentUserAndNeighbors.first()).isEqualTo(
|
||||||
|
MatrixUser(A_USER_ID, null, null)
|
||||||
|
)
|
||||||
assertThat(initialState.canReportBug).isFalse()
|
assertThat(initialState.canReportBug).isFalse()
|
||||||
|
skipItems(1)
|
||||||
val withUserState = awaitItem()
|
val withUserState = awaitItem()
|
||||||
assertThat(withUserState.matrixUser.userId).isEqualTo(A_USER_ID)
|
assertThat(withUserState.currentUserAndNeighbors.first()).isEqualTo(
|
||||||
assertThat(withUserState.matrixUser.displayName).isEqualTo(A_USER_NAME)
|
MatrixUser(A_USER_ID, A_USER_NAME, AN_AVATAR_URL)
|
||||||
assertThat(withUserState.matrixUser.avatarUrl).isEqualTo(AN_AVATAR_URL)
|
)
|
||||||
assertThat(withUserState.showAvatarIndicator).isFalse()
|
assertThat(withUserState.showAvatarIndicator).isFalse()
|
||||||
assertThat(withUserState.isSpaceFeatureEnabled).isFalse()
|
assertThat(withUserState.isSpaceFeatureEnabled).isFalse()
|
||||||
assertThat(withUserState.showNavigationBar).isFalse()
|
assertThat(withUserState.showNavigationBar).isFalse()
|
||||||
|
|
@ -75,6 +90,9 @@ class HomePresenterTest {
|
||||||
fun `present - can report bug`() = runTest {
|
fun `present - can report bug`() = runTest {
|
||||||
val presenter = createHomePresenter(
|
val presenter = createHomePresenter(
|
||||||
rageshakeFeatureAvailability = { flowOf(true) },
|
rageshakeFeatureAvailability = { flowOf(true) },
|
||||||
|
sessionStore = InMemorySessionStore(
|
||||||
|
updateUserProfileResult = { _, _, _ -> },
|
||||||
|
),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -92,6 +110,9 @@ class HomePresenterTest {
|
||||||
featureFlagService = FakeFeatureFlagService(
|
featureFlagService = FakeFeatureFlagService(
|
||||||
initialState = mapOf(FeatureFlags.Space.key to true),
|
initialState = mapOf(FeatureFlags.Space.key to true),
|
||||||
),
|
),
|
||||||
|
sessionStore = InMemorySessionStore(
|
||||||
|
updateUserProfileResult = { _, _, _ -> },
|
||||||
|
),
|
||||||
)
|
)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
|
|
@ -105,6 +126,9 @@ class HomePresenterTest {
|
||||||
val indicatorService = FakeIndicatorService()
|
val indicatorService = FakeIndicatorService()
|
||||||
val presenter = createHomePresenter(
|
val presenter = createHomePresenter(
|
||||||
indicatorService = indicatorService,
|
indicatorService = indicatorService,
|
||||||
|
sessionStore = InMemorySessionStore(
|
||||||
|
updateUserProfileResult = { _, _, _ -> },
|
||||||
|
),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -124,19 +148,28 @@ class HomePresenterTest {
|
||||||
userAvatarUrl = null,
|
userAvatarUrl = null,
|
||||||
)
|
)
|
||||||
matrixClient.givenGetProfileResult(matrixClient.sessionId, Result.failure(AN_EXCEPTION))
|
matrixClient.givenGetProfileResult(matrixClient.sessionId, Result.failure(AN_EXCEPTION))
|
||||||
val presenter = createHomePresenter(client = matrixClient)
|
val presenter = createHomePresenter(
|
||||||
|
client = matrixClient,
|
||||||
|
sessionStore = InMemorySessionStore(
|
||||||
|
updateUserProfileResult = { _, _, _ -> },
|
||||||
|
),
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.matrixUser).isEqualTo(MatrixUser(matrixClient.sessionId))
|
assertThat(initialState.currentUserAndNeighbors.first()).isEqualTo(MatrixUser(matrixClient.sessionId))
|
||||||
// No new state is coming
|
// No new state is coming
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - NavigationBar change`() = runTest {
|
fun `present - NavigationBar change`() = runTest {
|
||||||
val presenter = createHomePresenter()
|
val presenter = createHomePresenter(
|
||||||
|
sessionStore = InMemorySessionStore(
|
||||||
|
updateUserProfileResult = { _, _, _ -> },
|
||||||
|
),
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -152,6 +185,9 @@ class HomePresenterTest {
|
||||||
fun `present - NavigationBar is hidden when the last space is left`() = runTest {
|
fun `present - NavigationBar is hidden when the last space is left`() = runTest {
|
||||||
val homeSpacesPresenter = MutablePresenter(aHomeSpacesState())
|
val homeSpacesPresenter = MutablePresenter(aHomeSpacesState())
|
||||||
val presenter = createHomePresenter(
|
val presenter = createHomePresenter(
|
||||||
|
sessionStore = InMemorySessionStore(
|
||||||
|
updateUserProfileResult = { _, _, _ -> },
|
||||||
|
),
|
||||||
featureFlagService = FakeFeatureFlagService(
|
featureFlagService = FakeFeatureFlagService(
|
||||||
initialState = mapOf(FeatureFlags.Space.key to true),
|
initialState = mapOf(FeatureFlags.Space.key to true),
|
||||||
),
|
),
|
||||||
|
|
@ -185,6 +221,7 @@ internal fun createHomePresenter(
|
||||||
indicatorService: IndicatorService = FakeIndicatorService(),
|
indicatorService: IndicatorService = FakeIndicatorService(),
|
||||||
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
|
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
|
||||||
homeSpacesPresenter: Presenter<HomeSpacesState> = Presenter { aHomeSpacesState() },
|
homeSpacesPresenter: Presenter<HomeSpacesState> = Presenter { aHomeSpacesState() },
|
||||||
|
sessionStore: SessionStore = InMemorySessionStore(),
|
||||||
) = HomePresenter(
|
) = HomePresenter(
|
||||||
client = client,
|
client = client,
|
||||||
syncService = syncService,
|
syncService = syncService,
|
||||||
|
|
@ -195,4 +232,5 @@ internal fun createHomePresenter(
|
||||||
homeSpacesPresenter = homeSpacesPresenter,
|
homeSpacesPresenter = homeSpacesPresenter,
|
||||||
rageshakeFeatureAvailability = rageshakeFeatureAvailability,
|
rageshakeFeatureAvailability = rageshakeFeatureAvailability,
|
||||||
featureFlagService = featureFlagService,
|
featureFlagService = featureFlagService,
|
||||||
|
sessionStore = sessionStore,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.invite.api.InviteData
|
import io.element.android.features.invite.api.InviteData
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
|
|
@ -21,7 +21,7 @@ import io.element.android.libraries.architecture.inputs
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class DeclineAndBlockNode(
|
class DeclineAndBlockNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.features.invite.api.InviteData
|
import io.element.android.features.invite.api.InviteData
|
||||||
import io.element.android.features.invite.impl.DeclineInvite
|
import io.element.android.features.invite.impl.DeclineInvite
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
|
|
@ -28,7 +28,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Inject
|
@AssistedInject
|
||||||
class DeclineAndBlockPresenter(
|
class DeclineAndBlockPresenter(
|
||||||
@Assisted private val inviteData: InviteData,
|
@Assisted private val inviteData: InviteData,
|
||||||
private val declineInvite: DeclineInvite,
|
private val declineInvite: DeclineInvite,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
<string name="screen_decline_and_block_block_user_option_title">"Блокиране на потребителя"</string>
|
<string name="screen_decline_and_block_block_user_option_title">"Блокиране на потребителя"</string>
|
||||||
<string name="screen_invites_decline_chat_message">"Сигурни ли сте, че искате да отхвърлите поканата за присъединяване в %1$s?"</string>
|
<string name="screen_invites_decline_chat_message">"Сигурни ли сте, че искате да отхвърлите поканата за присъединяване в %1$s?"</string>
|
||||||
<string name="screen_invites_decline_chat_title">"Отказване на покана"</string>
|
<string name="screen_invites_decline_chat_title">"Отказване на покана"</string>
|
||||||
|
<string name="screen_invites_decline_direct_chat_message">"Сигурни ли сте, че искате да откажете този личен чат с %1$s?"</string>
|
||||||
|
<string name="screen_invites_decline_direct_chat_title">"Отказване на чат"</string>
|
||||||
<string name="screen_invites_empty_list">"Няма покани"</string>
|
<string name="screen_invites_empty_list">"Няма покани"</string>
|
||||||
<string name="screen_invites_invited_you">"%1$s (%2$s) ви покани"</string>
|
<string name="screen_invites_invited_you">"%1$s (%2$s) ви покани"</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<string name="screen_decline_and_block_block_user_option_title">"Felhasználó letiltása"</string>
|
<string name="screen_decline_and_block_block_user_option_title">"Felhasználó letiltása"</string>
|
||||||
<string name="screen_decline_and_block_report_user_option_description">"A szoba jelentése a fiókszolgáltatójának."</string>
|
<string name="screen_decline_and_block_report_user_option_description">"A szoba jelentése a fiókszolgáltatójának."</string>
|
||||||
<string name="screen_decline_and_block_report_user_reason_placeholder">"Írja le a jelentés okát…"</string>
|
<string name="screen_decline_and_block_report_user_reason_placeholder">"Írja le a jelentés okát…"</string>
|
||||||
<string name="screen_decline_and_block_title">"Elutasítás és blokkolás"</string>
|
<string name="screen_decline_and_block_title">"Elutasítás és letiltás"</string>
|
||||||
<string name="screen_invites_decline_chat_message">"Biztos, hogy elutasítja a meghívást, hogy csatlakozzon ehhez: %1$s?"</string>
|
<string name="screen_invites_decline_chat_message">"Biztos, hogy elutasítja a meghívást, hogy csatlakozzon ehhez: %1$s?"</string>
|
||||||
<string name="screen_invites_decline_chat_title">"Meghívás elutasítása"</string>
|
<string name="screen_invites_decline_chat_title">"Meghívás elutasítása"</string>
|
||||||
<string name="screen_invites_decline_direct_chat_message">"Biztos, hogy elutasítja ezt a privát csevegést vele: %1$s?"</string>
|
<string name="screen_invites_decline_direct_chat_message">"Biztos, hogy elutasítja ezt a privát csevegést vele: %1$s?"</string>
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import dev.zacsweers.metro.Inject
|
|
||||||
import io.element.android.features.invitepeople.api.InvitePeopleEvents
|
import io.element.android.features.invitepeople.api.InvitePeopleEvents
|
||||||
import io.element.android.features.invitepeople.api.InvitePeoplePresenter
|
import io.element.android.features.invitepeople.api.InvitePeoplePresenter
|
||||||
import io.element.android.features.invitepeople.api.InvitePeopleState
|
import io.element.android.features.invitepeople.api.InvitePeopleState
|
||||||
|
|
@ -51,7 +51,7 @@ import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@Inject
|
@AssistedInject
|
||||||
class DefaultInvitePeoplePresenter(
|
class DefaultInvitePeoplePresenter(
|
||||||
@Assisted private val joinedRoom: JoinedRoom?,
|
@Assisted private val joinedRoom: JoinedRoom?,
|
||||||
@Assisted private val roomId: RoomId,
|
@Assisted private val roomId: RoomId,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.invite.api.InviteData
|
import io.element.android.features.invite.api.InviteData
|
||||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteView
|
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteView
|
||||||
|
|
@ -30,7 +30,7 @@ import io.element.android.libraries.di.SessionScope
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class JoinRoomFlowNode(
|
class JoinRoomFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||||
import io.element.android.features.invite.api.InviteData
|
import io.element.android.features.invite.api.InviteData
|
||||||
import io.element.android.features.invite.api.SeenInvitesStore
|
import io.element.android.features.invite.api.SeenInvitesStore
|
||||||
|
|
@ -59,7 +59,7 @@ import kotlinx.coroutines.launch
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
import kotlin.jvm.optionals.getOrNull
|
import kotlin.jvm.optionals.getOrNull
|
||||||
|
|
||||||
@Inject
|
@AssistedInject
|
||||||
class JoinRoomPresenter(
|
class JoinRoomPresenter(
|
||||||
@Assisted private val roomId: RoomId,
|
@Assisted private val roomId: RoomId,
|
||||||
@Assisted private val roomIdOrAlias: RoomIdOrAlias,
|
@Assisted private val roomIdOrAlias: RoomIdOrAlias,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
<string name="screen_join_room_fail_reason">"Tato místnost je buď určena pouze pro zvané, nebo do ní může být omezen přístup na úrovni prostoru."</string>
|
<string name="screen_join_room_fail_reason">"Tato místnost je buď určena pouze pro zvané, nebo do ní může být omezen přístup na úrovni prostoru."</string>
|
||||||
<string name="screen_join_room_forget_action">"Zapomenout na tuto místnost"</string>
|
<string name="screen_join_room_forget_action">"Zapomenout na tuto místnost"</string>
|
||||||
<string name="screen_join_room_invite_required_message">"Abyste se mohli připojit k této místnosti, potřebujete pozvánku."</string>
|
<string name="screen_join_room_invite_required_message">"Abyste se mohli připojit k této místnosti, potřebujete pozvánku."</string>
|
||||||
|
<string name="screen_join_room_invited_by">"Pozván(a)"</string>
|
||||||
<string name="screen_join_room_join_action">"Připojit se do místnosti"</string>
|
<string name="screen_join_room_join_action">"Připojit se do místnosti"</string>
|
||||||
<string name="screen_join_room_join_restricted_message">"Abyste se mohli připojit, musíte být pozváni nebo být členem některého prostoru."</string>
|
<string name="screen_join_room_join_restricted_message">"Abyste se mohli připojit, musíte být pozváni nebo být členem některého prostoru."</string>
|
||||||
<string name="screen_join_room_knock_action">"Zaklepejte a připojte se"</string>
|
<string name="screen_join_room_knock_action">"Zaklepejte a připojte se"</string>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
<string name="screen_join_room_fail_reason">"Dette rum er enten kun for gæster, eller der kan være sat begrænsninger for adgangen på klyngeniveau."</string>
|
<string name="screen_join_room_fail_reason">"Dette rum er enten kun for gæster, eller der kan være sat begrænsninger for adgangen på klyngeniveau."</string>
|
||||||
<string name="screen_join_room_forget_action">"Glem dette rum"</string>
|
<string name="screen_join_room_forget_action">"Glem dette rum"</string>
|
||||||
<string name="screen_join_room_invite_required_message">"Du har brug for en invitation for at deltage i dette rum"</string>
|
<string name="screen_join_room_invite_required_message">"Du har brug for en invitation for at deltage i dette rum"</string>
|
||||||
|
<string name="screen_join_room_invited_by">"Inviteret af"</string>
|
||||||
<string name="screen_join_room_join_action">"Deltag i rummet"</string>
|
<string name="screen_join_room_join_action">"Deltag i rummet"</string>
|
||||||
<string name="screen_join_room_join_restricted_message">"Du skal muligvis være inviteret eller være medlem af en klynge for at deltage."</string>
|
<string name="screen_join_room_join_restricted_message">"Du skal muligvis være inviteret eller være medlem af en klynge for at deltage."</string>
|
||||||
<string name="screen_join_room_knock_action">"Send anmodning om at deltage"</string>
|
<string name="screen_join_room_knock_action">"Send anmodning om at deltage"</string>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
<string name="screen_join_room_fail_reason">"Dieser Chat ist entweder nur auf Einladung zugänglich oder es gibt andere Zugangsbeschränkungen durch Spaces."</string>
|
<string name="screen_join_room_fail_reason">"Dieser Chat ist entweder nur auf Einladung zugänglich oder es gibt andere Zugangsbeschränkungen durch Spaces."</string>
|
||||||
<string name="screen_join_room_forget_action">"Vergiss diesen Chat"</string>
|
<string name="screen_join_room_forget_action">"Vergiss diesen Chat"</string>
|
||||||
<string name="screen_join_room_invite_required_message">"Du benötigst eine Einladung, um diesem Chat beizutreten"</string>
|
<string name="screen_join_room_invite_required_message">"Du benötigst eine Einladung, um diesem Chat beizutreten"</string>
|
||||||
|
<string name="screen_join_room_invited_by">"Eingeladen von"</string>
|
||||||
<string name="screen_join_room_join_action">"Chat beitreten"</string>
|
<string name="screen_join_room_join_action">"Chat beitreten"</string>
|
||||||
<string name="screen_join_room_join_restricted_message">"Möglicherweise musst du eingeladen werden oder ein Mitglied eines Spaces sein, um beitreten zu können."</string>
|
<string name="screen_join_room_join_restricted_message">"Möglicherweise musst du eingeladen werden oder ein Mitglied eines Spaces sein, um beitreten zu können."</string>
|
||||||
<string name="screen_join_room_knock_action">"Anklopfen"</string>
|
<string name="screen_join_room_knock_action">"Anklopfen"</string>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
<string name="screen_join_room_fail_reason">"Ligipääs siia jututuppa on võimalik vaid kutse alusel või kehtivad siin kogukonnakohased piirangud."</string>
|
<string name="screen_join_room_fail_reason">"Ligipääs siia jututuppa on võimalik vaid kutse alusel või kehtivad siin kogukonnakohased piirangud."</string>
|
||||||
<string name="screen_join_room_forget_action">"Unusta see jututuba"</string>
|
<string name="screen_join_room_forget_action">"Unusta see jututuba"</string>
|
||||||
<string name="screen_join_room_invite_required_message">"Selle jututoaga liitumiseks vajad sa kutset"</string>
|
<string name="screen_join_room_invite_required_message">"Selle jututoaga liitumiseks vajad sa kutset"</string>
|
||||||
|
<string name="screen_join_room_invited_by">"Kutsuja"</string>
|
||||||
<string name="screen_join_room_join_action">"Liitu jututoaga"</string>
|
<string name="screen_join_room_join_action">"Liitu jututoaga"</string>
|
||||||
<string name="screen_join_room_join_restricted_message">"Selle jututoaga liitumiseks sa vajad kutset või pead juba olema kogukonna liige."</string>
|
<string name="screen_join_room_join_restricted_message">"Selle jututoaga liitumiseks sa vajad kutset või pead juba olema kogukonna liige."</string>
|
||||||
<string name="screen_join_room_knock_action">"Liitumiseks koputa jututoa uksele"</string>
|
<string name="screen_join_room_knock_action">"Liitumiseks koputa jututoa uksele"</string>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
<string name="screen_join_room_fail_reason">"Ce salon est accessible uniquement sur invitation ou il peut y avoir des restrictions d’accès au niveau de l’espace."</string>
|
<string name="screen_join_room_fail_reason">"Ce salon est accessible uniquement sur invitation ou il peut y avoir des restrictions d’accès au niveau de l’espace."</string>
|
||||||
<string name="screen_join_room_forget_action">"Oublier ce salon"</string>
|
<string name="screen_join_room_forget_action">"Oublier ce salon"</string>
|
||||||
<string name="screen_join_room_invite_required_message">"Vous avez besoin d’une invitation pour rejoindre ce salon"</string>
|
<string name="screen_join_room_invite_required_message">"Vous avez besoin d’une invitation pour rejoindre ce salon"</string>
|
||||||
|
<string name="screen_join_room_invited_by">"Invité(e) par"</string>
|
||||||
<string name="screen_join_room_join_action">"Rejoindre"</string>
|
<string name="screen_join_room_join_action">"Rejoindre"</string>
|
||||||
<string name="screen_join_room_join_restricted_message">"Il est possible que vous deviez être invité ou être membre d’un Espace pour pouvoir rejoindre le salon."</string>
|
<string name="screen_join_room_join_restricted_message">"Il est possible que vous deviez être invité ou être membre d’un Espace pour pouvoir rejoindre le salon."</string>
|
||||||
<string name="screen_join_room_knock_action">"Demander à joindre"</string>
|
<string name="screen_join_room_knock_action">"Demander à joindre"</string>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
<string name="screen_join_room_fail_reason">"Ebbe a szobába csak meghívóval vagy tértagsággal lehet belépni."</string>
|
<string name="screen_join_room_fail_reason">"Ebbe a szobába csak meghívóval vagy tértagsággal lehet belépni."</string>
|
||||||
<string name="screen_join_room_forget_action">"Szoba elfelejtése"</string>
|
<string name="screen_join_room_forget_action">"Szoba elfelejtése"</string>
|
||||||
<string name="screen_join_room_invite_required_message">"Meghívóra van szüksége ahhoz, hogy csatlakozzon ehhez a szobához"</string>
|
<string name="screen_join_room_invite_required_message">"Meghívóra van szüksége ahhoz, hogy csatlakozzon ehhez a szobához"</string>
|
||||||
|
<string name="screen_join_room_invited_by">"Meghívta:"</string>
|
||||||
<string name="screen_join_room_join_action">"Csatlakozás a szobához"</string>
|
<string name="screen_join_room_join_action">"Csatlakozás a szobához"</string>
|
||||||
<string name="screen_join_room_join_restricted_message">"A csatlakozáshoz meghívásra vagy tértagságra lehet szüksége."</string>
|
<string name="screen_join_room_join_restricted_message">"A csatlakozáshoz meghívásra vagy tértagságra lehet szüksége."</string>
|
||||||
<string name="screen_join_room_knock_action">"Kopogtasson a csatlakozáshoz"</string>
|
<string name="screen_join_room_knock_action">"Kopogtasson a csatlakozáshoz"</string>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
<string name="screen_join_room_fail_reason">"A entrada nesta sala ou está limitada a convites ou a alguma configuração de espaço."</string>
|
<string name="screen_join_room_fail_reason">"A entrada nesta sala ou está limitada a convites ou a alguma configuração de espaço."</string>
|
||||||
<string name="screen_join_room_forget_action">"Esquecer esta sala"</string>
|
<string name="screen_join_room_forget_action">"Esquecer esta sala"</string>
|
||||||
<string name="screen_join_room_invite_required_message">"Precisas de um convite para entrares nesta sala"</string>
|
<string name="screen_join_room_invite_required_message">"Precisas de um convite para entrares nesta sala"</string>
|
||||||
|
<string name="screen_join_room_invited_by">"Convidado por"</string>
|
||||||
<string name="screen_join_room_join_action">"Entrar na sala"</string>
|
<string name="screen_join_room_join_action">"Entrar na sala"</string>
|
||||||
<string name="screen_join_room_join_restricted_message">"Podes ter que ser convidado ou pertenceres a um espaço para poderes entrar."</string>
|
<string name="screen_join_room_join_restricted_message">"Podes ter que ser convidado ou pertenceres a um espaço para poderes entrar."</string>
|
||||||
<string name="screen_join_room_knock_action">"Bater à porta"</string>
|
<string name="screen_join_room_knock_action">"Bater à porta"</string>
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,12 @@ import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.libraries.di.RoomScope
|
import io.element.android.libraries.di.RoomScope
|
||||||
|
|
||||||
@ContributesNode(RoomScope::class)
|
@ContributesNode(RoomScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class KnockRequestsListNode(
|
class KnockRequestsListNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="leave_room_alert_empty_subtitle">"Сигурни ли сте, че искате да напуснете тази стая? Вие сте единственият човек тук. Ако напуснете, никой няма да може да се присъедини в бъдеще, включително и вие."</string>
|
||||||
|
<string name="leave_room_alert_private_subtitle">"Сигурни ли сте, че искате да напуснете тази стая? Тази стая не е общодостъпна и няма да можете да се присъедините отново без покана."</string>
|
||||||
<string name="leave_room_alert_subtitle">"Сигурни ли сте, че искате да напуснете стаята?"</string>
|
<string name="leave_room_alert_subtitle">"Сигурни ли сте, че искате да напуснете стаята?"</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.licenses.impl.details.DependenciesDetailsNode
|
import io.element.android.features.licenses.impl.details.DependenciesDetailsNode
|
||||||
import io.element.android.features.licenses.impl.list.DependencyLicensesListNode
|
import io.element.android.features.licenses.impl.list.DependencyLicensesListNode
|
||||||
|
|
@ -28,7 +28,7 @@ import io.element.android.libraries.architecture.createNode
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class DependenciesFlowNode(
|
class DependenciesFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,14 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.licenses.impl.model.DependencyLicenseItem
|
import io.element.android.features.licenses.impl.model.DependencyLicenseItem
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
import io.element.android.libraries.architecture.inputs
|
import io.element.android.libraries.architecture.inputs
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class DependenciesDetailsNode(
|
class DependenciesDetailsNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,12 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.licenses.impl.model.DependencyLicenseItem
|
import io.element.android.features.licenses.impl.model.DependencyLicenseItem
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class DependencyLicensesListNode(
|
class DependencyLicensesListNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@ import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import dev.zacsweers.metro.Inject
|
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@Inject
|
@AssistedInject
|
||||||
class DefaultPermissionsPresenter(
|
class DefaultPermissionsPresenter(
|
||||||
@Assisted private val permissions: List<String>
|
@Assisted private val permissions: List<String>
|
||||||
) : PermissionsPresenter {
|
) : PermissionsPresenter {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.MobileScreen
|
import im.vector.app.features.analytics.plan.MobileScreen
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
|
|
@ -24,7 +24,7 @@ import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
|
|
||||||
@ContributesNode(RoomScope::class)
|
@ContributesNode(RoomScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class SendLocationNode(
|
class SendLocationNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.Composer
|
import im.vector.app.features.analytics.plan.Composer
|
||||||
import io.element.android.features.location.impl.common.MapDefaults
|
import io.element.android.features.location.impl.common.MapDefaults
|
||||||
import io.element.android.features.location.impl.common.actions.LocationActions
|
import io.element.android.features.location.impl.common.actions.LocationActions
|
||||||
|
|
@ -36,7 +36,7 @@ import io.element.android.libraries.textcomposer.model.MessageComposerMode
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Inject
|
@AssistedInject
|
||||||
class SendLocationPresenter(
|
class SendLocationPresenter(
|
||||||
permissionsPresenterFactory: PermissionsPresenter.Factory,
|
permissionsPresenterFactory: PermissionsPresenter.Factory,
|
||||||
private val room: JoinedRoom,
|
private val room: JoinedRoom,
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.MobileScreen
|
import im.vector.app.features.analytics.plan.MobileScreen
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.location.api.ShowLocationEntryPoint
|
import io.element.android.features.location.api.ShowLocationEntryPoint
|
||||||
|
|
@ -23,7 +23,7 @@ import io.element.android.libraries.di.RoomScope
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
|
|
||||||
@ContributesNode(RoomScope::class)
|
@ContributesNode(RoomScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class ShowLocationNode(
|
class ShowLocationNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
import io.element.android.features.location.impl.common.MapDefaults
|
import io.element.android.features.location.impl.common.MapDefaults
|
||||||
import io.element.android.features.location.impl.common.actions.LocationActions
|
import io.element.android.features.location.impl.common.actions.LocationActions
|
||||||
|
|
@ -26,7 +26,7 @@ import io.element.android.features.location.impl.common.permissions.PermissionsS
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
|
|
||||||
@Inject
|
@AssistedInject
|
||||||
class ShowLocationPresenter(
|
class ShowLocationPresenter(
|
||||||
@Assisted private val location: Location,
|
@Assisted private val location: Location,
|
||||||
@Assisted private val description: String?,
|
@Assisted private val description: String?,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
|
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
|
||||||
import io.element.android.features.lockscreen.impl.settings.LockScreenSettingsFlowNode
|
import io.element.android.features.lockscreen.impl.settings.LockScreenSettingsFlowNode
|
||||||
|
|
@ -29,7 +29,7 @@ import io.element.android.libraries.di.SessionScope
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class LockScreenFlowNode(
|
class LockScreenFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.newRoot
|
import com.bumble.appyx.navmodel.backstack.operation.newRoot
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCallback
|
import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCallback
|
||||||
import io.element.android.features.lockscreen.impl.pin.PinCodeManager
|
import io.element.android.features.lockscreen.impl.pin.PinCodeManager
|
||||||
|
|
@ -35,7 +35,7 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class LockScreenSettingsFlowNode(
|
class LockScreenSettingsFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,12 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class LockScreenSettingsNode(
|
class LockScreenSettingsNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import com.bumble.appyx.core.plugin.plugins
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.newRoot
|
import com.bumble.appyx.navmodel.backstack.operation.newRoot
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.lockscreen.impl.biometric.BiometricAuthenticatorManager
|
import io.element.android.features.lockscreen.impl.biometric.BiometricAuthenticatorManager
|
||||||
import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCallback
|
import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCallback
|
||||||
|
|
@ -32,7 +32,7 @@ import io.element.android.libraries.di.SessionScope
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class LockScreenSetupFlowNode(
|
class LockScreenSetupFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,12 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class SetupBiometricNode(
|
class SetupBiometricNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,12 @@ import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class SetupPinNode(
|
class SetupPinNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,12 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class PinUnlockNode(
|
class PinUnlockNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_app_lock_biometric_authentication">"биометрично удостоверяване"</string>
|
||||||
|
<string name="screen_app_lock_biometric_unlock">"биометрично отключване"</string>
|
||||||
|
<string name="screen_app_lock_biometric_unlock_title_android">"Отключване с биометрия"</string>
|
||||||
|
<string name="screen_app_lock_confirm_biometric_authentication_android">"Потвърдете биометричните данни"</string>
|
||||||
<string name="screen_app_lock_forgot_pin">"Забравихте PIN?"</string>
|
<string name="screen_app_lock_forgot_pin">"Забравихте PIN?"</string>
|
||||||
<string name="screen_app_lock_settings_change_pin">"Промяна на PIN кода"</string>
|
<string name="screen_app_lock_settings_change_pin">"Промяна на PIN кода"</string>
|
||||||
|
<string name="screen_app_lock_settings_enable_biometric_unlock">"Разрешаване на биометрично отключване"</string>
|
||||||
<string name="screen_app_lock_settings_remove_pin">"Премахване на PIN"</string>
|
<string name="screen_app_lock_settings_remove_pin">"Премахване на PIN"</string>
|
||||||
<string name="screen_app_lock_settings_remove_pin_alert_message">"Сигурни ли сте, че искате да премахнете PIN?"</string>
|
<string name="screen_app_lock_settings_remove_pin_alert_message">"Сигурни ли сте, че искате да премахнете PIN?"</string>
|
||||||
<string name="screen_app_lock_settings_remove_pin_alert_title">"Премахване на PIN?"</string>
|
<string name="screen_app_lock_settings_remove_pin_alert_title">"Премахване на PIN?"</string>
|
||||||
|
|
@ -9,6 +14,10 @@
|
||||||
<string name="screen_app_lock_setup_biometric_unlock_skip">"Предпочитам да използвам PIN"</string>
|
<string name="screen_app_lock_setup_biometric_unlock_skip">"Предпочитам да използвам PIN"</string>
|
||||||
<string name="screen_app_lock_setup_choose_pin">"Избор на PIN"</string>
|
<string name="screen_app_lock_setup_choose_pin">"Избор на PIN"</string>
|
||||||
<string name="screen_app_lock_setup_confirm_pin">"Потвърждаване на PIN"</string>
|
<string name="screen_app_lock_setup_confirm_pin">"Потвърждаване на PIN"</string>
|
||||||
|
<string name="screen_app_lock_setup_pin_context">"Заключете %1$s, за да добавите допълнителна сигурност към вашите чатове.
|
||||||
|
|
||||||
|
Изберете нещо запомнящо се. Ако забравите този PIN, ще бъдете излезли от приложението."</string>
|
||||||
|
<string name="screen_app_lock_setup_pin_forbidden_dialog_content">"Не можете да изберете това за ваш PIN код от съображения за сигурност"</string>
|
||||||
<string name="screen_app_lock_setup_pin_forbidden_dialog_title">"Избор на различен PIN"</string>
|
<string name="screen_app_lock_setup_pin_forbidden_dialog_title">"Избор на различен PIN"</string>
|
||||||
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Моля, въведете един и същ PIN два пъти"</string>
|
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Моля, въведете един и същ PIN два пъти"</string>
|
||||||
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"PINs не съвпадат"</string>
|
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"PINs не съвпадат"</string>
|
||||||
|
|
@ -20,6 +29,7 @@
|
||||||
<item quantity="one">"Грешен PIN. Имате още %1$d шанс"</item>
|
<item quantity="one">"Грешен PIN. Имате още %1$d шанс"</item>
|
||||||
<item quantity="other">"Грешен PIN. Имате още %1$d шанса"</item>
|
<item quantity="other">"Грешен PIN. Имате още %1$d шанса"</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<string name="screen_app_lock_use_biometric_android">"Използване на биометрия"</string>
|
||||||
<string name="screen_app_lock_use_pin_android">"Използване на PIN"</string>
|
<string name="screen_app_lock_use_pin_android">"Използване на PIN"</string>
|
||||||
<string name="screen_signout_in_progress_dialog_content">"Излизане…"</string>
|
<string name="screen_signout_in_progress_dialog_content">"Излизане…"</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ dependencies {
|
||||||
implementation(projects.libraries.testtags)
|
implementation(projects.libraries.testtags)
|
||||||
implementation(projects.libraries.uiStrings)
|
implementation(projects.libraries.uiStrings)
|
||||||
implementation(projects.libraries.permissions.api)
|
implementation(projects.libraries.permissions.api)
|
||||||
|
implementation(projects.libraries.sessionStorage.api)
|
||||||
implementation(projects.libraries.qrcode)
|
implementation(projects.libraries.qrcode)
|
||||||
implementation(projects.libraries.oidc.api)
|
implementation(projects.libraries.oidc.api)
|
||||||
implementation(projects.libraries.uiUtils)
|
implementation(projects.libraries.uiUtils)
|
||||||
|
|
@ -56,5 +57,6 @@ dependencies {
|
||||||
testImplementation(projects.libraries.matrix.test)
|
testImplementation(projects.libraries.matrix.test)
|
||||||
testImplementation(projects.libraries.oidc.test)
|
testImplementation(projects.libraries.oidc.test)
|
||||||
testImplementation(projects.libraries.permissions.test)
|
testImplementation(projects.libraries.permissions.test)
|
||||||
|
testImplementation(projects.libraries.sessionStorage.test)
|
||||||
testImplementation(projects.libraries.wellknown.test)
|
testImplementation(projects.libraries.wellknown.test)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.singleTop
|
import com.bumble.appyx.navmodel.backstack.operation.singleTop
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.login.api.LoginEntryPoint
|
import io.element.android.features.login.api.LoginEntryPoint
|
||||||
|
|
@ -51,7 +51,7 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class LoginFlowNode(
|
class LoginFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
@ -87,7 +87,7 @@ class LoginFlowNode(
|
||||||
// by pressing back or by closing the Custom Chrome Tab.
|
// by pressing back or by closing the Custom Chrome Tab.
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
delay(5000)
|
delay(5000)
|
||||||
oidcActionFlow.post(OidcAction.GoBack)
|
oidcActionFlow.post(OidcAction.GoBack(toUnblock = true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,9 +94,14 @@ class LoginHelper(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun onOidcAction(oidcAction: OidcAction) {
|
private suspend fun onOidcAction(oidcAction: OidcAction) {
|
||||||
|
if (oidcAction is OidcAction.GoBack && oidcAction.toUnblock && loginModeState.value !is AsyncData.Loading) {
|
||||||
|
// Ignore GoBack action if the current state is not Loading. This GoBack action is coming from LoginFlowNode.
|
||||||
|
// This can happen if there is an error, for instance attempt to login again on the same account.
|
||||||
|
return
|
||||||
|
}
|
||||||
loginModeState.value = AsyncData.Loading()
|
loginModeState.value = AsyncData.Loading()
|
||||||
when (oidcAction) {
|
when (oidcAction) {
|
||||||
OidcAction.GoBack -> {
|
is OidcAction.GoBack -> {
|
||||||
authenticationService.cancelOidcLogin()
|
authenticationService.cancelOidcLogin()
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
loginModeState.value = AsyncData.Uninitialized
|
loginModeState.value = AsyncData.Uninitialized
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import io.element.android.features.login.impl.R
|
import io.element.android.features.login.impl.R
|
||||||
import io.element.android.features.login.impl.dialogs.SlidingSyncNotSupportedDialog
|
import io.element.android.features.login.impl.dialogs.SlidingSyncNotSupportedDialog
|
||||||
import io.element.android.features.login.impl.error.ChangeServerError
|
import io.element.android.features.login.impl.error.ChangeServerError
|
||||||
import io.element.android.features.login.impl.error.ChangeServerErrorProvider
|
|
||||||
import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported
|
import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported
|
||||||
import io.element.android.libraries.androidutils.system.openGooglePlay
|
import io.element.android.libraries.androidutils.system.openGooglePlay
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
@ -23,6 +22,7 @@ import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.LocalBuildMeta
|
import io.element.android.libraries.designsystem.theme.LocalBuildMeta
|
||||||
|
import io.element.android.libraries.matrix.api.auth.AuthenticationException
|
||||||
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
|
|
@ -89,6 +89,12 @@ fun LoginModeView(
|
||||||
onSubmit = onClearError,
|
onSubmit = onClearError,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is AuthenticationException.AccountAlreadyLoggedIn -> {
|
||||||
|
ErrorDialog(
|
||||||
|
content = stringResource(CommonStrings.error_account_already_logged_in, error.message.orEmpty()),
|
||||||
|
onSubmit = onClearError,
|
||||||
|
)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
ErrorDialog(
|
ErrorDialog(
|
||||||
content = stringResource(CommonStrings.error_unknown),
|
content = stringResource(CommonStrings.error_unknown),
|
||||||
|
|
@ -113,7 +119,7 @@ fun LoginModeView(
|
||||||
|
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun LoginModeViewPreview(@PreviewParameter(ChangeServerErrorProvider::class) error: ChangeServerError) {
|
internal fun LoginModeViewPreview(@PreviewParameter(LoginModeViewErrorProvider::class) error: Throwable) {
|
||||||
ElementPreview {
|
ElementPreview {
|
||||||
LoginModeView(
|
LoginModeView(
|
||||||
loginMode = AsyncData.Failure(error),
|
loginMode = AsyncData.Failure(error),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.login.impl.login
|
||||||
|
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import io.element.android.features.login.impl.error.ChangeServerErrorProvider
|
||||||
|
import io.element.android.libraries.matrix.api.auth.AuthenticationException
|
||||||
|
|
||||||
|
class LoginModeViewErrorProvider : PreviewParameterProvider<Exception> {
|
||||||
|
override val values: Sequence<Exception>
|
||||||
|
get() = ChangeServerErrorProvider().values +
|
||||||
|
AuthenticationException.AccountAlreadyLoggedIn("@alice:matrix.org")
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,7 @@ import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.replace
|
import com.bumble.appyx.navmodel.backstack.operation.replace
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.login.impl.di.QrCodeLoginBindings
|
import io.element.android.features.login.impl.di.QrCodeLoginBindings
|
||||||
import io.element.android.features.login.impl.di.QrCodeLoginGraph
|
import io.element.android.features.login.impl.di.QrCodeLoginGraph
|
||||||
|
|
@ -49,7 +49,7 @@ import kotlinx.parcelize.Parcelize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class QrCodeLoginFlowNode(
|
class QrCodeLoginFlowNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,12 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.login.impl.util.openLearnMorePage
|
import io.element.android.features.login.impl.util.openLearnMorePage
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class ChangeAccountProviderNode(
|
class ChangeAccountProviderNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.login.impl.util.openLearnMorePage
|
import io.element.android.features.login.impl.util.openLearnMorePage
|
||||||
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class ChooseAccountProviderNode(
|
class ChooseAccountProviderNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.login.impl.util.openLearnMorePage
|
import io.element.android.features.login.impl.util.openLearnMorePage
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
|
|
@ -24,7 +24,7 @@ import io.element.android.libraries.architecture.inputs
|
||||||
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class ConfirmAccountProviderNode(
|
class ConfirmAccountProviderNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,12 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
|
||||||
import io.element.android.features.login.impl.login.LoginHelper
|
import io.element.android.features.login.impl.login.LoginHelper
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
|
||||||
@Inject
|
@AssistedInject
|
||||||
class ConfirmAccountProviderPresenter(
|
class ConfirmAccountProviderPresenter(
|
||||||
@Assisted private val params: Params,
|
@Assisted private val params: Params,
|
||||||
private val accountProviderDataSource: AccountProviderDataSource,
|
private val accountProviderDataSource: AccountProviderDataSource,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
|
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
|
||||||
|
|
@ -24,7 +24,7 @@ import io.element.android.libraries.architecture.NodeInputs
|
||||||
import io.element.android.libraries.architecture.inputs
|
import io.element.android.libraries.architecture.inputs
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class CreateAccountNode(
|
class CreateAccountNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.data.tryOrNull
|
import io.element.android.libraries.core.data.tryOrNull
|
||||||
|
|
@ -31,7 +31,7 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@Inject
|
@AssistedInject
|
||||||
class CreateAccountPresenter(
|
class CreateAccountPresenter(
|
||||||
@Assisted private val url: String,
|
@Assisted private val url: String,
|
||||||
private val authenticationService: MatrixAuthenticationService,
|
private val authenticationService: MatrixAuthenticationService,
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class LoginPasswordNode(
|
class LoginPasswordNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.login.impl.util.openLearnMorePage
|
import io.element.android.features.login.impl.util.openLearnMorePage
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
|
|
@ -24,7 +24,7 @@ import io.element.android.libraries.architecture.inputs
|
||||||
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
||||||
|
|
||||||
@ContributesNode(AppScope::class)
|
@ContributesNode(AppScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class OnBoardingNode(
|
class OnBoardingNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
@ -97,6 +97,7 @@ class OnBoardingNode(
|
||||||
onNeedLoginPassword = ::onLoginPasswordNeeded,
|
onNeedLoginPassword = ::onLoginPasswordNeeded,
|
||||||
onLearnMoreClick = { openLearnMorePage(context) },
|
onLearnMoreClick = { openLearnMorePage(context) },
|
||||||
onCreateAccountContinue = ::onCreateAccountContinue,
|
onCreateAccountContinue = ::onCreateAccountContinue,
|
||||||
|
onBackClick = ::navigateUp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.appconfig.OnBoardingConfig
|
import io.element.android.appconfig.OnBoardingConfig
|
||||||
import io.element.android.features.enterprise.api.EnterpriseService
|
import io.element.android.features.enterprise.api.EnterpriseService
|
||||||
import io.element.android.features.enterprise.api.canConnectToAnyHomeserver
|
import io.element.android.features.enterprise.api.canConnectToAnyHomeserver
|
||||||
|
|
@ -27,9 +27,10 @@ import io.element.android.features.login.impl.login.LoginHelper
|
||||||
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
import io.element.android.libraries.ui.utils.MultipleTapToUnlock
|
import io.element.android.libraries.ui.utils.MultipleTapToUnlock
|
||||||
|
|
||||||
@Inject
|
@AssistedInject
|
||||||
class OnBoardingPresenter(
|
class OnBoardingPresenter(
|
||||||
@Assisted private val params: OnBoardingNode.Params,
|
@Assisted private val params: OnBoardingNode.Params,
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
|
|
@ -38,6 +39,7 @@ class OnBoardingPresenter(
|
||||||
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
|
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
|
||||||
private val loginHelper: LoginHelper,
|
private val loginHelper: LoginHelper,
|
||||||
private val onBoardingLogoResIdProvider: OnBoardingLogoResIdProvider,
|
private val onBoardingLogoResIdProvider: OnBoardingLogoResIdProvider,
|
||||||
|
private val sessionStore: SessionStore,
|
||||||
) : Presenter<OnBoardingState> {
|
) : Presenter<OnBoardingState> {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
|
|
@ -86,6 +88,10 @@ class OnBoardingPresenter(
|
||||||
val onBoardingLogoResId = remember {
|
val onBoardingLogoResId = remember {
|
||||||
onBoardingLogoResIdProvider.get()
|
onBoardingLogoResIdProvider.get()
|
||||||
}
|
}
|
||||||
|
val isAddingAccount by produceState(initialValue = false) {
|
||||||
|
// We are adding an account if there is at least one session already stored
|
||||||
|
value = sessionStore.getAllSessions().isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
val loginMode by loginHelper.collectLoginMode()
|
val loginMode by loginHelper.collectLoginMode()
|
||||||
|
|
||||||
|
|
@ -109,6 +115,7 @@ class OnBoardingPresenter(
|
||||||
}
|
}
|
||||||
|
|
||||||
return OnBoardingState(
|
return OnBoardingState(
|
||||||
|
isAddingAccount = isAddingAccount,
|
||||||
productionApplicationName = buildMeta.productionApplicationName,
|
productionApplicationName = buildMeta.productionApplicationName,
|
||||||
defaultAccountProvider = defaultAccountProvider,
|
defaultAccountProvider = defaultAccountProvider,
|
||||||
mustChooseAccountProvider = mustChooseAccountProvider,
|
mustChooseAccountProvider = mustChooseAccountProvider,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import io.element.android.features.login.impl.login.LoginMode
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
||||||
data class OnBoardingState(
|
data class OnBoardingState(
|
||||||
|
val isAddingAccount: Boolean,
|
||||||
val productionApplicationName: String,
|
val productionApplicationName: String,
|
||||||
val defaultAccountProvider: String?,
|
val defaultAccountProvider: String?,
|
||||||
val mustChooseAccountProvider: Boolean,
|
val mustChooseAccountProvider: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,16 @@ open class OnBoardingStateProvider : PreviewParameterProvider<OnBoardingState> {
|
||||||
anOnBoardingState(canLoginWithQrCode = true, canCreateAccount = true, canReportBug = true),
|
anOnBoardingState(canLoginWithQrCode = true, canCreateAccount = true, canReportBug = true),
|
||||||
anOnBoardingState(defaultAccountProvider = "element.io", canCreateAccount = false, canReportBug = true),
|
anOnBoardingState(defaultAccountProvider = "element.io", canCreateAccount = false, canReportBug = true),
|
||||||
anOnBoardingState(customLogoResId = R.drawable.sample_background),
|
anOnBoardingState(customLogoResId = R.drawable.sample_background),
|
||||||
|
anOnBoardingState(
|
||||||
|
isAddingAccount = true,
|
||||||
|
canLoginWithQrCode = true,
|
||||||
|
canCreateAccount = true,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun anOnBoardingState(
|
fun anOnBoardingState(
|
||||||
|
isAddingAccount: Boolean = false,
|
||||||
productionApplicationName: String = "Element",
|
productionApplicationName: String = "Element",
|
||||||
defaultAccountProvider: String? = null,
|
defaultAccountProvider: String? = null,
|
||||||
mustChooseAccountProvider: Boolean = false,
|
mustChooseAccountProvider: Boolean = false,
|
||||||
|
|
@ -39,6 +45,7 @@ fun anOnBoardingState(
|
||||||
loginMode: AsyncData<LoginMode> = AsyncData.Uninitialized,
|
loginMode: AsyncData<LoginMode> = AsyncData.Uninitialized,
|
||||||
eventSink: (OnBoardingEvents) -> Unit = {},
|
eventSink: (OnBoardingEvents) -> Unit = {},
|
||||||
) = OnBoardingState(
|
) = OnBoardingState(
|
||||||
|
isAddingAccount = isAddingAccount,
|
||||||
productionApplicationName = productionApplicationName,
|
productionApplicationName = productionApplicationName,
|
||||||
defaultAccountProvider = defaultAccountProvider,
|
defaultAccountProvider = defaultAccountProvider,
|
||||||
mustChooseAccountProvider = mustChooseAccountProvider,
|
mustChooseAccountProvider = mustChooseAccountProvider,
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,9 @@ import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtom
|
import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtom
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtomSize
|
import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtomSize
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
|
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
|
||||||
|
import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage
|
||||||
import io.element.android.libraries.designsystem.atomic.pages.OnBoardingPage
|
import io.element.android.libraries.designsystem.atomic.pages.OnBoardingPage
|
||||||
|
import io.element.android.libraries.designsystem.components.BigIcon
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.Button
|
import io.element.android.libraries.designsystem.theme.components.Button
|
||||||
|
|
@ -58,6 +60,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
@Composable
|
@Composable
|
||||||
fun OnBoardingView(
|
fun OnBoardingView(
|
||||||
state: OnBoardingState,
|
state: OnBoardingState,
|
||||||
|
onBackClick: () -> Unit,
|
||||||
onSignInWithQrCode: () -> Unit,
|
onSignInWithQrCode: () -> Unit,
|
||||||
onSignIn: (mustChooseAccountProvider: Boolean) -> Unit,
|
onSignIn: (mustChooseAccountProvider: Boolean) -> Unit,
|
||||||
onCreateAccount: () -> Unit,
|
onCreateAccount: () -> Unit,
|
||||||
|
|
@ -67,6 +70,52 @@ fun OnBoardingView(
|
||||||
onCreateAccountContinue: (url: String) -> Unit,
|
onCreateAccountContinue: (url: String) -> Unit,
|
||||||
onReportProblem: () -> Unit,
|
onReportProblem: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val loginView = @Composable {
|
||||||
|
LoginModeView(
|
||||||
|
loginMode = state.loginMode,
|
||||||
|
onClearError = {
|
||||||
|
state.eventSink(OnBoardingEvents.ClearError)
|
||||||
|
},
|
||||||
|
onLearnMoreClick = onLearnMoreClick,
|
||||||
|
onOidcDetails = onOidcDetails,
|
||||||
|
onNeedLoginPassword = onNeedLoginPassword,
|
||||||
|
onCreateAccountContinue = onCreateAccountContinue,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val buttons = @Composable {
|
||||||
|
OnBoardingButtons(
|
||||||
|
state = state,
|
||||||
|
onSignInWithQrCode = onSignInWithQrCode,
|
||||||
|
onSignIn = onSignIn,
|
||||||
|
onCreateAccount = onCreateAccount,
|
||||||
|
onReportProblem = onReportProblem,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.isAddingAccount) {
|
||||||
|
AddOtherAccountScaffold(
|
||||||
|
modifier = modifier,
|
||||||
|
loginView = loginView,
|
||||||
|
buttons = buttons,
|
||||||
|
onBackClick = onBackClick,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
AddFirstAccountScaffold(
|
||||||
|
modifier = modifier,
|
||||||
|
state = state,
|
||||||
|
loginView = loginView,
|
||||||
|
buttons = buttons,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun AddFirstAccountScaffold(
|
||||||
|
state: OnBoardingState,
|
||||||
|
loginView: @Composable () -> Unit,
|
||||||
|
buttons: @Composable () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
OnBoardingPage(
|
OnBoardingPage(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -79,29 +128,31 @@ fun OnBoardingView(
|
||||||
} else {
|
} else {
|
||||||
OnBoardingContent(state = state)
|
OnBoardingContent(state = state)
|
||||||
}
|
}
|
||||||
LoginModeView(
|
loginView()
|
||||||
loginMode = state.loginMode,
|
|
||||||
onClearError = {
|
|
||||||
state.eventSink(OnBoardingEvents.ClearError)
|
|
||||||
},
|
|
||||||
onLearnMoreClick = onLearnMoreClick,
|
|
||||||
onOidcDetails = onOidcDetails,
|
|
||||||
onNeedLoginPassword = onNeedLoginPassword,
|
|
||||||
onCreateAccountContinue = onCreateAccountContinue,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
footer = {
|
footer = {
|
||||||
OnBoardingButtons(
|
buttons()
|
||||||
state = state,
|
|
||||||
onSignInWithQrCode = onSignInWithQrCode,
|
|
||||||
onSignIn = onSignIn,
|
|
||||||
onCreateAccount = onCreateAccount,
|
|
||||||
onReportProblem = onReportProblem,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun AddOtherAccountScaffold(
|
||||||
|
loginView: @Composable () -> Unit,
|
||||||
|
buttons: @Composable () -> Unit,
|
||||||
|
onBackClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
FlowStepPage(
|
||||||
|
modifier = modifier,
|
||||||
|
title = stringResource(CommonStrings.common_add_account),
|
||||||
|
iconStyle = BigIcon.Style.Default(CompoundIcons.HomeSolid()),
|
||||||
|
buttons = { buttons() },
|
||||||
|
content = loginView,
|
||||||
|
onBackClick = onBackClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun OnBoardingContent(state: OnBoardingState) {
|
private fun OnBoardingContent(state: OnBoardingState) {
|
||||||
Box(
|
Box(
|
||||||
|
|
@ -226,27 +277,29 @@ private fun OnBoardingButtons(
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (state.canReportBug) {
|
if (state.isAddingAccount.not()) {
|
||||||
// Add a report problem text button. Use a Text since we need a special theme here.
|
if (state.canReportBug) {
|
||||||
Text(
|
// Add a report problem text button. Use a Text since we need a special theme here.
|
||||||
modifier = Modifier
|
Text(
|
||||||
.clickable(onClick = onReportProblem)
|
modifier = Modifier
|
||||||
.padding(16.dp),
|
.clickable(onClick = onReportProblem)
|
||||||
text = stringResource(id = CommonStrings.common_report_a_problem),
|
.padding(16.dp),
|
||||||
style = ElementTheme.typography.fontBodySmRegular,
|
text = stringResource(id = CommonStrings.common_report_a_problem),
|
||||||
color = ElementTheme.colors.textSecondary,
|
style = ElementTheme.typography.fontBodySmRegular,
|
||||||
)
|
color = ElementTheme.colors.textSecondary,
|
||||||
} else {
|
)
|
||||||
Text(
|
} else {
|
||||||
modifier = Modifier
|
Text(
|
||||||
.clickable {
|
modifier = Modifier
|
||||||
state.eventSink(OnBoardingEvents.OnVersionClick)
|
.clickable {
|
||||||
}
|
state.eventSink(OnBoardingEvents.OnVersionClick)
|
||||||
.padding(16.dp),
|
}
|
||||||
text = stringResource(id = R.string.screen_onboarding_app_version, state.version),
|
.padding(16.dp),
|
||||||
style = ElementTheme.typography.fontBodySmRegular,
|
text = stringResource(id = R.string.screen_onboarding_app_version, state.version),
|
||||||
color = ElementTheme.colors.textSecondary,
|
style = ElementTheme.typography.fontBodySmRegular,
|
||||||
)
|
color = ElementTheme.colors.textSecondary,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -258,6 +311,7 @@ internal fun OnBoardingViewPreview(
|
||||||
) = ElementPreview {
|
) = ElementPreview {
|
||||||
OnBoardingView(
|
OnBoardingView(
|
||||||
state = state,
|
state = state,
|
||||||
|
onBackClick = {},
|
||||||
onSignInWithQrCode = {},
|
onSignInWithQrCode = {},
|
||||||
onSignIn = {},
|
onSignIn = {},
|
||||||
onCreateAccount = {},
|
onCreateAccount = {},
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,13 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.login.impl.di.QrCodeLoginScope
|
import io.element.android.features.login.impl.di.QrCodeLoginScope
|
||||||
import io.element.android.libraries.architecture.inputs
|
import io.element.android.libraries.architecture.inputs
|
||||||
|
|
||||||
@ContributesNode(QrCodeLoginScope::class)
|
@ContributesNode(QrCodeLoginScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class QrCodeConfirmationNode(
|
class QrCodeConfirmationNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.login.impl.di.QrCodeLoginScope
|
import io.element.android.features.login.impl.di.QrCodeLoginScope
|
||||||
import io.element.android.features.login.impl.qrcode.QrCodeErrorScreenType
|
import io.element.android.features.login.impl.qrcode.QrCodeErrorScreenType
|
||||||
|
|
@ -22,7 +22,7 @@ import io.element.android.libraries.architecture.inputs
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
|
|
||||||
@ContributesNode(QrCodeLoginScope::class)
|
@ContributesNode(QrCodeLoginScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class QrCodeErrorNode(
|
class QrCodeErrorNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,12 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.login.impl.di.QrCodeLoginScope
|
import io.element.android.features.login.impl.di.QrCodeLoginScope
|
||||||
|
|
||||||
@ContributesNode(QrCodeLoginScope::class)
|
@ContributesNode(QrCodeLoginScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class QrCodeIntroNode(
|
class QrCodeIntroNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,13 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.login.impl.di.QrCodeLoginScope
|
import io.element.android.features.login.impl.di.QrCodeLoginScope
|
||||||
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData
|
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData
|
||||||
|
|
||||||
@ContributesNode(QrCodeLoginScope::class)
|
@ContributesNode(QrCodeLoginScope::class)
|
||||||
@Inject
|
@AssistedInject
|
||||||
class QrCodeScanNode(
|
class QrCodeScanNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue