diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt index ed61431511..e55fca755d 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomDataStore.kt @@ -16,20 +16,43 @@ package io.element.android.features.createroom.impl +import io.element.android.features.createroom.impl.configureroom.RoomPrivacy import io.element.android.features.createroom.impl.di.CreateRoomScope +import io.element.android.features.userlist.api.UserListDataStore import io.element.android.libraries.di.SingleIn +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine import javax.inject.Inject @SingleIn(CreateRoomScope::class) -class CreateRoomDataStore @Inject constructor() { +class CreateRoomDataStore @Inject constructor( + val selectedUserListDataStore: UserListDataStore, +) { private val createRoomConfigFlow: MutableStateFlow = MutableStateFlow(CreateRoomConfig()) - fun getCreateRoomConfig(): Flow = createRoomConfigFlow + fun getCreateRoomConfig(): Flow = combine( + selectedUserListDataStore.selectedUsers(), + createRoomConfigFlow, + ) { selectedUsers, config -> + config.copy(invites = selectedUsers.toImmutableList()) + } - fun setCreateRoomConfig(createRoomConfig: CreateRoomConfig) { - createRoomConfigFlow.tryEmit(createRoomConfig) + fun setRoomName(roomName: String?) { + createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(roomName = roomName?.takeIf { it.isNotEmpty() })) + } + + fun setTopic(topic: String?) { + createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(topic = topic?.takeIf { it.isNotEmpty() })) + } + + fun setAvatarUrl(avatarUrl: String?) { + createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(avatarUrl = avatarUrl)) + } + + fun setPrivacy(privacy: RoomPrivacy?) { + createRoomConfigFlow.tryEmit(createRoomConfigFlow.value.copy(privacy = privacy)) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt index 6602e4de14..88e24f58b7 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenter.kt @@ -17,9 +17,6 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.features.userlist.api.SelectionMode import io.element.android.features.userlist.api.UserListDataSource @@ -39,16 +36,13 @@ class AddPeoplePresenter @Inject constructor( userListPresenterFactory.create( UserListPresenterArgs(selectionMode = SelectionMode.Multiple), userListDataSource, + dataStore.selectedUserListDataStore, ) } @Composable override fun present(): AddPeopleState { val userListState = userListPresenter.present() - val createRoomConfig = dataStore.getCreateRoomConfig().collectAsState(CreateRoomConfig()) - LaunchedEffect(userListState.selectedUsers) { - dataStore.setCreateRoomConfig(createRoomConfig.value.copy(invites = userListState.selectedUsers)) - } fun handleEvents(event: AddPeopleEvents) { // do nothing for now } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 7cb5401511..e9211976af 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -24,7 +24,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore import io.element.android.libraries.architecture.Presenter -import kotlinx.collections.immutable.toImmutableList import javax.inject.Inject class ConfigureRoomPresenter @Inject constructor( @@ -41,18 +40,11 @@ class ConfigureRoomPresenter @Inject constructor( fun handleEvents(event: ConfigureRoomEvents) { when (event) { - is ConfigureRoomEvents.AvatarUriChanged -> - dataStore.setCreateRoomConfig(createRoomConfig.value.copy(avatarUrl = event.uri?.toString())) - is ConfigureRoomEvents.RoomNameChanged -> - dataStore.setCreateRoomConfig(createRoomConfig.value.copy(roomName = event.name.takeUnless { it.isEmpty() })) - is ConfigureRoomEvents.TopicChanged -> - dataStore.setCreateRoomConfig(createRoomConfig.value.copy(topic = event.topic.takeUnless { it.isEmpty() })) - is ConfigureRoomEvents.RoomPrivacyChanged -> - dataStore.setCreateRoomConfig(createRoomConfig.value.copy(privacy = event.privacy)) - is ConfigureRoomEvents.RemoveFromSelection -> - dataStore.setCreateRoomConfig( - createRoomConfig.value.copy(invites = createRoomConfig.value.invites.minus(event.matrixUser).toImmutableList()) - ) + is ConfigureRoomEvents.AvatarUriChanged -> dataStore.setAvatarUrl(event.uri?.toString()) + is ConfigureRoomEvents.RoomNameChanged -> dataStore.setRoomName(event.name) + is ConfigureRoomEvents.TopicChanged -> dataStore.setTopic(event.topic) + is ConfigureRoomEvents.RoomPrivacyChanged -> dataStore.setPrivacy(event.privacy) + is ConfigureRoomEvents.RemoveFromSelection -> dataStore.selectedUserListDataStore.removeUserFromSelection(event.matrixUser) ConfigureRoomEvents.CreateRoom -> Unit } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt index 19ad6fdeb9..2ef30908e1 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenter.kt @@ -23,6 +23,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import io.element.android.features.userlist.api.SelectionMode import io.element.android.features.userlist.api.UserListDataSource +import io.element.android.features.userlist.api.UserListDataStore import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.libraries.architecture.Async @@ -39,6 +40,7 @@ import javax.inject.Named class CreateRoomRootPresenter @Inject constructor( private val presenterFactory: UserListPresenter.Factory, @Named("AllUsers") private val userListDataSource: UserListDataSource, + private val userListDataStore: UserListDataStore, private val matrixClient: MatrixClient, ) : Presenter { @@ -46,6 +48,7 @@ class CreateRoomRootPresenter @Inject constructor( presenterFactory.create( UserListPresenterArgs(selectionMode = SelectionMode.Single), userListDataSource, + userListDataStore, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt index 24fb25991b..8841bd9d5e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import io.element.android.features.userlist.api.SelectionMode import io.element.android.features.userlist.api.UserListDataSource +import io.element.android.features.userlist.api.UserListDataStore import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.libraries.architecture.Async @@ -37,12 +38,14 @@ import javax.inject.Named class RoomMemberListPresenter @Inject constructor( private val userListPresenterFactory: UserListPresenter.Factory, @Named("RoomMembers") private val userListDataSource: UserListDataSource, + private val userListDataStore: UserListDataStore, ) : Presenter { private val userListPresenter by lazy { userListPresenterFactory.create( UserListPresenterArgs(selectionMode = SelectionMode.Single), userListDataSource, + userListDataStore, ) } diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataStore.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataStore.kt new file mode 100644 index 0000000000..c1e982dd59 --- /dev/null +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListDataStore.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.userlist.api + +import io.element.android.libraries.matrix.ui.model.MatrixUser +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +class UserListDataStore @Inject constructor() { + + private val selectedUsers: MutableStateFlow> = MutableStateFlow(emptyList()) + + fun selectUser(user: MatrixUser) { + selectedUsers.tryEmit(selectedUsers.value.plus(user)) + } + + fun removeUserFromSelection(user: MatrixUser) { + selectedUsers.tryEmit(selectedUsers.value.minus(user)) + } + + fun selectedUsers(): Flow> = selectedUsers +} diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenter.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenter.kt index 2fa416bfb5..90205eab2a 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenter.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenter.kt @@ -21,6 +21,10 @@ import io.element.android.libraries.architecture.Presenter interface UserListPresenter : Presenter { interface Factory { - fun create(args: UserListPresenterArgs, userListDataSource: UserListDataSource): UserListPresenter + fun create( + args: UserListPresenterArgs, + userListDataSource: UserListDataSource, + userListDataStore: UserListDataStore, + ): UserListPresenter } } diff --git a/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt b/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt index 5aaa106eb7..8e1829f4a7 100644 --- a/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt +++ b/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt @@ -21,6 +21,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -32,10 +33,11 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.features.userlist.api.UserListDataSource +import io.element.android.features.userlist.api.UserListDataStore import io.element.android.features.userlist.api.UserListEvents +import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.features.userlist.api.UserListState -import io.element.android.features.userlist.api.UserListPresenter import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.MatrixPatterns import io.element.android.libraries.matrix.api.core.UserId @@ -49,21 +51,24 @@ import kotlinx.coroutines.launch class DefaultUserListPresenter @AssistedInject constructor( @Assisted val args: UserListPresenterArgs, @Assisted val userListDataSource: UserListDataSource, + @Assisted val userListDataStore: UserListDataStore, ) : UserListPresenter { @AssistedFactory @ContributesBinding(SessionScope::class) interface DefaultUserListFactory : UserListPresenter.Factory { - override fun create(args: UserListPresenterArgs, userListDataSource: UserListDataSource): DefaultUserListPresenter + override fun create( + args: UserListPresenterArgs, + userListDataSource: UserListDataSource, + userListDataStore: UserListDataStore, + ): DefaultUserListPresenter } @Composable override fun present(): UserListState { val localCoroutineScope = rememberCoroutineScope() var isSearchActive by rememberSaveable { mutableStateOf(false) } - val selectedUsers: MutableState> = remember { - mutableStateOf(persistentListOf()) - } + val selectedUsers = userListDataStore.selectedUsers().collectAsState(emptyList()) val selectedUsersListState = rememberLazyListState() var searchQuery by rememberSaveable { mutableStateOf("") } val searchResults: MutableState> = remember { @@ -76,11 +81,11 @@ class DefaultUserListPresenter @AssistedInject constructor( is UserListEvents.UpdateSearchQuery -> searchQuery = event.query is UserListEvents.AddToSelection -> { if (event.matrixUser !in selectedUsers.value) { - selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableList() + userListDataStore.selectUser(event.matrixUser) } localCoroutineScope.scrollToFirstSelectedUser(selectedUsersListState) } - is UserListEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableList() + is UserListEvents.RemoveFromSelection -> userListDataStore.removeUserFromSelection(event.matrixUser) } }