Merge pull request #5166 from element-hq/feature/fga/create_room_flow_rework
Create room flow rework
This commit is contained in:
@@ -3,18 +3,18 @@ appId: ${MAESTRO_APP_ID}
|
||||
# Purpose: Test the creation and deletion of a room
|
||||
- tapOn: "Create a new conversation or room"
|
||||
- tapOn: "New room"
|
||||
- tapOn: "Search for someone"
|
||||
- inputText: ${MAESTRO_INVITEE1_MXID}
|
||||
- tapOn:
|
||||
text: ${MAESTRO_INVITEE1_MXID}
|
||||
index: 1
|
||||
- tapOn: "Next"
|
||||
- tapOn: "e.g. your project name"
|
||||
- inputText: "aRoomName"
|
||||
- tapOn: "What is this room about?"
|
||||
- inputText: "aRoomTopic"
|
||||
- tapOn: "Create"
|
||||
- takeScreenshot: build/maestro/320-createAndDeleteRoom
|
||||
- tapOn: "Search for someone"
|
||||
- inputText: ${MAESTRO_INVITEE1_MXID}
|
||||
- tapOn:
|
||||
text: ${MAESTRO_INVITEE1_MXID}
|
||||
index: 1
|
||||
- tapOn: "Finish"
|
||||
- tapOn: "aRoomName"
|
||||
- tapOn: "Invite"
|
||||
# assert there's 1 member and 1 invitee
|
||||
|
||||
@@ -65,6 +65,7 @@ dependencies {
|
||||
testImplementation(projects.features.rageshake.test)
|
||||
testImplementation(projects.services.appnavstate.test)
|
||||
testImplementation(projects.services.analytics.test)
|
||||
testImplementation(projects.services.toolbox.test)
|
||||
testImplementation(libs.test.appyx.junit)
|
||||
testImplementation(libs.test.arch.core)
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ import io.element.android.appnav.loggedin.SendQueues
|
||||
import io.element.android.appnav.room.RoomFlowNode
|
||||
import io.element.android.appnav.room.RoomNavigationTarget
|
||||
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode
|
||||
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||
import io.element.android.features.enterprise.api.SessionEnterpriseService
|
||||
import io.element.android.features.ftue.api.FtueEntryPoint
|
||||
import io.element.android.features.ftue.api.state.FtueService
|
||||
@@ -60,6 +59,7 @@ import io.element.android.features.roomdirectory.api.RoomDescription
|
||||
import io.element.android.features.roomdirectory.api.RoomDirectoryEntryPoint
|
||||
import io.element.android.features.securebackup.api.SecureBackupEntryPoint
|
||||
import io.element.android.features.share.api.ShareEntryPoint
|
||||
import io.element.android.features.startchat.api.StartChatEntryPoint
|
||||
import io.element.android.features.userprofile.api.UserProfileEntryPoint
|
||||
import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint
|
||||
import io.element.android.libraries.architecture.BackstackView
|
||||
@@ -94,15 +94,6 @@ import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
import kotlin.collections.List
|
||||
import kotlin.collections.any
|
||||
import kotlin.collections.emptyList
|
||||
import kotlin.collections.first
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.listOf
|
||||
import kotlin.collections.mapNotNull
|
||||
import kotlin.collections.plus
|
||||
import kotlin.collections.setOf
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.toKotlinDuration
|
||||
@@ -113,7 +104,7 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val homeEntryPoint: HomeEntryPoint,
|
||||
private val preferencesEntryPoint: PreferencesEntryPoint,
|
||||
private val createRoomEntryPoint: CreateRoomEntryPoint,
|
||||
private val startChatEntryPoint: StartChatEntryPoint,
|
||||
private val appNavigationStateService: AppNavigationStateService,
|
||||
private val secureBackupEntryPoint: SecureBackupEntryPoint,
|
||||
private val userProfileEntryPoint: UserProfileEntryPoint,
|
||||
@@ -304,7 +295,7 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||
backstack.push(NavTarget.Settings())
|
||||
}
|
||||
|
||||
override fun onCreateRoomClick() {
|
||||
override fun onStartChatClick() {
|
||||
backstack.push(NavTarget.CreateRoom)
|
||||
}
|
||||
|
||||
@@ -422,7 +413,7 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||
.build()
|
||||
}
|
||||
NavTarget.CreateRoom -> {
|
||||
val callback = object : CreateRoomEntryPoint.Callback {
|
||||
val callback = object : StartChatEntryPoint.Callback {
|
||||
override fun onOpenRoom(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>) {
|
||||
backstack.replace(NavTarget.Room(roomIdOrAlias = roomIdOrAlias, serverNames = serverNames))
|
||||
}
|
||||
@@ -432,7 +423,7 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
createRoomEntryPoint
|
||||
startChatEntryPoint
|
||||
.nodeBuilder(this, buildContext)
|
||||
.callback(callback)
|
||||
.build()
|
||||
|
||||
@@ -19,6 +19,7 @@ import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.services.apperror.api.AppErrorState
|
||||
import io.element.android.services.apperror.api.AppErrorStateService
|
||||
import io.element.android.services.apperror.impl.DefaultAppErrorStateService
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
@@ -42,7 +43,9 @@ class RootPresenterTest {
|
||||
@Test
|
||||
fun `present - passes app error state`() = runTest {
|
||||
val presenter = createRootPresenter(
|
||||
appErrorService = DefaultAppErrorStateService().apply {
|
||||
appErrorService = DefaultAppErrorStateService(
|
||||
stringProvider = FakeStringProvider(),
|
||||
).apply {
|
||||
showError("Bad news", "Something bad happened")
|
||||
}
|
||||
)
|
||||
@@ -61,7 +64,9 @@ class RootPresenterTest {
|
||||
}
|
||||
|
||||
private fun createRootPresenter(
|
||||
appErrorService: AppErrorStateService = DefaultAppErrorStateService(),
|
||||
appErrorService: AppErrorStateService = DefaultAppErrorStateService(
|
||||
stringProvider = FakeStringProvider(),
|
||||
),
|
||||
): RootPresenter {
|
||||
return RootPresenter(
|
||||
crashDetectionPresenter = { aCrashDetectionState() },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 New Vector Ltd.
|
||||
* 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.
|
||||
@@ -11,17 +11,17 @@ import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
interface CreateRoomEntryPoint : FeatureEntryPoint {
|
||||
fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
|
||||
|
||||
interface NodeBuilder {
|
||||
fun callback(callback: Callback): NodeBuilder
|
||||
fun build(): Node
|
||||
}
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun onOpenRoom(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>)
|
||||
fun onOpenRoomDirectory()
|
||||
fun onRoomCreated(roomId: RoomId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import extension.ComponentMergingStrategy
|
||||
import extension.setupAnvil
|
||||
|
||||
/*
|
||||
@@ -23,7 +22,7 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
setupAnvil(componentMergingStrategy = ComponentMergingStrategy.KSP)
|
||||
setupAnvil()
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.core)
|
||||
@@ -41,6 +40,7 @@ dependencies {
|
||||
implementation(projects.services.analytics.api)
|
||||
implementation(libs.coil.compose)
|
||||
implementation(projects.libraries.featureflag.api)
|
||||
implementation(projects.features.invitepeople.api)
|
||||
api(projects.features.createroom.api)
|
||||
|
||||
testImplementation(libs.test.junit)
|
||||
@@ -56,7 +56,7 @@ dependencies {
|
||||
testImplementation(projects.libraries.mediaupload.test)
|
||||
testImplementation(projects.libraries.permissions.test)
|
||||
testImplementation(projects.libraries.usersearch.test)
|
||||
testImplementation(projects.features.createroom.test)
|
||||
testImplementation(projects.features.startchat.test)
|
||||
testImplementation(projects.libraries.featureflag.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
testImplementation(libs.androidx.compose.ui.test.junit)
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 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.createroom.impl
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import com.bumble.appyx.core.plugin.plugins
|
||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.createroom.CreateRoomNavigator
|
||||
import io.element.android.features.createroom.impl.addpeople.AddPeopleNode
|
||||
import io.element.android.features.createroom.impl.configureroom.ConfigureRoomNode
|
||||
import io.element.android.features.createroom.impl.di.CreateRoomComponent
|
||||
import io.element.android.libraries.architecture.BackstackView
|
||||
import io.element.android.libraries.architecture.BaseFlowNode
|
||||
import io.element.android.libraries.architecture.bindings
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
import io.element.android.libraries.di.DaggerComponentOwner
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
class ConfigureRoomFlowNode @AssistedInject constructor(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
) : DaggerComponentOwner,
|
||||
BaseFlowNode<ConfigureRoomFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
initialElement = NavTarget.Root,
|
||||
savedStateMap = buildContext.savedStateMap,
|
||||
),
|
||||
buildContext = buildContext,
|
||||
plugins = plugins
|
||||
) {
|
||||
private val component by lazy {
|
||||
parent!!.bindings<CreateRoomComponent.ParentBindings>().createRoomComponentBuilder().build()
|
||||
}
|
||||
private val navigator = plugins<CreateRoomNavigator>().first()
|
||||
|
||||
override val daggerComponent: Any
|
||||
get() = component
|
||||
|
||||
sealed interface NavTarget : Parcelable {
|
||||
@Parcelize
|
||||
data object Root : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data object ConfigureRoom : NavTarget
|
||||
}
|
||||
|
||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||
return when (navTarget) {
|
||||
NavTarget.Root -> {
|
||||
val callback = object : AddPeopleNode.Callback {
|
||||
override fun onContinue() {
|
||||
backstack.push(NavTarget.ConfigureRoom)
|
||||
}
|
||||
}
|
||||
createNode<AddPeopleNode>(buildContext = buildContext, plugins = listOf(callback))
|
||||
}
|
||||
NavTarget.ConfigureRoom -> {
|
||||
createNode<ConfigureRoomNode>(buildContext = buildContext, plugins = listOf(navigator))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
BackstackView()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 New Vector Ltd.
|
||||
* 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.
|
||||
@@ -8,28 +8,25 @@
|
||||
package io.element.android.features.createroom.impl
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.navigation.transition.JumpToEndTransitionHandler
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import com.bumble.appyx.core.plugin.plugins
|
||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||
import com.bumble.appyx.navmodel.backstack.operation.replace
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.createroom.DefaultCreateRoomNavigator
|
||||
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||
import io.element.android.features.createroom.impl.joinbyaddress.JoinRoomByAddressNode
|
||||
import io.element.android.features.createroom.impl.root.CreateRoomRootNode
|
||||
import io.element.android.features.createroom.impl.addpeople.AddPeopleNode
|
||||
import io.element.android.features.createroom.impl.configureroom.ConfigureRoomNode
|
||||
import io.element.android.libraries.architecture.BackstackView
|
||||
import io.element.android.libraries.architecture.BaseFlowNode
|
||||
import io.element.android.libraries.architecture.OverlayView
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
@@ -38,53 +35,48 @@ class CreateRoomFlowNode @AssistedInject constructor(
|
||||
@Assisted plugins: List<Plugin>,
|
||||
) : BaseFlowNode<CreateRoomFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
initialElement = NavTarget.Root,
|
||||
initialElement = NavTarget.ConfigureRoom,
|
||||
savedStateMap = buildContext.savedStateMap,
|
||||
),
|
||||
buildContext = buildContext,
|
||||
plugins = plugins
|
||||
) {
|
||||
sealed interface NavTarget : Parcelable {
|
||||
@Parcelize
|
||||
data object Root : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data object NewRoom : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data object JoinByAddress : NavTarget
|
||||
private fun onRoomCreated(roomId: RoomId) {
|
||||
plugins<CreateRoomEntryPoint.Callback>().forEach { it.onRoomCreated(roomId) }
|
||||
}
|
||||
|
||||
private val navigator = DefaultCreateRoomNavigator(
|
||||
backstack = backstack,
|
||||
overlay = overlay,
|
||||
openRoom = { roomIdOrAlias, viaServers ->
|
||||
plugins<CreateRoomEntryPoint.Callback>().forEach { it.onOpenRoom(roomIdOrAlias, viaServers) }
|
||||
},
|
||||
openRoomDirectory = {
|
||||
plugins<CreateRoomEntryPoint.Callback>().forEach { it.onOpenRoomDirectory() }
|
||||
}
|
||||
)
|
||||
|
||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||
return when (navTarget) {
|
||||
NavTarget.Root -> {
|
||||
createNode<CreateRoomRootNode>(buildContext = buildContext, plugins = listOf(navigator))
|
||||
NavTarget.ConfigureRoom -> {
|
||||
val callback = object : ConfigureRoomNode.Callback {
|
||||
override fun onCreateRoomSuccess(roomId: RoomId) {
|
||||
backstack.replace(NavTarget.AddPeople(roomId))
|
||||
}
|
||||
}
|
||||
createNode<ConfigureRoomNode>(buildContext, plugins = listOf(callback))
|
||||
}
|
||||
NavTarget.NewRoom -> {
|
||||
createNode<ConfigureRoomFlowNode>(buildContext = buildContext, plugins = listOf(navigator))
|
||||
}
|
||||
NavTarget.JoinByAddress -> {
|
||||
createNode<JoinRoomByAddressNode>(buildContext = buildContext, plugins = listOf(navigator))
|
||||
is NavTarget.AddPeople -> {
|
||||
val inputs = AddPeopleNode.Inputs(navTarget.roomId)
|
||||
val callback: AddPeopleNode.Callback = object : AddPeopleNode.Callback {
|
||||
override fun onFinish() {
|
||||
onRoomCreated(navTarget.roomId)
|
||||
}
|
||||
}
|
||||
createNode<AddPeopleNode>(buildContext, plugins = listOf(inputs, callback))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
Box(modifier = modifier) {
|
||||
BackstackView()
|
||||
OverlayView(transitionHandler = remember { JumpToEndTransitionHandler() })
|
||||
}
|
||||
BackstackView()
|
||||
}
|
||||
|
||||
sealed interface NavTarget : Parcelable {
|
||||
@Parcelize
|
||||
data object ConfigureRoom : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class AddPeople(val roomId: RoomId) : NavTarget
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 New Vector Ltd.
|
||||
* 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.
|
||||
@@ -13,10 +13,10 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultCreateRoomEntryPoint @Inject constructor() : CreateRoomEntryPoint {
|
||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): CreateRoomEntryPoint.NodeBuilder {
|
||||
val plugins = ArrayList<Plugin>()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 New Vector Ltd.
|
||||
* 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.
|
||||
@@ -16,30 +16,46 @@ import com.bumble.appyx.core.plugin.plugins
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.createroom.impl.di.CreateRoomScope
|
||||
import io.element.android.features.invitepeople.api.InvitePeoplePresenter
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleRenderer
|
||||
import io.element.android.libraries.architecture.NodeInputs
|
||||
import io.element.android.libraries.architecture.inputs
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
@ContributesNode(CreateRoomScope::class)
|
||||
@ContributesNode(SessionScope::class)
|
||||
class AddPeopleNode @AssistedInject constructor(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val presenter: AddPeoplePresenter,
|
||||
invitePeoplePresenterFactory: InvitePeoplePresenter.Factory,
|
||||
private val invitePeopleRenderer: InvitePeopleRenderer,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
data class Inputs(
|
||||
val roomId: RoomId,
|
||||
) : NodeInputs
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun onContinue()
|
||||
fun onFinish()
|
||||
}
|
||||
|
||||
private fun onContinue() {
|
||||
plugins<Callback>().forEach { it.onContinue() }
|
||||
private fun onFinish() {
|
||||
plugins<Callback>().forEach { it.onFinish() }
|
||||
}
|
||||
|
||||
private val roomId = inputs<Inputs>().roomId
|
||||
private val invitePeoplePresenter = invitePeoplePresenterFactory.create(
|
||||
joinedRoom = null,
|
||||
roomId = roomId,
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
val state = invitePeoplePresenter.present()
|
||||
AddPeopleView(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
onBackClick = this::navigateUp,
|
||||
onNextClick = this::onContinue,
|
||||
)
|
||||
onFinish = ::onFinish,
|
||||
) {
|
||||
invitePeopleRenderer.Render(state, Modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 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.createroom.impl.addpeople
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import io.element.android.features.createroom.impl.CreateRoomDataStore
|
||||
import io.element.android.features.createroom.impl.userlist.SelectionMode
|
||||
import io.element.android.features.createroom.impl.userlist.UserListPresenter
|
||||
import io.element.android.features.createroom.impl.userlist.UserListPresenterArgs
|
||||
import io.element.android.features.createroom.impl.userlist.UserListState
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.usersearch.api.UserRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class AddPeoplePresenter @Inject constructor(
|
||||
userListPresenterFactory: UserListPresenter.Factory,
|
||||
userRepository: UserRepository,
|
||||
dataStore: CreateRoomDataStore,
|
||||
) : Presenter<UserListState> {
|
||||
private val userListPresenter = userListPresenterFactory.create(
|
||||
UserListPresenterArgs(
|
||||
selectionMode = SelectionMode.Multiple,
|
||||
),
|
||||
userRepository,
|
||||
dataStore.selectedUserListDataStore,
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun present(): UserListState {
|
||||
return userListPresenter.present()
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 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.createroom.impl.addpeople
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.createroom.impl.userlist.SelectionMode
|
||||
import io.element.android.features.createroom.impl.userlist.UserListState
|
||||
import io.element.android.features.createroom.impl.userlist.aRecentDirectRoomList
|
||||
import io.element.android.features.createroom.impl.userlist.aUserListState
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
|
||||
import io.element.android.libraries.usersearch.api.UserSearchResult
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
open class AddPeopleUserListStateProvider : PreviewParameterProvider<UserListState> {
|
||||
override val values: Sequence<UserListState>
|
||||
get() = sequenceOf(
|
||||
aUserListState(),
|
||||
aUserListState(
|
||||
searchResults = SearchBarResultState.Results(aMatrixUserList().toImmutableList()),
|
||||
selectedUsers = aMatrixUserList().toImmutableList(),
|
||||
isSearchActive = false,
|
||||
selectionMode = SelectionMode.Multiple,
|
||||
),
|
||||
aUserListState(
|
||||
searchResults = SearchBarResultState.Results(
|
||||
aMatrixUserList()
|
||||
.mapIndexed { index, matrixUser ->
|
||||
UserSearchResult(matrixUser, index % 2 == 0)
|
||||
}
|
||||
.toImmutableList()
|
||||
),
|
||||
selectedUsers = aMatrixUserList().toImmutableList(),
|
||||
isSearchActive = true,
|
||||
selectionMode = SelectionMode.Multiple,
|
||||
),
|
||||
aUserListState(
|
||||
recentDirectRooms = aRecentDirectRoomList(),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022-2024 New Vector Ltd.
|
||||
* 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.
|
||||
@@ -7,77 +7,68 @@
|
||||
|
||||
package io.element.android.features.createroom.impl.addpeople
|
||||
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.features.createroom.impl.R
|
||||
import io.element.android.features.createroom.impl.components.UserListView
|
||||
import io.element.android.features.createroom.impl.userlist.UserListEvents
|
||||
import io.element.android.features.createroom.impl.userlist.UserListState
|
||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvents
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleState
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleStateProvider
|
||||
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun AddPeopleView(
|
||||
state: UserListState,
|
||||
onBackClick: () -> Unit,
|
||||
onNextClick: () -> Unit,
|
||||
state: InvitePeopleState,
|
||||
onFinish: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
invitePeopleView: @Composable () -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
HeaderFooterPage(
|
||||
modifier = modifier,
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
topBar = {
|
||||
AddPeopleViewTopBar(
|
||||
hasSelectedUsers = state.selectedUsers.isNotEmpty(),
|
||||
onBackClick = {
|
||||
if (state.isSearchActive) {
|
||||
state.eventSink(UserListEvents.OnSearchActiveChanged(false))
|
||||
} else {
|
||||
onBackClick()
|
||||
}
|
||||
AddPeopleTopBar(onSkipClick = onFinish)
|
||||
},
|
||||
footer = {
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_finish),
|
||||
onClick = {
|
||||
state.eventSink(InvitePeopleEvents.SendInvites)
|
||||
onFinish()
|
||||
},
|
||||
onNextClick = onNextClick,
|
||||
enabled = state.canInvite,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 16.dp)
|
||||
)
|
||||
}
|
||||
) { padding ->
|
||||
UserListView(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding),
|
||||
state = state,
|
||||
showBackButton = false,
|
||||
onSelectUser = {},
|
||||
onDeselectUser = {},
|
||||
)
|
||||
}
|
||||
},
|
||||
content = invitePeopleView
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun AddPeopleViewTopBar(
|
||||
hasSelectedUsers: Boolean,
|
||||
onBackClick: () -> Unit,
|
||||
onNextClick: () -> Unit,
|
||||
private fun AddPeopleTopBar(
|
||||
onSkipClick: () -> Unit,
|
||||
) {
|
||||
TopAppBar(
|
||||
titleStr = stringResource(id = R.string.screen_create_room_add_people_title),
|
||||
navigationIcon = { BackButton(onClick = onBackClick) },
|
||||
titleStr = stringResource(R.string.screen_create_room_add_people_title),
|
||||
actions = {
|
||||
val textActionResId = if (hasSelectedUsers) CommonStrings.action_next else CommonStrings.action_skip
|
||||
TextButton(
|
||||
text = stringResource(id = textActionResId),
|
||||
onClick = onNextClick,
|
||||
text = stringResource(CommonStrings.action_skip),
|
||||
onClick = onSkipClick,
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -85,10 +76,10 @@ private fun AddPeopleViewTopBar(
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun AddPeopleViewPreview(@PreviewParameter(AddPeopleUserListStateProvider::class) state: UserListState) = ElementPreview {
|
||||
internal fun AddPeopleViewPreview(@PreviewParameter(InvitePeopleStateProvider::class) state: InvitePeopleState) = ElementPreview {
|
||||
AddPeopleView(
|
||||
state = state,
|
||||
onBackClick = {},
|
||||
onNextClick = {},
|
||||
invitePeopleView = {},
|
||||
onFinish = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
package io.element.android.features.createroom.impl.configureroom
|
||||
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||
|
||||
sealed interface ConfigureRoomEvents {
|
||||
@@ -16,7 +15,6 @@ sealed interface ConfigureRoomEvents {
|
||||
data class RoomVisibilityChanged(val visibilityItem: RoomVisibilityItem) : ConfigureRoomEvents
|
||||
data class RoomAccessChanged(val roomAccess: RoomAccessItem) : ConfigureRoomEvents
|
||||
data class RoomAddressChanged(val roomAddress: String) : ConfigureRoomEvents
|
||||
data class RemoveUserFromSelection(val matrixUser: MatrixUser) : ConfigureRoomEvents
|
||||
data object CreateRoom : ConfigureRoomEvents
|
||||
data class HandleAvatarAction(val action: AvatarAction) : ConfigureRoomEvents
|
||||
data object CancelCreateRoom : ConfigureRoomEvents
|
||||
|
||||
@@ -18,19 +18,20 @@ import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.features.analytics.plan.MobileScreen
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.createroom.CreateRoomNavigator
|
||||
import io.element.android.features.createroom.impl.di.CreateRoomScope
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
|
||||
@ContributesNode(CreateRoomScope::class)
|
||||
@ContributesNode(SessionScope::class)
|
||||
class ConfigureRoomNode @AssistedInject constructor(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val presenter: ConfigureRoomPresenter,
|
||||
private val analyticsService: AnalyticsService,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
private val navigator = plugins<CreateRoomNavigator>().first()
|
||||
interface Callback : Plugin {
|
||||
fun onCreateRoomSuccess(roomId: RoomId)
|
||||
}
|
||||
|
||||
init {
|
||||
lifecycle.subscribe(
|
||||
@@ -40,6 +41,10 @@ class ConfigureRoomNode @AssistedInject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun onCreateRoomSuccess(roomId: RoomId) {
|
||||
plugins<Callback>().forEach { it.onCreateRoomSuccess(roomId) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
@@ -47,9 +52,7 @@ class ConfigureRoomNode @AssistedInject constructor(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
onBackClick = this::navigateUp,
|
||||
onCreateRoomSuccess = {
|
||||
navigator.onOpenRoom(roomIdOrAlias = it.toRoomIdOrAlias(), serverNames = emptyList())
|
||||
},
|
||||
onCreateRoomSuccess = ::onCreateRoomSuccess,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import im.vector.app.features.analytics.plan.CreatedRoom
|
||||
import io.element.android.features.createroom.impl.CreateRoomConfig
|
||||
import io.element.android.features.createroom.impl.CreateRoomDataStore
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||
@@ -50,7 +48,7 @@ import javax.inject.Inject
|
||||
import kotlin.jvm.optionals.getOrDefault
|
||||
|
||||
class ConfigureRoomPresenter @Inject constructor(
|
||||
private val dataStore: CreateRoomDataStore,
|
||||
private val dataStore: CreateRoomConfigStore,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val mediaPickerProvider: PickerProvider,
|
||||
private val mediaPreProcessor: MediaPreProcessor,
|
||||
@@ -66,7 +64,7 @@ class ConfigureRoomPresenter @Inject constructor(
|
||||
@Composable
|
||||
override fun present(): ConfigureRoomState {
|
||||
val cameraPermissionState = cameraPermissionPresenter.present()
|
||||
val createRoomConfig by dataStore.createRoomConfigWithInvites.collectAsState(CreateRoomConfig())
|
||||
val createRoomConfig by dataStore.getCreateRoomConfigFlow().collectAsState(CreateRoomConfig())
|
||||
val homeserverName = remember { matrixClient.userIdServerName() }
|
||||
val isKnockFeatureEnabled by remember {
|
||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock)
|
||||
@@ -121,7 +119,6 @@ class ConfigureRoomPresenter @Inject constructor(
|
||||
is ConfigureRoomEvents.RoomNameChanged -> dataStore.setRoomName(event.name)
|
||||
is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic)
|
||||
is ConfigureRoomEvents.RoomVisibilityChanged -> dataStore.setRoomVisibility(event.visibilityItem)
|
||||
is ConfigureRoomEvents.RemoveUserFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser)
|
||||
is ConfigureRoomEvents.RoomAccessChanged -> dataStore.setRoomAccess(event.roomAccess)
|
||||
is ConfigureRoomEvents.RoomAddressChanged -> dataStore.setRoomAddress(event.roomAddress)
|
||||
is ConfigureRoomEvents.CreateRoom -> createRoom(createRoomConfig)
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
package io.element.android.features.createroom.impl.configureroom
|
||||
|
||||
import io.element.android.features.createroom.impl.CreateRoomConfig
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
package io.element.android.features.createroom.impl.configureroom
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.createroom.impl.CreateRoomConfig
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
|
||||
|
||||
@@ -12,7 +12,6 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
@@ -58,7 +57,6 @@ import io.element.android.libraries.designsystem.theme.components.TextField
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet
|
||||
import io.element.android.libraries.matrix.ui.components.SelectedUsersRowList
|
||||
import io.element.android.libraries.matrix.ui.components.UnsavedAvatar
|
||||
import io.element.android.libraries.matrix.ui.room.address.RoomAddressField
|
||||
import io.element.android.libraries.permissions.api.PermissionsView
|
||||
@@ -112,16 +110,6 @@ fun ConfigureRoomView(
|
||||
topic = state.config.topic.orEmpty(),
|
||||
onTopicChange = { state.eventSink(ConfigureRoomEvents.TopicChanged(it)) },
|
||||
)
|
||||
if (state.config.invites.isNotEmpty()) {
|
||||
SelectedUsersRowList(
|
||||
contentPadding = PaddingValues(horizontal = 24.dp),
|
||||
selectedUsers = state.config.invites,
|
||||
onUserRemove = {
|
||||
focusManager.clearFocus()
|
||||
state.eventSink(ConfigureRoomEvents.RemoveUserFromSelection(it))
|
||||
},
|
||||
)
|
||||
}
|
||||
RoomVisibilityOptions(
|
||||
selected = when (state.config.roomVisibility) {
|
||||
is RoomVisibilityState.Private -> RoomVisibilityItem.Private
|
||||
|
||||
@@ -5,10 +5,9 @@
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.createroom.impl
|
||||
package io.element.android.features.createroom.impl.configureroom
|
||||
|
||||
import android.net.Uri
|
||||
import io.element.android.features.createroom.impl.configureroom.RoomVisibilityState
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
@@ -5,45 +5,29 @@
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.createroom.impl
|
||||
package io.element.android.features.createroom.impl.configureroom
|
||||
|
||||
import android.net.Uri
|
||||
import io.element.android.features.createroom.impl.configureroom.RoomAccess
|
||||
import io.element.android.features.createroom.impl.configureroom.RoomAccessItem
|
||||
import io.element.android.features.createroom.impl.configureroom.RoomAddress
|
||||
import io.element.android.features.createroom.impl.configureroom.RoomVisibilityItem
|
||||
import io.element.android.features.createroom.impl.configureroom.RoomVisibilityState
|
||||
import io.element.android.features.createroom.impl.di.CreateRoomScope
|
||||
import io.element.android.features.createroom.impl.userlist.UserListDataStore
|
||||
import io.element.android.libraries.androidutils.file.safeDelete
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.getAndUpdate
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
@SingleIn(CreateRoomScope::class)
|
||||
class CreateRoomDataStore @Inject constructor(
|
||||
val selectedUserListDataStore: UserListDataStore,
|
||||
class CreateRoomConfigStore @Inject constructor(
|
||||
private val roomAliasHelper: RoomAliasHelper,
|
||||
) {
|
||||
private val createRoomConfigFlow: MutableStateFlow<CreateRoomConfig> = MutableStateFlow(CreateRoomConfig())
|
||||
|
||||
private var cachedAvatarUri: Uri? = null
|
||||
set(value) {
|
||||
field?.path?.let { File(it) }?.safeDelete()
|
||||
field = value
|
||||
}
|
||||
|
||||
val createRoomConfigWithInvites: Flow<CreateRoomConfig> = combine(
|
||||
selectedUserListDataStore.selectedUsers,
|
||||
createRoomConfigFlow,
|
||||
) { selectedUsers, config ->
|
||||
config.copy(invites = selectedUsers.toImmutableList())
|
||||
}
|
||||
fun getCreateRoomConfigFlow(): StateFlow<CreateRoomConfig> = createRoomConfigFlow
|
||||
|
||||
fun setRoomName(roomName: String) {
|
||||
createRoomConfigFlow.getAndUpdate { config ->
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 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.createroom.impl.di
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import com.squareup.anvil.annotations.MergeSubcomponent
|
||||
import io.element.android.libraries.architecture.NodeFactoriesBindings
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
|
||||
@SingleIn(CreateRoomScope::class)
|
||||
@MergeSubcomponent(CreateRoomScope::class)
|
||||
interface CreateRoomComponent : NodeFactoriesBindings {
|
||||
@MergeSubcomponent.Builder
|
||||
interface Builder {
|
||||
fun build(): CreateRoomComponent
|
||||
}
|
||||
|
||||
@ContributesTo(SessionScope::class)
|
||||
interface ParentBindings {
|
||||
fun createRoomComponentBuilder(): Builder
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 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.createroom.impl.di
|
||||
|
||||
abstract class CreateRoomScope private constructor()
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 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.createroom.impl.root
|
||||
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
||||
sealed interface CreateRoomRootEvents {
|
||||
data class StartDM(val matrixUser: MatrixUser) : CreateRoomRootEvents
|
||||
data object CancelStartDM : CreateRoomRootEvents
|
||||
}
|
||||
@@ -14,6 +14,4 @@
|
||||
<string name="screen_create_room_room_name_label">"Назва пакоя"</string>
|
||||
<string name="screen_create_room_title">"Стварыце пакой"</string>
|
||||
<string name="screen_create_room_topic_label">"Тэма (неабавязкова)"</string>
|
||||
<string name="screen_room_directory_search_title">"Каталог пакояў"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Пры спробе пачаць чат адбылася памылка"</string>
|
||||
</resources>
|
||||
|
||||
@@ -15,9 +15,4 @@
|
||||
<string name="screen_create_room_room_visibility_section_title">"Видимост на стаята"</string>
|
||||
<string name="screen_create_room_title">"Създаване на стая"</string>
|
||||
<string name="screen_create_room_topic_label">"Тема за разговор (незадължително)"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Присъединяване към стая по адрес"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Не е валиден адрес"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Въведете…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Стаята не е намерена"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"напр. #room-name:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ To můžete kdykoli změnit v nastavení místnosti."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Viditelnost místnosti"</string>
|
||||
<string name="screen_create_room_title">"Vytvořit místnost"</string>
|
||||
<string name="screen_create_room_topic_label">"Téma (nepovinné)"</string>
|
||||
<string name="screen_room_directory_search_title">"Adresář místností"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Při pokusu o zahájení chatu došlo k chybě"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Vstoupit do místnosti pomocí adresy"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Neplatná adresa"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Zadejte…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Odpovídající místnost nalezena"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Místnost nebyla nalezena"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"např. #room-name:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Gallwch newid hyn unrhyw bryd yng ngosodiadau ystafell."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Gwelededd yr ystafell"</string>
|
||||
<string name="screen_create_room_title">"Creu ystafell"</string>
|
||||
<string name="screen_create_room_topic_label">"Pwnc (dewisol)"</string>
|
||||
<string name="screen_room_directory_search_title">"Cyfeiriadur ystafelloedd"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Digwyddodd gwall wrth geisio cychwyn sgwrs"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Ymuno â\'r ystafell yn ôl cyfeiriad"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Ddim yn gyfeiriad dilys"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Ewch i mewn…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Cafwyd hyd i ystafell gyfatebol"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Heb ganfod yr ystafell"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"e.e. #enw-ystafell:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Du kan ændre dette når som helst i rummets indstillinger."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Rummets synlighed"</string>
|
||||
<string name="screen_create_room_title">"Opret et rum"</string>
|
||||
<string name="screen_create_room_topic_label">"Emne (valgfrit)"</string>
|
||||
<string name="screen_room_directory_search_title">"Register over rum"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Der opstod en fejl under forsøget på at starte en samtale"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Tilslut dig rummet med adressen"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Ikke en gyldig adresse"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Indtast…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Matchende rum fundet"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Rum ikke fundet"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"f.eks. #rummets-navn:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Sie können dies aber jederzeit in den Chatroomeinstellungen ändern."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">" Sichtbarkeit des Chatrooms"</string>
|
||||
<string name="screen_create_room_title">"Raum erstellen"</string>
|
||||
<string name="screen_create_room_topic_label">"Thema (optional)"</string>
|
||||
<string name="screen_room_directory_search_title">"Raum-Verzeichnis"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Beim Versuch, einen Chat zu starten, ist ein Fehler aufgetreten"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Raum per Adresse betreten"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Keine gültige Adresse"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Eintreten…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Passender Raum gefunden"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Raum nicht gefunden"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"z. B. #room -name:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@
|
||||
<string name="screen_create_room_room_visibility_section_title">"Ορατότητα αίθουσας"</string>
|
||||
<string name="screen_create_room_title">"Δημιουργία αίθουσας"</string>
|
||||
<string name="screen_create_room_topic_label">"Θέμα (προαιρετικό)"</string>
|
||||
<string name="screen_room_directory_search_title">"Κατάλογος αιθουσών"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Παρουσιάστηκε σφάλμα κατά την προσπάθεια έναρξης μιας συνομιλίας"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Συμμετοχή σε αίθουσα μέσω διεύθυνσης"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Μη έγκυρη διεύθυνση"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Εισάγετε…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Βρέθηκε η αντίστοιχη αίθουσα"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Η αίθουσα δεν βρέθηκε"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"π.χ. #όνομα-αίθουσας:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Puedes cambiar esto en cualquier momento en los ajustes de la sala."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Visibilidad de la sala"</string>
|
||||
<string name="screen_create_room_title">"Crear una sala"</string>
|
||||
<string name="screen_create_room_topic_label">"Tema (opcional)"</string>
|
||||
<string name="screen_room_directory_search_title">"Directorio de salas"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Se ha producido un error al intentar iniciar un chat"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Unirse a una sala por su dirección"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Dirección no válida"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Introducir…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Sala encontrada"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"No se encontró la sala"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"p. ej., #nombre-de-la-sala:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Sa võid seda jututoa seadistustest alati muuta."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Jututoa nähtavus"</string>
|
||||
<string name="screen_create_room_title">"Loo jututuba"</string>
|
||||
<string name="screen_create_room_topic_label">"Teema (kui soovid lisada)"</string>
|
||||
<string name="screen_room_directory_search_title">"Jututubade kataloog"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Vestluse alustamisel tekkis viga"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Liitu jututoaga aadressi alusel"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"See pole kehtiv aadress"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Sisene…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Leidsime vastava jututoa"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Jututuba ei leidu"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"nt. #jututoa-nimi:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -16,8 +16,4 @@ Gelaren ezarpenetan aldatu dezakezu hobespena."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Gelaren ikusgarritasuna"</string>
|
||||
<string name="screen_create_room_title">"Sortu gela"</string>
|
||||
<string name="screen_create_room_topic_label">"Mintzagaia (aukerakoa)"</string>
|
||||
<string name="screen_room_directory_search_title">"Gelen direktorioa"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Errorea gertatu da txata hasten saiatzean"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Sartu…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Ez da gela aurkitu"</string>
|
||||
</resources>
|
||||
|
||||
@@ -17,12 +17,4 @@
|
||||
<string name="screen_create_room_room_visibility_section_title">"نمایانی اتاق"</string>
|
||||
<string name="screen_create_room_title">"ایجاد اتاق"</string>
|
||||
<string name="screen_create_room_topic_label">"موضوع (اختیاری)"</string>
|
||||
<string name="screen_room_directory_search_title">"فهرست اتاقها"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"هنگام تلاش برای شروع چت خطایی روی داد"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"پیوستن به اتاق با نشانی"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"نشانی معتبری نیست"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"ورود…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"اتاق مطابق پیدا شد"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"اتاق پیدا نشد"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"نمونه: #room-name:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Voit muuttaa tämän milloin tahansa huoneen asetuksista."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Huoneen näkyvyys"</string>
|
||||
<string name="screen_create_room_title">"Luo huone"</string>
|
||||
<string name="screen_create_room_topic_label">"Aihe (valinnainen)"</string>
|
||||
<string name="screen_room_directory_search_title">"Huoneluettelo"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Keskustelun aloituksessa tapahtui virhe"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Liity huoneeseen osoitteella"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Osoite ei ole kelvollinen"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Syötä…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Täsmäävä huone löytyi"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Huonetta ei löytynyt"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"esim. #huoneen-nimi:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Vous pouvez modifier cela à tout moment dans les paramètres du salon."</string
|
||||
<string name="screen_create_room_room_visibility_section_title">"Visibilité du salon"</string>
|
||||
<string name="screen_create_room_title">"Créer un salon"</string>
|
||||
<string name="screen_create_room_topic_label">"Sujet (facultatif)"</string>
|
||||
<string name="screen_room_directory_search_title">"Annuaire des salons"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Une erreur s’est produite lors de la tentative de création de la discussion"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Saisir une adresse de salon"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Ce n’est pas une adresse valide"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Saisir…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Ce salon existe"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Salon non trouvé"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"ex: #nom-du-salon:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Ezt bármikor módosíthatja a szobabeállításokban."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Szoba láthatósága"</string>
|
||||
<string name="screen_create_room_title">"Szoba létrehozása"</string>
|
||||
<string name="screen_create_room_topic_label">"Téma (nem kötelező)"</string>
|
||||
<string name="screen_room_directory_search_title">"Szobakatalógus"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Hiba történt a csevegés indításakor"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Csatlakozás a szobához cím szerint"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Nem érvényes cím"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Írja be…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Megfelelő szoba található"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Szoba nem található"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"pl. #szoba-neve:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Anda dapat mengubah ini kapan pun dalam pengaturan ruangan."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Keterlihatan ruangan"</string>
|
||||
<string name="screen_create_room_title">"Buat ruangan"</string>
|
||||
<string name="screen_create_room_topic_label">"Topik (opsional)"</string>
|
||||
<string name="screen_room_directory_search_title">"Direktori ruangan"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Terjadi kesalahan saat mencoba memulai obrolan"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Bergabung dalam ruangan berdasarkan alamat"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Bukan alamat yang valid"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Masuk…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Ruangan yang cocok ditemukan"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Ruangan tidak ditemukan"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"mis. #nama-ruangan:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Puoi modificarlo in qualsiasi momento nelle impostazioni della stanza."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Visibilità della stanza"</string>
|
||||
<string name="screen_create_room_title">"Crea una stanza"</string>
|
||||
<string name="screen_create_room_topic_label">"Argomento (facoltativo)"</string>
|
||||
<string name="screen_room_directory_search_title">"Elenco delle stanze"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Si è verificato un errore durante il tentativo di avviare una chat"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Accedi alla stanza tramite indirizzo"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Indirizzo non valido"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Inserisci…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Stanza trovata"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Stanza non trovata"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"ad esempio #room -name:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -10,6 +10,4 @@
|
||||
<string name="screen_create_room_room_name_label">"ოთახის სახელი"</string>
|
||||
<string name="screen_create_room_title">"ოთახის შექმნა"</string>
|
||||
<string name="screen_create_room_topic_label">"თემა (სურვილისამებრ)"</string>
|
||||
<string name="screen_room_directory_search_title">"ოთახის კატალოგი"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"ჩატის დაწყების მცდელობისას შეცდომა მოხდა"</string>
|
||||
</resources>
|
||||
|
||||
@@ -10,5 +10,4 @@ Tai galite bet kada pakeisti kambario nustatymuose."</string>
|
||||
<string name="screen_create_room_room_name_label">"Kambario pavadinimas"</string>
|
||||
<string name="screen_create_room_title">"Kurti kambarį"</string>
|
||||
<string name="screen_create_room_topic_label">"Tema (nebūtina)"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Bandant pradėti pokalbį įvyko klaida"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Du kan endre dette når som helst i rominnstillingene."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Romsynlighet"</string>
|
||||
<string name="screen_create_room_title">"Opprett et rom"</string>
|
||||
<string name="screen_create_room_topic_label">"Emne (valgfritt)"</string>
|
||||
<string name="screen_room_directory_search_title">"Romkatalog"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Det oppstod en feil når du prøvde å starte en chat"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Bli med i rommet med adresse"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Ikke en gyldig adresse"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Gå inn…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Matchende rom funnet"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Rom ikke funnet"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"f.eks. #rom-navn:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -16,6 +16,4 @@ Je kunt dit op elk gewenst moment wijzigen in de kamerinstellingen."</string>
|
||||
<string name="screen_create_room_room_name_label">"Naam van de kamer"</string>
|
||||
<string name="screen_create_room_title">"Creëer een kamer"</string>
|
||||
<string name="screen_create_room_topic_label">"Onderwerp (optioneel)"</string>
|
||||
<string name="screen_room_directory_search_title">"Kamergids"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Er is een fout opgetreden bij het starten van een chat"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Możesz to zmienić w ustawieniach pokoju."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Widoczność pomieszczenia"</string>
|
||||
<string name="screen_create_room_title">"Utwórz pokój"</string>
|
||||
<string name="screen_create_room_topic_label">"Temat (opcjonalnie)"</string>
|
||||
<string name="screen_room_directory_search_title">"Katalog pokoi"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Wystąpił błąd podczas próby rozpoczęcia czatu"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Dołącz do pokoju za pomocą adresu"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Nieprawidłowy adres"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Wprowadź…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Znaleziono pasujący pokój"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Nie znaleziono pokoju"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"np. #room-name:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Você pode mudar isso a qualquer momento nas configurações da sala."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Visibilidade da sala"</string>
|
||||
<string name="screen_create_room_title">"Criar uma sala"</string>
|
||||
<string name="screen_create_room_topic_label">"Tópico (opcional)"</string>
|
||||
<string name="screen_room_directory_search_title">"Diretório de salas"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Ocorreu um erro ao tentar iniciar um chat"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Entrar na sala pelo endereço"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Não é um endereço válido"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Entrar…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Foi encontrada uma sala correspondente"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Sala não encontrada"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"Por exemplo, #nome-da-sala:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Pode alterar esta opção nas definições da sala."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Visibilidade da sala"</string>
|
||||
<string name="screen_create_room_title">"Criar uma sala"</string>
|
||||
<string name="screen_create_room_topic_label">"Descrição (opcional)"</string>
|
||||
<string name="screen_room_directory_search_title">"Diretório de salas"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Ocorreu um erro ao tentar iniciar uma conversa"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Entrar na sala pelo endereço"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Não é um endereço válido"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Entrar…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Sala correspondente encontrado"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Sala não encontrada"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"por exemplo, #sala:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -17,6 +17,4 @@ Puteți modifica acest lucru oricând în setări."</string>
|
||||
<string name="screen_create_room_room_name_label">"Numele camerei"</string>
|
||||
<string name="screen_create_room_title">"Creați o cameră"</string>
|
||||
<string name="screen_create_room_topic_label">"Subiect (opțional)"</string>
|
||||
<string name="screen_room_directory_search_title">"Director de camere"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"A apărut o eroare la încercarea începerii conversației"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@
|
||||
<string name="screen_create_room_room_visibility_section_title">"Видимость комнаты"</string>
|
||||
<string name="screen_create_room_title">"Создать комнату"</string>
|
||||
<string name="screen_create_room_topic_label">"Тема (необязательно)"</string>
|
||||
<string name="screen_room_directory_search_title">"Каталог комнат"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Произошла ошибка при запуске чата"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Присоединиться к комнате по адресу"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Недействительный адрес"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Ввести…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Соответствующая комната найдена"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Комната не найдена"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"прим. #room-name:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Môžete to kedykoľvek zmeniť v nastaveniach miestnosti."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Viditeľnosť miestnosti"</string>
|
||||
<string name="screen_create_room_title">"Vytvoriť miestnosť"</string>
|
||||
<string name="screen_create_room_topic_label">"Téma (voliteľné)"</string>
|
||||
<string name="screen_room_directory_search_title">"Adresár miestností"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Pri pokuse o spustenie konverzácie sa vyskytla chyba"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Pripojte sa do miestnosti podľa adresy"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Neplatná adresa"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Zadajte…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Nájdená zodpovedajúca miestnosť"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Miestnosť sa nenašla"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"napr. #nazov-miestnosti:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ Du kan ändra detta när som helst i rumsinställningarna."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Rumssynlighet"</string>
|
||||
<string name="screen_create_room_title">"Skapa ett rum"</string>
|
||||
<string name="screen_create_room_topic_label">"Ämne (valfritt)"</string>
|
||||
<string name="screen_room_directory_search_title">"Rumskatalog"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Ett fel uppstod när du försökte starta en chatt"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Gå med i rum med adress"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Inte en giltig adress"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Ange …"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Matchande rum hittades"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Rummet hittades inte"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"t.ex. #rumsnamn:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,6 +19,4 @@ Bunu istediğiniz zaman oda ayarlarından değiştirebilirsiniz."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Oda görünürlüğü"</string>
|
||||
<string name="screen_create_room_title">"Bir oda oluştur"</string>
|
||||
<string name="screen_create_room_topic_label">"Konu (isteğe bağlı)"</string>
|
||||
<string name="screen_room_directory_search_title">"Oda dizini"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Sohbet başlatmaya çalışırken bir hata oluştu"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@
|
||||
<string name="screen_create_room_room_visibility_section_title">"Видимість кімнати"</string>
|
||||
<string name="screen_create_room_title">"Створити кімнату"</string>
|
||||
<string name="screen_create_room_topic_label">"Тема (необов\'язково)"</string>
|
||||
<string name="screen_room_directory_search_title">"Каталог кімнат"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Під час спроби почати бесіду сталася помилка"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Приєднатися до кімнати за адресою"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Недійсна адреса"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Введіть…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Знайдено відповідну кімнату"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Кімната не знайдена"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"наприклад, #room-name:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -11,6 +11,4 @@
|
||||
<string name="screen_create_room_room_name_label">"کمرے کا نام"</string>
|
||||
<string name="screen_create_room_title">"ایک کمرہ بنائیں"</string>
|
||||
<string name="screen_create_room_topic_label">"موضوع (اختیاری)"</string>
|
||||
<string name="screen_room_directory_search_title">"کمرے کا راہنامچہ"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"گفتگو شروع کرنے کی کوشش کرتے وقت ایک خرابی واقع ہوگئی"</string>
|
||||
</resources>
|
||||
|
||||
@@ -9,5 +9,4 @@
|
||||
<string name="screen_create_room_room_name_label">"Xona nomi"</string>
|
||||
<string name="screen_create_room_title">"Xonani yaratish"</string>
|
||||
<string name="screen_create_room_topic_label">"Mavzu (ixtiyoriy)"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Suhbatni boshlashda xatolik yuz berdi"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@
|
||||
<string name="screen_create_room_room_visibility_section_title">"聊天室能見度"</string>
|
||||
<string name="screen_create_room_title">"建立聊天室"</string>
|
||||
<string name="screen_create_room_topic_label">"主題(非必填)"</string>
|
||||
<string name="screen_room_directory_search_title">"聊天室目錄"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"嘗試開始聊天時發生錯誤"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"按地址加入聊天室"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"不是有效的位址"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"輸入……"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"找到相符的聊天室"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"找不到聊天室"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"例如 #room-name:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,6 +19,4 @@
|
||||
<string name="screen_create_room_room_visibility_section_title">"房间可见性"</string>
|
||||
<string name="screen_create_room_title">"创建聊天室"</string>
|
||||
<string name="screen_create_room_topic_label">"主题(可选)"</string>
|
||||
<string name="screen_room_directory_search_title">"聊天室目录"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"在开始聊天时发生了错误"</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,12 +19,4 @@ You can change this anytime in room settings."</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Room visibility"</string>
|
||||
<string name="screen_create_room_title">"Create a room"</string>
|
||||
<string name="screen_create_room_topic_label">"Topic (optional)"</string>
|
||||
<string name="screen_room_directory_search_title">"Room directory"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"An error occurred when trying to start a chat"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Join room by address"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Not a valid address"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Enter…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Matching room found"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Room not found"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"e.g. #room-name:matrix.org"</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 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.createroom.impl.addpeople
|
||||
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.createroom.impl.CreateRoomDataStore
|
||||
import io.element.android.features.createroom.impl.userlist.FakeUserListPresenterFactory
|
||||
import io.element.android.features.createroom.impl.userlist.UserListDataStore
|
||||
import io.element.android.libraries.matrix.test.room.alias.FakeRoomAliasHelper
|
||||
import io.element.android.libraries.usersearch.test.FakeUserRepository
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class AddPeoplePresenterTest {
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
private lateinit var presenter: AddPeoplePresenter
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
presenter = AddPeoplePresenter(
|
||||
FakeUserListPresenterFactory(),
|
||||
FakeUserRepository(),
|
||||
CreateRoomDataStore(UserListDataStore(), FakeRoomAliasHelper())
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
// TODO This doesn't actually test anything...
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 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.createroom.impl.addpeople
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.createroom.impl.userlist.UserListEvents
|
||||
import io.element.android.features.createroom.impl.userlist.UserListState
|
||||
import io.element.android.features.createroom.impl.userlist.aUserListState
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.clickOn
|
||||
import io.element.android.tests.testutils.ensureCalledOnce
|
||||
import io.element.android.tests.testutils.pressBack
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AddPeopleViewTest {
|
||||
@get:Rule
|
||||
val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun `clicking on back invokes the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<UserListEvents>()
|
||||
ensureCalledOnce {
|
||||
rule.setAddPeopleView(
|
||||
aUserListState(
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
onBackClick = it
|
||||
)
|
||||
rule.pressBack()
|
||||
}
|
||||
eventsRecorder.assertSingle(UserListEvents.UpdateSearchQuery(""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on back during search emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<UserListEvents>()
|
||||
rule.setAddPeopleView(
|
||||
aUserListState(
|
||||
isSearchActive = true,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.pressBack()
|
||||
eventsRecorder.assertSingle(UserListEvents.OnSearchActiveChanged(false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on skip invokes the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<UserListEvents>()
|
||||
ensureCalledOnce {
|
||||
rule.setAddPeopleView(
|
||||
aUserListState(
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
onNextClick = it
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_skip)
|
||||
}
|
||||
eventsRecorder.assertSingle(UserListEvents.UpdateSearchQuery(""))
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setAddPeopleView(
|
||||
state: UserListState,
|
||||
onBackClick: () -> Unit = EnsureNeverCalled(),
|
||||
onNextClick: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
setContent {
|
||||
AddPeopleView(
|
||||
state = state,
|
||||
onBackClick = onBackClick,
|
||||
onNextClick = onNextClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,21 @@
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.createroom.impl.configureroom
|
||||
package io.element.android.features.startchat.impl.configureroom
|
||||
|
||||
import android.net.Uri
|
||||
import app.cash.turbine.TurbineTestContext
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import im.vector.app.features.analytics.plan.CreatedRoom
|
||||
import io.element.android.features.createroom.impl.CreateRoomConfig
|
||||
import io.element.android.features.createroom.impl.CreateRoomDataStore
|
||||
import io.element.android.features.createroom.impl.userlist.UserListDataStore
|
||||
import io.element.android.features.createroom.impl.configureroom.ConfigureRoomEvents
|
||||
import io.element.android.features.createroom.impl.configureroom.ConfigureRoomPresenter
|
||||
import io.element.android.features.createroom.impl.configureroom.ConfigureRoomState
|
||||
import io.element.android.features.createroom.impl.configureroom.CreateRoomConfig
|
||||
import io.element.android.features.createroom.impl.configureroom.CreateRoomConfigStore
|
||||
import io.element.android.features.createroom.impl.configureroom.RoomAccess
|
||||
import io.element.android.features.createroom.impl.configureroom.RoomAddress
|
||||
import io.element.android.features.createroom.impl.configureroom.RoomVisibilityItem
|
||||
import io.element.android.features.createroom.impl.configureroom.RoomVisibilityState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
@@ -28,7 +34,6 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.alias.FakeRoomAliasHelper
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||
import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity
|
||||
import io.element.android.libraries.mediapickers.api.PickerProvider
|
||||
@@ -48,8 +53,6 @@ import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkAll
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
@@ -67,7 +70,7 @@ private const val AN_URI_FROM_CAMERA_2 = "content://uri_from_camera_2"
|
||||
private const val AN_URI_FROM_GALLERY = "content://uri_from_gallery"
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class ConfigureBaseRoomPresenterTest {
|
||||
class ConfigureRoomPresenterTest {
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
@@ -124,12 +127,11 @@ class ConfigureBaseRoomPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - state is updated when fields are changed`() = runTest {
|
||||
val userListDataStore = UserListDataStore()
|
||||
val pickerProvider = FakePickerProvider()
|
||||
val permissionsPresenter = FakePermissionsPresenter()
|
||||
val roomAliasHelper = FakeRoomAliasHelper()
|
||||
val presenter = createConfigureRoomPresenter(
|
||||
createRoomDataStore = CreateRoomDataStore(userListDataStore, roomAliasHelper),
|
||||
dataStore = CreateRoomConfigStore(roomAliasHelper),
|
||||
pickerProvider = pickerProvider,
|
||||
permissionsPresenter = permissionsPresenter,
|
||||
)
|
||||
@@ -137,20 +139,9 @@ class ConfigureBaseRoomPresenterTest {
|
||||
val initialState = initialState()
|
||||
var expectedConfig = CreateRoomConfig()
|
||||
assertThat(initialState.config).isEqualTo(expectedConfig)
|
||||
|
||||
// Select User
|
||||
val selectedUser1 = aMatrixUser()
|
||||
val selectedUser2 = aMatrixUser("@id_of_bob:server.org", "Bob")
|
||||
userListDataStore.selectUser(selectedUser1)
|
||||
skipItems(1)
|
||||
userListDataStore.selectUser(selectedUser2)
|
||||
var newState = awaitItem()
|
||||
expectedConfig = expectedConfig.copy(invites = persistentListOf(selectedUser1, selectedUser2))
|
||||
assertThat(newState.config).isEqualTo(expectedConfig)
|
||||
|
||||
// Room name
|
||||
initialState.eventSink(ConfigureRoomEvents.RoomNameChanged(A_ROOM_NAME))
|
||||
newState = awaitItem()
|
||||
var newState = awaitItem()
|
||||
expectedConfig = expectedConfig.copy(roomName = A_ROOM_NAME)
|
||||
assertThat(newState.config).isEqualTo(expectedConfig)
|
||||
|
||||
@@ -206,12 +197,6 @@ class ConfigureBaseRoomPresenterTest {
|
||||
)
|
||||
)
|
||||
assertThat(newState.config).isEqualTo(expectedConfig)
|
||||
|
||||
// Remove user
|
||||
newState.eventSink(ConfigureRoomEvents.RemoveUserFromSelection(selectedUser1))
|
||||
newState = awaitItem()
|
||||
expectedConfig = expectedConfig.copy(invites = expectedConfig.invites.minus(selectedUser1).toImmutableList())
|
||||
assertThat(newState.config).isEqualTo(expectedConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,16 +248,16 @@ class ConfigureBaseRoomPresenterTest {
|
||||
val matrixClient = createMatrixClient()
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val mediaPreProcessor = FakeMediaPreProcessor()
|
||||
val createRoomDataStore = CreateRoomDataStore(UserListDataStore(), FakeRoomAliasHelper())
|
||||
val dataStore = CreateRoomConfigStore(FakeRoomAliasHelper())
|
||||
val presenter = createConfigureRoomPresenter(
|
||||
createRoomDataStore = createRoomDataStore,
|
||||
dataStore = dataStore,
|
||||
mediaPreProcessor = mediaPreProcessor,
|
||||
matrixClient = matrixClient,
|
||||
analyticsService = analyticsService
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = initialState()
|
||||
createRoomDataStore.setAvatarUri(Uri.parse(AN_URI_FROM_GALLERY))
|
||||
dataStore.setAvatarUri(Uri.parse(AN_URI_FROM_GALLERY))
|
||||
skipItems(1)
|
||||
mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(mockk(), mockk(), mockk())))
|
||||
matrixClient.givenUploadMediaResult(Result.failure(AN_EXCEPTION))
|
||||
@@ -405,7 +390,7 @@ class ConfigureBaseRoomPresenterTest {
|
||||
|
||||
private fun createConfigureRoomPresenter(
|
||||
roomAliasHelper: RoomAliasHelper = FakeRoomAliasHelper(),
|
||||
createRoomDataStore: CreateRoomDataStore = CreateRoomDataStore(UserListDataStore(), roomAliasHelper),
|
||||
dataStore: CreateRoomConfigStore = CreateRoomConfigStore(roomAliasHelper),
|
||||
matrixClient: MatrixClient = createMatrixClient(),
|
||||
pickerProvider: PickerProvider = FakePickerProvider(),
|
||||
mediaPreProcessor: MediaPreProcessor = FakeMediaPreProcessor(),
|
||||
@@ -414,7 +399,7 @@ class ConfigureBaseRoomPresenterTest {
|
||||
isKnockFeatureEnabled: Boolean = true,
|
||||
mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(),
|
||||
) = ConfigureRoomPresenter(
|
||||
dataStore = createRoomDataStore,
|
||||
dataStore = dataStore,
|
||||
matrixClient = matrixClient,
|
||||
mediaPickerProvider = pickerProvider,
|
||||
mediaPreProcessor = mediaPreProcessor,
|
||||
@@ -22,7 +22,7 @@ interface HomeEntryPoint : FeatureEntryPoint {
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun onRoomClick(roomId: RoomId)
|
||||
fun onCreateRoomClick()
|
||||
fun onStartChatClick()
|
||||
fun onSettingsClick()
|
||||
fun onSetUpRecoveryClick()
|
||||
fun onSessionConfirmRecoveryKeyClick()
|
||||
|
||||
@@ -120,8 +120,8 @@ class HomeFlowNode @AssistedInject constructor(
|
||||
plugins<HomeEntryPoint.Callback>().forEach { it.onSettingsClick() }
|
||||
}
|
||||
|
||||
private fun onCreateRoomClick() {
|
||||
plugins<HomeEntryPoint.Callback>().forEach { it.onCreateRoomClick() }
|
||||
private fun onStartChatClick() {
|
||||
plugins<HomeEntryPoint.Callback>().forEach { it.onStartChatClick() }
|
||||
}
|
||||
|
||||
private fun onSetUpRecoveryClick() {
|
||||
@@ -171,7 +171,7 @@ class HomeFlowNode @AssistedInject constructor(
|
||||
homeState = state,
|
||||
onRoomClick = this::onRoomClick,
|
||||
onSettingsClick = this::onOpenSettings,
|
||||
onCreateRoomClick = this::onCreateRoomClick,
|
||||
onStartChatClick = this::onStartChatClick,
|
||||
onSetUpRecoveryClick = this::onSetUpRecoveryClick,
|
||||
onConfirmRecoveryKeyClick = this::onSessionConfirmRecoveryKeyClick,
|
||||
onRoomSettingsClick = this::onRoomSettingsClick,
|
||||
|
||||
@@ -72,7 +72,7 @@ fun HomeView(
|
||||
onSettingsClick: () -> Unit,
|
||||
onSetUpRecoveryClick: () -> Unit,
|
||||
onConfirmRecoveryKeyClick: () -> Unit,
|
||||
onCreateRoomClick: () -> Unit,
|
||||
onStartChatClick: () -> Unit,
|
||||
onRoomSettingsClick: (roomId: RoomId) -> Unit,
|
||||
onMenuActionClick: (RoomListMenuAction) -> Unit,
|
||||
onReportRoomClick: (roomId: RoomId) -> Unit,
|
||||
@@ -116,7 +116,7 @@ fun HomeView(
|
||||
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
||||
onRoomClick = { if (firstThrottler.canHandle()) onRoomClick(it) },
|
||||
onOpenSettings = { if (firstThrottler.canHandle()) onSettingsClick() },
|
||||
onCreateRoomClick = { if (firstThrottler.canHandle()) onCreateRoomClick() },
|
||||
onStartChatClick = { if (firstThrottler.canHandle()) onStartChatClick() },
|
||||
onMenuActionClick = onMenuActionClick,
|
||||
modifier = Modifier.padding(top = topPadding),
|
||||
)
|
||||
@@ -145,7 +145,7 @@ private fun HomeScaffold(
|
||||
onConfirmRecoveryKeyClick: () -> Unit,
|
||||
onRoomClick: (RoomId) -> Unit,
|
||||
onOpenSettings: () -> Unit,
|
||||
onCreateRoomClick: () -> Unit,
|
||||
onStartChatClick: () -> Unit,
|
||||
onMenuActionClick: (RoomListMenuAction) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -236,7 +236,7 @@ private fun HomeScaffold(
|
||||
onSetUpRecoveryClick = onSetUpRecoveryClick,
|
||||
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
||||
onRoomClick = ::onRoomClick,
|
||||
onCreateRoomClick = onCreateRoomClick,
|
||||
onCreateRoomClick = onStartChatClick,
|
||||
contentPadding = PaddingValues(
|
||||
// FAB height is 56dp, bottom padding is 16dp, we add 8dp as extra margin -> 56+16+8 = 80,
|
||||
// and include provided bottom padding
|
||||
@@ -280,7 +280,7 @@ private fun HomeScaffold(
|
||||
floatingActionButton = {
|
||||
if (state.displayActions) {
|
||||
FloatingActionButton(
|
||||
onClick = onCreateRoomClick,
|
||||
onClick = onStartChatClick,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Plus(),
|
||||
@@ -304,7 +304,7 @@ internal fun HomeViewPreview(@PreviewParameter(HomeStateProvider::class) state:
|
||||
onSettingsClick = {},
|
||||
onSetUpRecoveryClick = {},
|
||||
onConfirmRecoveryKeyClick = {},
|
||||
onCreateRoomClick = {},
|
||||
onStartChatClick = {},
|
||||
onRoomSettingsClick = {},
|
||||
onReportRoomClick = {},
|
||||
onMenuActionClick = {},
|
||||
|
||||
@@ -33,6 +33,7 @@ For now, you can deselect filters in order to see your other chats"</string>
|
||||
<string name="screen_roomlist_filter_invites">"Invites"</string>
|
||||
<string name="screen_roomlist_filter_invites_empty_state_title">"You don\'t have any pending invites."</string>
|
||||
<string name="screen_roomlist_filter_low_priority">"Low Priority"</string>
|
||||
<string name="screen_roomlist_filter_low_priority_empty_state_title">"You don’t have any low priority chats yet"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"You can deselect filters in order to see your other chats"</string>
|
||||
<string name="screen_roomlist_filter_mixed_empty_state_title">"You don’t have chats for this selection"</string>
|
||||
<string name="screen_roomlist_filter_people">"People"</string>
|
||||
|
||||
@@ -284,7 +284,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomL
|
||||
onSettingsClick = onSettingsClick,
|
||||
onSetUpRecoveryClick = onSetUpRecoveryClick,
|
||||
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
||||
onCreateRoomClick = onCreateRoomClick,
|
||||
onStartChatClick = onCreateRoomClick,
|
||||
onRoomSettingsClick = onRoomSettingsClick,
|
||||
onMenuActionClick = onMenuActionClick,
|
||||
onDeclineInviteAndBlockUser = onDeclineInviteAndBlockUser,
|
||||
|
||||
18
features/invitepeople/api/build.gradle.kts
Normal file
18
features/invitepeople/api/build.gradle.kts
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 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.
|
||||
*/
|
||||
plugins {
|
||||
id("io.element.android-compose-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.features.invitepeople.api"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.invitepeople.api
|
||||
|
||||
interface InvitePeopleEvents {
|
||||
data object SendInvites : InvitePeopleEvents
|
||||
data object CloseSearch : InvitePeopleEvents
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.invitepeople.api
|
||||
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
|
||||
interface InvitePeoplePresenter : Presenter<InvitePeopleState> {
|
||||
interface Factory {
|
||||
fun create(
|
||||
joinedRoom: JoinedRoom?,
|
||||
roomId: RoomId,
|
||||
): InvitePeoplePresenter
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.invitepeople.api
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
interface InvitePeopleRenderer {
|
||||
@Composable
|
||||
fun Render(
|
||||
state: InvitePeopleState,
|
||||
modifier: Modifier,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.invitepeople.api
|
||||
|
||||
interface InvitePeopleState {
|
||||
val canInvite: Boolean
|
||||
val isSearchActive: Boolean
|
||||
val eventSink: (InvitePeopleEvents) -> Unit
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.invitepeople.api
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
|
||||
class InvitePeopleStateProvider : PreviewParameterProvider<InvitePeopleState> {
|
||||
override val values: Sequence<InvitePeopleState>
|
||||
get() = sequenceOf(
|
||||
aPreviewInvitePeopleState(),
|
||||
aPreviewInvitePeopleState(canInvite = true),
|
||||
aPreviewInvitePeopleState(isSearchActive = true)
|
||||
)
|
||||
}
|
||||
|
||||
private data class PreviewInvitePeopleState(
|
||||
override val canInvite: Boolean,
|
||||
override val isSearchActive: Boolean,
|
||||
override val eventSink: (InvitePeopleEvents) -> Unit,
|
||||
) : InvitePeopleState
|
||||
|
||||
private fun aPreviewInvitePeopleState(
|
||||
canInvite: Boolean = false,
|
||||
isSearchActive: Boolean = false,
|
||||
eventSink: (InvitePeopleEvents) -> Unit = {},
|
||||
) = PreviewInvitePeopleState(
|
||||
canInvite = canInvite,
|
||||
isSearchActive = isSearchActive,
|
||||
eventSink = eventSink
|
||||
)
|
||||
53
features/invitepeople/impl/build.gradle.kts
Normal file
53
features/invitepeople/impl/build.gradle.kts
Normal file
@@ -0,0 +1,53 @@
|
||||
import extension.setupAnvil
|
||||
|
||||
/*
|
||||
* Copyright 2022-2024 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("io.element.android-compose-library")
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.features.invitepeople.impl"
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
isIncludeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupAnvil()
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.matrixui)
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.libraries.androidutils)
|
||||
implementation(projects.libraries.usersearch.impl)
|
||||
implementation(libs.coil.compose)
|
||||
implementation(projects.services.apperror.api)
|
||||
api(projects.features.invitepeople.api)
|
||||
|
||||
testImplementation(libs.test.junit)
|
||||
testImplementation(libs.test.mockk)
|
||||
testImplementation(libs.coroutines.test)
|
||||
testImplementation(libs.molecule.runtime)
|
||||
testImplementation(libs.test.truth)
|
||||
testImplementation(libs.test.turbine)
|
||||
testImplementation(libs.test.robolectric)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.usersearch.test)
|
||||
testImplementation(projects.services.apperror.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
testImplementation(libs.androidx.compose.ui.test.junit)
|
||||
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 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.invitepeople.impl
|
||||
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvents
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
||||
sealed interface DefaultInvitePeopleEvents : InvitePeopleEvents {
|
||||
data class ToggleUser(val user: MatrixUser) : DefaultInvitePeopleEvents
|
||||
data class UpdateSearchQuery(val query: String) : DefaultInvitePeopleEvents
|
||||
data class OnSearchActiveChanged(val active: Boolean) : DefaultInvitePeopleEvents
|
||||
}
|
||||
@@ -5,50 +5,88 @@
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.roomdetails.impl.invite
|
||||
package io.element.android.features.invitepeople.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.features.roomdetails.impl.members.RoomMemberListDataSource
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvents
|
||||
import io.element.android.features.invitepeople.api.InvitePeoplePresenter
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleState
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.map
|
||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||
import io.element.android.libraries.matrix.api.room.filterMembers
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.libraries.usersearch.api.UserRepository
|
||||
import io.element.android.services.apperror.api.AppErrorStateService
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomInviteMembersPresenter @Inject constructor(
|
||||
class DefaultInvitePeoplePresenter @AssistedInject constructor(
|
||||
@Assisted private val joinedRoom: JoinedRoom?,
|
||||
@Assisted private val roomId: RoomId,
|
||||
private val userRepository: UserRepository,
|
||||
private val roomMemberListDataSource: RoomMemberListDataSource,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
) : Presenter<RoomInviteMembersState> {
|
||||
@SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope,
|
||||
private val appErrorStateService: AppErrorStateService,
|
||||
private val matrixClient: MatrixClient,
|
||||
) : InvitePeoplePresenter {
|
||||
@AssistedFactory
|
||||
@ContributesBinding(SessionScope::class)
|
||||
interface Factory : InvitePeoplePresenter.Factory {
|
||||
override fun create(joinedRoom: JoinedRoom?, roomId: RoomId): DefaultInvitePeoplePresenter
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun present(): RoomInviteMembersState {
|
||||
override fun present(): InvitePeopleState {
|
||||
val roomMembers = remember { mutableStateOf<AsyncData<ImmutableList<RoomMember>>>(AsyncData.Loading()) }
|
||||
val selectedUsers = remember { mutableStateOf<ImmutableList<MatrixUser>>(persistentListOf()) }
|
||||
val searchResults = remember { mutableStateOf<SearchBarResultState<ImmutableList<InvitableUser>>>(SearchBarResultState.Initial()) }
|
||||
var searchQuery by rememberSaveable { mutableStateOf("") }
|
||||
var searchActive by rememberSaveable { mutableStateOf(false) }
|
||||
val showSearchLoader = rememberSaveable { mutableStateOf(false) }
|
||||
val room by produceState(if (joinedRoom != null) AsyncData.Success(joinedRoom) else AsyncData.Loading()) {
|
||||
if (joinedRoom == null) {
|
||||
val result = matrixClient.getJoinedRoom(roomId)
|
||||
value = if (result == null) {
|
||||
AsyncData.Failure(Exception("Room not found"))
|
||||
} else {
|
||||
AsyncData.Success(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
fetchMembers(roomMembers)
|
||||
LaunchedEffect(room.isSuccess()) {
|
||||
room.dataOrNull()?.let {
|
||||
fetchMembers(it, roomMembers)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(searchQuery, roomMembers) {
|
||||
performSearch(
|
||||
@@ -60,33 +98,61 @@ class RoomInviteMembersPresenter @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
return RoomInviteMembersState(
|
||||
fun handleEvents(event: InvitePeopleEvents) {
|
||||
when (event) {
|
||||
is DefaultInvitePeopleEvents.OnSearchActiveChanged -> {
|
||||
searchActive = event.active
|
||||
searchQuery = ""
|
||||
}
|
||||
|
||||
is DefaultInvitePeopleEvents.UpdateSearchQuery -> {
|
||||
searchQuery = event.query
|
||||
}
|
||||
|
||||
is DefaultInvitePeopleEvents.ToggleUser -> {
|
||||
selectedUsers.toggleUser(event.user)
|
||||
searchResults.toggleUser(event.user)
|
||||
}
|
||||
is InvitePeopleEvents.SendInvites -> {
|
||||
room.dataOrNull()?.let {
|
||||
sessionCoroutineScope.sendInvites(it, selectedUsers.value)
|
||||
}
|
||||
}
|
||||
is InvitePeopleEvents.CloseSearch -> {
|
||||
searchActive = false
|
||||
searchQuery = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DefaultInvitePeopleState(
|
||||
room = room.map { },
|
||||
canInvite = selectedUsers.value.isNotEmpty(),
|
||||
selectedUsers = selectedUsers.value,
|
||||
searchQuery = searchQuery,
|
||||
isSearchActive = searchActive,
|
||||
searchResults = searchResults.value,
|
||||
showSearchLoader = showSearchLoader.value,
|
||||
eventSink = {
|
||||
when (it) {
|
||||
is RoomInviteMembersEvents.OnSearchActiveChanged -> {
|
||||
searchActive = it.active
|
||||
searchQuery = ""
|
||||
}
|
||||
|
||||
is RoomInviteMembersEvents.UpdateSearchQuery -> {
|
||||
searchQuery = it.query
|
||||
}
|
||||
|
||||
is RoomInviteMembersEvents.ToggleUser -> {
|
||||
selectedUsers.toggleUser(it.user)
|
||||
searchResults.toggleUser(it.user)
|
||||
}
|
||||
}
|
||||
}
|
||||
eventSink = ::handleEvents,
|
||||
)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.sendInvites(
|
||||
room: JoinedRoom,
|
||||
selectedUsers: List<MatrixUser>,
|
||||
) = launch {
|
||||
val anyInviteFailed = selectedUsers
|
||||
.map { room.inviteUserById(it.userId) }
|
||||
.any { it.isFailure }
|
||||
|
||||
if (anyInviteFailed) {
|
||||
appErrorStateService.showError(
|
||||
titleRes = CommonStrings.common_unable_to_invite_title,
|
||||
bodyRes = CommonStrings.common_unable_to_invite_message,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmName("toggleUserInSelectedUsers")
|
||||
private fun MutableState<ImmutableList<MatrixUser>>.toggleUser(user: MatrixUser) {
|
||||
value = if (value.contains(user)) {
|
||||
@@ -134,7 +200,7 @@ class RoomInviteMembersPresenter @Inject constructor(
|
||||
val isInvited = existingMembership == RoomMembershipState.INVITE
|
||||
InvitableUser(
|
||||
matrixUser = result.matrixUser,
|
||||
isSelected = selectedUsers.value.contains(result.matrixUser) || isJoined || isInvited,
|
||||
isSelected = selectedUsers.value.contains(result.matrixUser),
|
||||
isAlreadyJoined = isJoined,
|
||||
isAlreadyInvited = isInvited,
|
||||
isUnresolved = result.isUnresolved,
|
||||
@@ -144,11 +210,12 @@ class RoomInviteMembersPresenter @Inject constructor(
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
private suspend fun fetchMembers(roomMembers: MutableState<AsyncData<ImmutableList<RoomMember>>>) {
|
||||
private suspend fun fetchMembers(
|
||||
room: JoinedRoom,
|
||||
roomMembers: MutableState<AsyncData<ImmutableList<RoomMember>>>
|
||||
) {
|
||||
suspend {
|
||||
withContext(coroutineDispatchers.io) {
|
||||
roomMemberListDataSource.search("").toImmutableList()
|
||||
}
|
||||
room.filterMembers("", coroutineDispatchers.io).toImmutableList()
|
||||
}.runCatchingUpdatingState(roomMembers)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.invitepeople.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleRenderer
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleState
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultInvitePeopleRenderer @Inject constructor() : InvitePeopleRenderer {
|
||||
@Composable
|
||||
override fun Render(state: InvitePeopleState, modifier: Modifier) {
|
||||
if (state is DefaultInvitePeopleState) {
|
||||
InvitePeopleView(
|
||||
state = state,
|
||||
modifier = modifier
|
||||
)
|
||||
} else {
|
||||
error("Unsupported state type: ${state::javaClass}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,26 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 New Vector Ltd.
|
||||
* 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.roomdetails.impl.invite
|
||||
package io.element.android.features.invitepeople.impl
|
||||
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleEvents
|
||||
import io.element.android.features.invitepeople.api.InvitePeopleState
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class RoomInviteMembersState(
|
||||
val canInvite: Boolean,
|
||||
data class DefaultInvitePeopleState(
|
||||
val room: AsyncData<Unit>,
|
||||
override val canInvite: Boolean,
|
||||
val searchQuery: String,
|
||||
val showSearchLoader: Boolean,
|
||||
val searchResults: SearchBarResultState<ImmutableList<InvitableUser>>,
|
||||
val selectedUsers: ImmutableList<MatrixUser>,
|
||||
val isSearchActive: Boolean,
|
||||
val eventSink: (RoomInviteMembersEvents) -> Unit,
|
||||
)
|
||||
|
||||
data class InvitableUser(
|
||||
val matrixUser: MatrixUser,
|
||||
val isSelected: Boolean = false,
|
||||
val isAlreadyJoined: Boolean = false,
|
||||
val isAlreadyInvited: Boolean = false,
|
||||
val isUnresolved: Boolean = false,
|
||||
)
|
||||
override val isSearchActive: Boolean,
|
||||
override val eventSink: (InvitePeopleEvents) -> Unit
|
||||
) : InvitePeopleState
|
||||
@@ -5,9 +5,10 @@
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.roomdetails.impl.invite
|
||||
package io.element.android.features.invitepeople.impl
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||
@@ -16,15 +17,15 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
internal class RoomInviteMembersStateProvider : PreviewParameterProvider<RoomInviteMembersState> {
|
||||
override val values: Sequence<RoomInviteMembersState>
|
||||
internal class DefaultInvitePeopleStateProvider : PreviewParameterProvider<DefaultInvitePeopleState> {
|
||||
override val values: Sequence<DefaultInvitePeopleState>
|
||||
get() = sequenceOf(
|
||||
aRoomInviteMembersState(),
|
||||
aRoomInviteMembersState(canInvite = true, selectedUsers = aMatrixUserList().toImmutableList()),
|
||||
aRoomInviteMembersState(isSearchActive = true, searchQuery = "some query"),
|
||||
aRoomInviteMembersState(isSearchActive = true, searchQuery = "some query", selectedUsers = aMatrixUserList().toImmutableList()),
|
||||
aRoomInviteMembersState(isSearchActive = true, searchQuery = "some query", searchResults = SearchBarResultState.NoResultsFound()),
|
||||
aRoomInviteMembersState(
|
||||
aDefaultInvitePeopleState(),
|
||||
aDefaultInvitePeopleState(canInvite = true, selectedUsers = aMatrixUserList().toImmutableList()),
|
||||
aDefaultInvitePeopleState(isSearchActive = true, searchQuery = "some query"),
|
||||
aDefaultInvitePeopleState(isSearchActive = true, searchQuery = "some query", selectedUsers = aMatrixUserList().toImmutableList()),
|
||||
aDefaultInvitePeopleState(isSearchActive = true, searchQuery = "some query", searchResults = SearchBarResultState.NoResultsFound()),
|
||||
aDefaultInvitePeopleState(
|
||||
isSearchActive = true,
|
||||
canInvite = true,
|
||||
searchQuery = "some query",
|
||||
@@ -33,15 +34,15 @@ internal class RoomInviteMembersStateProvider : PreviewParameterProvider<RoomInv
|
||||
),
|
||||
searchResults = SearchBarResultState.Results(
|
||||
persistentListOf(
|
||||
InvitableUser(aMatrixUser("@alice:server.org")),
|
||||
InvitableUser(aMatrixUser("@bob:server.org", "Bob")),
|
||||
InvitableUser(aMatrixUser("@carol:server.org", "Carol"), isSelected = true),
|
||||
InvitableUser(aMatrixUser("@eve:server.org", "Eve"), isSelected = true, isAlreadyJoined = true),
|
||||
InvitableUser(aMatrixUser("@justin:server.org", "Justin"), isSelected = true, isAlreadyInvited = true),
|
||||
anInvitableUser(aMatrixUser("@alice:server.org")),
|
||||
anInvitableUser(aMatrixUser("@bob:server.org", "Bob")),
|
||||
anInvitableUser(aMatrixUser("@carol:server.org", "Carol"), isSelected = true),
|
||||
anInvitableUser(aMatrixUser("@eve:server.org", "Eve"), isSelected = true, isAlreadyJoined = true),
|
||||
anInvitableUser(aMatrixUser("@justin:server.org", "Justin"), isSelected = true, isAlreadyInvited = true),
|
||||
)
|
||||
)
|
||||
),
|
||||
aRoomInviteMembersState(
|
||||
aDefaultInvitePeopleState(
|
||||
isSearchActive = true,
|
||||
canInvite = true,
|
||||
searchQuery = "@alice:server.org",
|
||||
@@ -50,34 +51,51 @@ internal class RoomInviteMembersStateProvider : PreviewParameterProvider<RoomInv
|
||||
),
|
||||
searchResults = SearchBarResultState.Results(
|
||||
persistentListOf(
|
||||
InvitableUser(aMatrixUser("@alice:server.org"), isUnresolved = true),
|
||||
InvitableUser(aMatrixUser("@bob:server.org", "Bob")),
|
||||
anInvitableUser(aMatrixUser("@alice:server.org"), isUnresolved = true),
|
||||
anInvitableUser(aMatrixUser("@bob:server.org", "Bob")),
|
||||
)
|
||||
)
|
||||
),
|
||||
aRoomInviteMembersState(
|
||||
aDefaultInvitePeopleState(
|
||||
isSearchActive = true,
|
||||
canInvite = true,
|
||||
searchQuery = "@alice:server.org",
|
||||
searchResults = SearchBarResultState.Results(
|
||||
persistentListOf(
|
||||
InvitableUser(aMatrixUser("@alice:server.org"), isUnresolved = true),
|
||||
anInvitableUser(aMatrixUser("@alice:server.org"), isUnresolved = true),
|
||||
)
|
||||
),
|
||||
showSearchLoader = true,
|
||||
),
|
||||
aDefaultInvitePeopleState(room = AsyncData.Failure(Exception("Room not found"))),
|
||||
)
|
||||
}
|
||||
|
||||
private fun aRoomInviteMembersState(
|
||||
private fun anInvitableUser(
|
||||
matrixUser: MatrixUser,
|
||||
isSelected: Boolean = false,
|
||||
isAlreadyJoined: Boolean = false,
|
||||
isAlreadyInvited: Boolean = false,
|
||||
isUnresolved: Boolean = false,
|
||||
) = InvitableUser(
|
||||
matrixUser = matrixUser,
|
||||
isSelected = isSelected,
|
||||
isAlreadyJoined = isAlreadyJoined,
|
||||
isAlreadyInvited = isAlreadyInvited,
|
||||
isUnresolved = isUnresolved,
|
||||
)
|
||||
|
||||
private fun aDefaultInvitePeopleState(
|
||||
room: AsyncData<Unit> = AsyncData.Success(Unit),
|
||||
canInvite: Boolean = false,
|
||||
searchQuery: String = "",
|
||||
searchResults: SearchBarResultState<ImmutableList<InvitableUser>> = SearchBarResultState.Initial(),
|
||||
selectedUsers: ImmutableList<MatrixUser> = persistentListOf(),
|
||||
isSearchActive: Boolean = false,
|
||||
showSearchLoader: Boolean = false,
|
||||
): RoomInviteMembersState {
|
||||
return RoomInviteMembersState(
|
||||
): DefaultInvitePeopleState {
|
||||
return DefaultInvitePeopleState(
|
||||
room = room,
|
||||
canInvite = canInvite,
|
||||
searchQuery = searchQuery,
|
||||
searchResults = searchResults,
|
||||
@@ -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.invitepeople.impl
|
||||
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
||||
data class InvitableUser(
|
||||
val matrixUser: MatrixUser,
|
||||
val isSelected: Boolean,
|
||||
val isAlreadyJoined: Boolean,
|
||||
val isAlreadyInvited: Boolean,
|
||||
val isUnresolved: Boolean,
|
||||
)
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 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.invitepeople.impl
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.designsystem.components.async.AsyncFailure
|
||||
import io.element.android.libraries.designsystem.components.async.AsyncLoading
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBar
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.components.CheckableUserRow
|
||||
import io.element.android.libraries.matrix.ui.components.CheckableUserRowData
|
||||
import io.element.android.libraries.matrix.ui.components.SelectedUsersRowList
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
import io.element.android.libraries.matrix.ui.model.getBestName
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun InvitePeopleView(
|
||||
state: DefaultInvitePeopleState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (state.room) {
|
||||
is AsyncData.Failure -> InvitePeopleViewError(state.room.error, modifier)
|
||||
AsyncData.Uninitialized,
|
||||
is AsyncData.Loading,
|
||||
is AsyncData.Success -> InvitePeopleContentView(state, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InvitePeopleViewError(
|
||||
error: Throwable,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
AsyncFailure(
|
||||
throwable = error,
|
||||
onRetry = null,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InvitePeopleContentView(
|
||||
state: DefaultInvitePeopleState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
InvitePeopleSearchBar(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
query = state.searchQuery,
|
||||
showLoader = state.showSearchLoader,
|
||||
selectedUsers = state.selectedUsers,
|
||||
state = state.searchResults,
|
||||
active = state.isSearchActive,
|
||||
onActiveChange = {
|
||||
state.eventSink(
|
||||
DefaultInvitePeopleEvents.OnSearchActiveChanged(
|
||||
it
|
||||
)
|
||||
)
|
||||
},
|
||||
onTextChange = { state.eventSink(DefaultInvitePeopleEvents.UpdateSearchQuery(it)) },
|
||||
onToggleUser = { state.eventSink(DefaultInvitePeopleEvents.ToggleUser(it)) },
|
||||
)
|
||||
|
||||
if (!state.isSearchActive) {
|
||||
SelectedUsersRowList(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
selectedUsers = state.selectedUsers,
|
||||
autoScroll = true,
|
||||
onUserRemove = { state.eventSink(DefaultInvitePeopleEvents.ToggleUser(it)) },
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun InvitePeopleSearchBar(
|
||||
query: String,
|
||||
state: SearchBarResultState<ImmutableList<InvitableUser>>,
|
||||
showLoader: Boolean,
|
||||
selectedUsers: ImmutableList<MatrixUser>,
|
||||
active: Boolean,
|
||||
onActiveChange: (Boolean) -> Unit,
|
||||
onTextChange: (String) -> Unit,
|
||||
onToggleUser: (MatrixUser) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
placeHolderTitle: String = stringResource(CommonStrings.common_search_for_someone),
|
||||
) {
|
||||
SearchBar(
|
||||
query = query,
|
||||
onQueryChange = onTextChange,
|
||||
active = active,
|
||||
onActiveChange = onActiveChange,
|
||||
modifier = modifier,
|
||||
placeHolderTitle = placeHolderTitle,
|
||||
contentPrefix = {
|
||||
if (selectedUsers.isNotEmpty()) {
|
||||
SelectedUsersRowList(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
selectedUsers = selectedUsers,
|
||||
autoScroll = true,
|
||||
onUserRemove = onToggleUser,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
)
|
||||
}
|
||||
},
|
||||
showBackButton = false,
|
||||
resultState = state,
|
||||
contentSuffix = {
|
||||
if (showLoader) {
|
||||
AsyncLoading()
|
||||
}
|
||||
},
|
||||
resultHandler = { results ->
|
||||
Text(
|
||||
text = stringResource(id = CommonStrings.common_search_results),
|
||||
style = ElementTheme.typography.fontBodyLgMedium,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, top = 12.dp, end = 16.dp, bottom = 8.dp)
|
||||
)
|
||||
|
||||
LazyColumn {
|
||||
itemsIndexed(results) { index, invitableUser ->
|
||||
val invitedOrJoined = invitableUser.isAlreadyInvited || invitableUser.isAlreadyJoined
|
||||
val isUnresolved = invitableUser.isUnresolved && !invitedOrJoined
|
||||
val enabled = isUnresolved || !invitedOrJoined
|
||||
val data = if (isUnresolved) {
|
||||
CheckableUserRowData.Unresolved(
|
||||
avatarData = invitableUser.matrixUser.getAvatarData(AvatarSize.UserListItem),
|
||||
id = invitableUser.matrixUser.userId.value,
|
||||
)
|
||||
} else {
|
||||
CheckableUserRowData.Resolved(
|
||||
avatarData = invitableUser.matrixUser.getAvatarData(AvatarSize.UserListItem),
|
||||
name = invitableUser.matrixUser.getBestName(),
|
||||
subtext = when {
|
||||
// If they're already invited or joined we show that information
|
||||
invitableUser.isAlreadyJoined -> stringResource(R.string.screen_invite_users_already_a_member)
|
||||
invitableUser.isAlreadyInvited -> stringResource(R.string.screen_invite_users_already_invited)
|
||||
// Otherwise show the ID, unless that's already used for their name
|
||||
invitableUser.matrixUser.displayName.isNullOrEmpty()
|
||||
.not() -> invitableUser.matrixUser.userId.value
|
||||
else -> null
|
||||
}
|
||||
)
|
||||
}
|
||||
CheckableUserRow(
|
||||
checked = invitableUser.isSelected || invitedOrJoined,
|
||||
enabled = enabled,
|
||||
data = data,
|
||||
onCheckedChange = { onToggleUser(invitableUser.matrixUser) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
if (index < results.lastIndex) {
|
||||
HorizontalDivider()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun InvitePeopleViewPreview(@PreviewParameter(DefaultInvitePeopleStateProvider::class) state: DefaultInvitePeopleState) =
|
||||
ElementPreview {
|
||||
InvitePeopleView(state = state)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Ужо ўдзельнік"</string>
|
||||
<string name="screen_invite_users_already_invited">"Ужо запрасілі"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Вече е член"</string>
|
||||
<string name="screen_invite_users_already_invited">"Вече е бил поканен"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Již členem"</string>
|
||||
<string name="screen_invite_users_already_invited">"Již pozván(a)"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Eisoes yn aelod"</string>
|
||||
<string name="screen_invite_users_already_invited">"Wedi gwahodd yn barod"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Allerede medlem"</string>
|
||||
<string name="screen_invite_users_already_invited">"Allerede inviteret"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Bereits Mitglied"</string>
|
||||
<string name="screen_invite_users_already_invited">"Bereits eingeladen"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Ήδη μέλος"</string>
|
||||
<string name="screen_invite_users_already_invited">"Ήδη προσκεκλημένος"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Ya eres miembro"</string>
|
||||
<string name="screen_invite_users_already_invited">"Ya estás invitado"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Sa juba oled jututoa liige"</string>
|
||||
<string name="screen_invite_users_already_invited">"Sa juba oled kutse saanud"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Kidea da dagoeneko"</string>
|
||||
<string name="screen_invite_users_already_invited">"Lehendik ere gonbidatuta"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"از پیش عضو است"</string>
|
||||
<string name="screen_invite_users_already_invited">"از پیش دعوت شده"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"On jo jäsen"</string>
|
||||
<string name="screen_invite_users_already_invited">"On jo kutsuttu"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Déjà membre"</string>
|
||||
<string name="screen_invite_users_already_invited">"Déjà invité(e)"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Már tag"</string>
|
||||
<string name="screen_invite_users_already_invited">"Már meghívták"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Sudah menjadi anggota"</string>
|
||||
<string name="screen_invite_users_already_invited">"Sudah diundang"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Già membro"</string>
|
||||
<string name="screen_invite_users_already_invited">"Già invitato"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"უკვე წევრია"</string>
|
||||
<string name="screen_invite_users_already_invited">"უკვე მოწვეულია"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Jau narys"</string>
|
||||
<string name="screen_invite_users_already_invited">"Jau pakviestas"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Allerede medlem"</string>
|
||||
<string name="screen_invite_users_already_invited">"Allerede invitert"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invite_users_already_a_member">"Reeds lid"</string>
|
||||
<string name="screen_invite_users_already_invited">"Reeds uitgenodigd"</string>
|
||||
</resources>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user