Invite : remove invitelist entry points
This commit is contained in:
parent
1cdf561688
commit
807bd362fe
29 changed files with 16 additions and 1237 deletions
|
|
@ -33,7 +33,6 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.pop
|
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.replace
|
import com.bumble.appyx.navmodel.backstack.operation.replace
|
||||||
import com.bumble.appyx.navmodel.backstack.operation.singleTop
|
import com.bumble.appyx.navmodel.backstack.operation.singleTop
|
||||||
|
|
@ -48,7 +47,6 @@ import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||||
import io.element.android.features.ftue.api.FtueEntryPoint
|
import io.element.android.features.ftue.api.FtueEntryPoint
|
||||||
import io.element.android.features.ftue.api.state.FtueService
|
import io.element.android.features.ftue.api.state.FtueService
|
||||||
import io.element.android.features.ftue.api.state.FtueState
|
import io.element.android.features.ftue.api.state.FtueState
|
||||||
import io.element.android.features.invite.api.InviteListEntryPoint
|
|
||||||
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
|
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
|
||||||
import io.element.android.features.lockscreen.api.LockScreenLockState
|
import io.element.android.features.lockscreen.api.LockScreenLockState
|
||||||
import io.element.android.features.lockscreen.api.LockScreenService
|
import io.element.android.features.lockscreen.api.LockScreenService
|
||||||
|
|
@ -62,7 +60,6 @@ import io.element.android.features.securebackup.api.SecureBackupEntryPoint
|
||||||
import io.element.android.libraries.architecture.BackstackView
|
import io.element.android.libraries.architecture.BackstackView
|
||||||
import io.element.android.libraries.architecture.BaseFlowNode
|
import io.element.android.libraries.architecture.BaseFlowNode
|
||||||
import io.element.android.libraries.architecture.createNode
|
import io.element.android.libraries.architecture.createNode
|
||||||
import io.element.android.libraries.architecture.waitForChildAttached
|
|
||||||
import io.element.android.libraries.deeplink.DeeplinkData
|
import io.element.android.libraries.deeplink.DeeplinkData
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
|
|
@ -70,7 +67,6 @@ import io.element.android.libraries.di.SessionScope
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.MAIN_SPACE
|
import io.element.android.libraries.matrix.api.core.MAIN_SPACE
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
|
||||||
import io.element.android.libraries.matrix.api.sync.SyncState
|
import io.element.android.libraries.matrix.api.sync.SyncState
|
||||||
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
|
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
|
||||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||||
|
|
@ -95,7 +91,6 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||||
private val createRoomEntryPoint: CreateRoomEntryPoint,
|
private val createRoomEntryPoint: CreateRoomEntryPoint,
|
||||||
private val appNavigationStateService: AppNavigationStateService,
|
private val appNavigationStateService: AppNavigationStateService,
|
||||||
private val secureBackupEntryPoint: SecureBackupEntryPoint,
|
private val secureBackupEntryPoint: SecureBackupEntryPoint,
|
||||||
private val inviteListEntryPoint: InviteListEntryPoint,
|
|
||||||
private val ftueEntryPoint: FtueEntryPoint,
|
private val ftueEntryPoint: FtueEntryPoint,
|
||||||
private val coroutineScope: CoroutineScope,
|
private val coroutineScope: CoroutineScope,
|
||||||
private val networkMonitor: NetworkMonitor,
|
private val networkMonitor: NetworkMonitor,
|
||||||
|
|
@ -160,23 +155,6 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
observeSyncStateAndNetworkStatus()
|
observeSyncStateAndNetworkStatus()
|
||||||
observeInvitesLoadingState()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun observeInvitesLoadingState() {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|
||||||
matrixClient.roomListService.invites.loadingState
|
|
||||||
.collect { inviteState ->
|
|
||||||
when (inviteState) {
|
|
||||||
is RoomList.LoadingState.Loaded -> if (inviteState.numberOfRooms == 0) {
|
|
||||||
backstack.removeLast(NavTarget.InviteList)
|
|
||||||
}
|
|
||||||
RoomList.LoadingState.NotLoaded -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(FlowPreview::class)
|
||||||
|
|
@ -233,9 +211,6 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||||
val initialElement: SecureBackupEntryPoint.InitialTarget = SecureBackupEntryPoint.InitialTarget.Root
|
val initialElement: SecureBackupEntryPoint.InitialTarget = SecureBackupEntryPoint.InitialTarget.Root
|
||||||
) : NavTarget
|
) : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
data object InviteList : NavTarget
|
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data object Ftue : NavTarget
|
data object Ftue : NavTarget
|
||||||
|
|
||||||
|
|
@ -272,10 +247,6 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||||
backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey))
|
backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onInvitesClicked() {
|
|
||||||
backstack.push(NavTarget.InviteList)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRoomSettingsClicked(roomId: RoomId) {
|
override fun onRoomSettingsClicked(roomId: RoomId) {
|
||||||
backstack.push(NavTarget.Room(roomId, initialElement = RoomNavigationTarget.Details))
|
backstack.push(NavTarget.Room(roomId, initialElement = RoomNavigationTarget.Details))
|
||||||
}
|
}
|
||||||
|
|
@ -351,25 +322,6 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||||
.params(SecureBackupEntryPoint.Params(initialElement = navTarget.initialElement))
|
.params(SecureBackupEntryPoint.Params(initialElement = navTarget.initialElement))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
NavTarget.InviteList -> {
|
|
||||||
val callback = object : InviteListEntryPoint.Callback {
|
|
||||||
override fun onBackClicked() {
|
|
||||||
backstack.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onInviteClicked(roomId: RoomId) {
|
|
||||||
backstack.push(NavTarget.Room(roomId))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onInviteAccepted(roomId: RoomId) {
|
|
||||||
backstack.push(NavTarget.Room(roomId))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inviteListEntryPoint.nodeBuilder(this, buildContext)
|
|
||||||
.callback(callback)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
NavTarget.Ftue -> {
|
NavTarget.Ftue -> {
|
||||||
ftueEntryPoint.nodeBuilder(this, buildContext)
|
ftueEntryPoint.nodeBuilder(this, buildContext)
|
||||||
.callback(object : FtueEntryPoint.Callback {
|
.callback(object : FtueEntryPoint.Callback {
|
||||||
|
|
@ -414,10 +366,6 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||||
if (!canShowRoomList()) return@withContext
|
if (!canShowRoomList()) return@withContext
|
||||||
notificationDrawerManager.clearMembershipNotificationForSession(deeplinkData.sessionId)
|
notificationDrawerManager.clearMembershipNotificationForSession(deeplinkData.sessionId)
|
||||||
backstack.singleTop(NavTarget.RoomList)
|
backstack.singleTop(NavTarget.RoomList)
|
||||||
backstack.push(NavTarget.InviteList)
|
|
||||||
waitForChildAttached<Node, NavTarget> { navTarget ->
|
|
||||||
navTarget is NavTarget.InviteList
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun canShowRoomList(): Boolean {
|
private fun canShowRoomList(): Boolean {
|
||||||
|
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.invite.api
|
|
||||||
|
|
||||||
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.RoomId
|
|
||||||
|
|
||||||
interface InviteListEntryPoint : FeatureEntryPoint {
|
|
||||||
fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
|
|
||||||
|
|
||||||
interface NodeBuilder {
|
|
||||||
fun callback(callback: Callback): NodeBuilder
|
|
||||||
fun build(): Node
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Callback : Plugin {
|
|
||||||
fun onBackClicked()
|
|
||||||
fun onInviteClicked(roomId: RoomId)
|
|
||||||
fun onInviteAccepted(roomId: RoomId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.invite.api
|
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
interface SeenInvitesStore {
|
|
||||||
fun seenRoomIds(): Flow<Set<RoomId>>
|
|
||||||
suspend fun markAsSeen(roomIds: Set<RoomId>)
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.invite.impl
|
|
||||||
|
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
|
||||||
import com.bumble.appyx.core.node.Node
|
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
|
||||||
import io.element.android.features.invite.api.InviteListEntryPoint
|
|
||||||
import io.element.android.features.invite.impl.invitelist.InviteListNode
|
|
||||||
import io.element.android.libraries.architecture.createNode
|
|
||||||
import io.element.android.libraries.di.AppScope
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
|
||||||
class DefaultInviteListEntryPoint @Inject constructor() : InviteListEntryPoint {
|
|
||||||
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): InviteListEntryPoint.NodeBuilder {
|
|
||||||
val plugins = ArrayList<Plugin>()
|
|
||||||
|
|
||||||
return object : InviteListEntryPoint.NodeBuilder {
|
|
||||||
override fun callback(callback: InviteListEntryPoint.Callback): InviteListEntryPoint.NodeBuilder {
|
|
||||||
plugins += callback
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun build(): Node {
|
|
||||||
return parentNode.createNode<InviteListNode>(buildContext, plugins)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.invite.impl
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.datastore.core.DataStore
|
|
||||||
import androidx.datastore.preferences.core.Preferences
|
|
||||||
import androidx.datastore.preferences.core.edit
|
|
||||||
import androidx.datastore.preferences.core.stringSetPreferencesKey
|
|
||||||
import androidx.datastore.preferences.preferencesDataStore
|
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
|
||||||
import io.element.android.features.invite.api.SeenInvitesStore
|
|
||||||
import io.element.android.libraries.di.ApplicationContext
|
|
||||||
import io.element.android.libraries.di.SessionScope
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "elementx_seeninvites")
|
|
||||||
private val seenInvitesKey = stringSetPreferencesKey("seenInvites")
|
|
||||||
|
|
||||||
@ContributesBinding(SessionScope::class)
|
|
||||||
class DefaultSeenInvitesStore @Inject constructor(
|
|
||||||
@ApplicationContext context: Context
|
|
||||||
) : SeenInvitesStore {
|
|
||||||
private val store = context.dataStore
|
|
||||||
|
|
||||||
override fun seenRoomIds(): Flow<Set<RoomId>> =
|
|
||||||
store.data.map { prefs ->
|
|
||||||
prefs[seenInvitesKey]
|
|
||||||
.orEmpty()
|
|
||||||
.map { RoomId(it) }
|
|
||||||
.toSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun markAsSeen(roomIds: Set<RoomId>) {
|
|
||||||
store.edit { prefs ->
|
|
||||||
prefs[seenInvitesKey] = roomIds.map { it.value }.toSet()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,195 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.invite.impl.components
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.IntrinsicSize
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.heightIn
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
|
||||||
import androidx.compose.ui.text.SpanStyle
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import io.element.android.compound.theme.ElementTheme
|
|
||||||
import io.element.android.features.invite.impl.R
|
|
||||||
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
|
||||||
import io.element.android.features.invite.impl.model.InviteListInviteSummaryProvider
|
|
||||||
import io.element.android.features.invite.impl.model.InviteSender
|
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom
|
|
||||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Button
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.ButtonSize
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
|
||||||
|
|
||||||
private val minHeight = 72.dp
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun InviteSummaryRow(
|
|
||||||
invite: InviteListInviteSummary,
|
|
||||||
onAcceptClicked: () -> Unit,
|
|
||||||
onDeclineClicked: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.heightIn(min = minHeight)
|
|
||||||
) {
|
|
||||||
DefaultInviteSummaryRow(
|
|
||||||
invite = invite,
|
|
||||||
onAcceptClicked = onAcceptClicked,
|
|
||||||
onDeclineClicked = onDeclineClicked,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun DefaultInviteSummaryRow(
|
|
||||||
invite: InviteListInviteSummary,
|
|
||||||
onAcceptClicked: () -> Unit,
|
|
||||||
onDeclineClicked: () -> Unit,
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(16.dp)
|
|
||||||
.height(IntrinsicSize.Min),
|
|
||||||
verticalAlignment = Alignment.Top
|
|
||||||
) {
|
|
||||||
Avatar(
|
|
||||||
invite.roomAvatarData,
|
|
||||||
)
|
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(start = 16.dp, end = 4.dp)
|
|
||||||
.alignByBaseline()
|
|
||||||
.weight(1f)
|
|
||||||
) {
|
|
||||||
val bonusPadding = if (invite.isNew) 12.dp else 0.dp
|
|
||||||
|
|
||||||
// Name
|
|
||||||
Text(
|
|
||||||
text = invite.roomName,
|
|
||||||
color = MaterialTheme.colorScheme.primary,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
style = ElementTheme.typography.fontBodyLgMedium,
|
|
||||||
modifier = Modifier.padding(end = bonusPadding),
|
|
||||||
)
|
|
||||||
|
|
||||||
// ID or Alias
|
|
||||||
invite.roomAlias?.let {
|
|
||||||
Text(
|
|
||||||
style = ElementTheme.typography.fontBodyMdRegular,
|
|
||||||
text = it,
|
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
modifier = Modifier.padding(end = bonusPadding),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sender
|
|
||||||
invite.sender?.let { sender ->
|
|
||||||
SenderRow(sender = sender)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CTAs
|
|
||||||
Row(Modifier.padding(top = 12.dp)) {
|
|
||||||
OutlinedButton(
|
|
||||||
text = stringResource(CommonStrings.action_decline),
|
|
||||||
onClick = onDeclineClicked,
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
size = ButtonSize.Medium,
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(12.dp))
|
|
||||||
|
|
||||||
Button(
|
|
||||||
text = stringResource(CommonStrings.action_accept),
|
|
||||||
onClick = onAcceptClicked,
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
size = ButtonSize.Medium,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UnreadIndicatorAtom(isVisible = invite.isNew)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun SenderRow(sender: InviteSender) {
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
|
||||||
modifier = Modifier.padding(top = 6.dp),
|
|
||||||
) {
|
|
||||||
Avatar(
|
|
||||||
avatarData = sender.avatarData,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.screen_invites_invited_you, sender.displayName, sender.userId.value).let { text ->
|
|
||||||
val senderNameStart = LocalContext.current.getString(R.string.screen_invites_invited_you).indexOf("%1\$s")
|
|
||||||
AnnotatedString(
|
|
||||||
text = text,
|
|
||||||
spanStyles = listOf(
|
|
||||||
AnnotatedString.Range(
|
|
||||||
SpanStyle(
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
color = MaterialTheme.colorScheme.primary
|
|
||||||
),
|
|
||||||
start = senderNameStart,
|
|
||||||
end = senderNameStart + sender.displayName.length
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
style = ElementTheme.typography.fontBodyMdRegular,
|
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreviewsDayNight
|
|
||||||
@Composable
|
|
||||||
internal fun InviteSummaryRowPreview(@PreviewParameter(InviteListInviteSummaryProvider::class) data: InviteListInviteSummary) = ElementPreview {
|
|
||||||
InviteSummaryRow(
|
|
||||||
invite = data,
|
|
||||||
onAcceptClicked = {},
|
|
||||||
onDeclineClicked = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 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.invite.impl.invitelist
|
|
||||||
|
|
||||||
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
|
||||||
|
|
||||||
sealed interface InviteListEvents {
|
|
||||||
data class AcceptInvite(val invite: InviteListInviteSummary) : InviteListEvents
|
|
||||||
data class DeclineInvite(val invite: InviteListInviteSummary) : InviteListEvents
|
|
||||||
}
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 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.invite.impl.invitelist
|
|
||||||
|
|
||||||
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 dagger.assisted.Assisted
|
|
||||||
import dagger.assisted.AssistedInject
|
|
||||||
import io.element.android.anvilannotations.ContributesNode
|
|
||||||
import io.element.android.features.invite.api.InviteListEntryPoint
|
|
||||||
import io.element.android.libraries.di.SessionScope
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
|
||||||
class InviteListNode @AssistedInject constructor(
|
|
||||||
@Assisted buildContext: BuildContext,
|
|
||||||
@Assisted plugins: List<Plugin>,
|
|
||||||
private val presenter: InviteListPresenter,
|
|
||||||
) : Node(buildContext, plugins = plugins) {
|
|
||||||
private fun onBackClicked() {
|
|
||||||
plugins<InviteListEntryPoint.Callback>().forEach { it.onBackClicked() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onInviteAccepted(roomId: RoomId) {
|
|
||||||
plugins<InviteListEntryPoint.Callback>().forEach { it.onInviteAccepted(roomId) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onInviteClicked(roomId: RoomId) {
|
|
||||||
plugins<InviteListEntryPoint.Callback>().forEach { it.onInviteClicked(roomId) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
override fun View(modifier: Modifier) {
|
|
||||||
val state = presenter.present()
|
|
||||||
InviteListView(
|
|
||||||
state = state,
|
|
||||||
onBackClicked = ::onBackClicked,
|
|
||||||
onInviteAccepted = ::onInviteAccepted,
|
|
||||||
onInviteDeclined = {},
|
|
||||||
onInviteClicked = ::onInviteClicked,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 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.invite.impl.invitelist
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import io.element.android.features.invite.api.SeenInvitesStore
|
|
||||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents
|
|
||||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
|
||||||
import io.element.android.features.invite.api.response.InviteData
|
|
||||||
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
|
||||||
import io.element.android.features.invite.impl.model.InviteSender
|
|
||||||
import io.element.android.libraries.architecture.Presenter
|
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class InviteListPresenter @Inject constructor(
|
|
||||||
private val client: MatrixClient,
|
|
||||||
private val store: SeenInvitesStore,
|
|
||||||
private val acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
|
|
||||||
) : Presenter<InviteListState> {
|
|
||||||
@Composable
|
|
||||||
override fun present(): InviteListState {
|
|
||||||
val invites by client
|
|
||||||
.roomListService
|
|
||||||
.invites
|
|
||||||
.summaries
|
|
||||||
.collectAsState(initial = emptyList())
|
|
||||||
|
|
||||||
var seenInvites by remember { mutableStateOf<Set<RoomId>>(emptySet()) }
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
seenInvites = store.seenRoomIds().first()
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(invites) {
|
|
||||||
store.markAsSeen(
|
|
||||||
invites
|
|
||||||
.filterIsInstance<RoomSummary.Filled>()
|
|
||||||
.map { it.details.roomId }
|
|
||||||
.toSet()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val acceptDeclineInviteState = acceptDeclineInvitePresenter.present()
|
|
||||||
|
|
||||||
fun handleEvent(event: InviteListEvents) {
|
|
||||||
when (event) {
|
|
||||||
is InviteListEvents.AcceptInvite -> {
|
|
||||||
acceptDeclineInviteState.eventSink(
|
|
||||||
AcceptDeclineInviteEvents.AcceptInvite(event.invite.toInviteData())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
is InviteListEvents.DeclineInvite -> {
|
|
||||||
acceptDeclineInviteState.eventSink(
|
|
||||||
AcceptDeclineInviteEvents.DeclineInvite(event.invite.toInviteData())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val inviteList = remember(seenInvites, invites) {
|
|
||||||
invites
|
|
||||||
.filterIsInstance<RoomSummary.Filled>()
|
|
||||||
.map {
|
|
||||||
it.toInviteSummary(seenInvites.contains(it.details.roomId))
|
|
||||||
}
|
|
||||||
.toPersistentList()
|
|
||||||
}
|
|
||||||
|
|
||||||
return InviteListState(
|
|
||||||
inviteList = inviteList,
|
|
||||||
acceptDeclineInviteState = acceptDeclineInviteState,
|
|
||||||
eventSink = ::handleEvent
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun RoomSummary.Filled.toInviteSummary(seen: Boolean) = details.run {
|
|
||||||
val i = inviter
|
|
||||||
val avatarData = if (isDirect && i != null) {
|
|
||||||
AvatarData(
|
|
||||||
id = i.userId.value,
|
|
||||||
name = i.displayName,
|
|
||||||
url = i.avatarUrl,
|
|
||||||
size = AvatarSize.RoomInviteItem,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
AvatarData(
|
|
||||||
id = roomId.value,
|
|
||||||
name = name,
|
|
||||||
url = avatarUrl,
|
|
||||||
size = AvatarSize.RoomInviteItem,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val alias = if (isDirect) {
|
|
||||||
inviter?.userId?.value
|
|
||||||
} else {
|
|
||||||
canonicalAlias
|
|
||||||
}
|
|
||||||
|
|
||||||
InviteListInviteSummary(
|
|
||||||
roomId = roomId,
|
|
||||||
roomName = name,
|
|
||||||
roomAlias = alias,
|
|
||||||
roomAvatarData = avatarData,
|
|
||||||
isDirect = isDirect,
|
|
||||||
isNew = !seen,
|
|
||||||
sender = inviter
|
|
||||||
?.takeIf { !isDirect }
|
|
||||||
?.run {
|
|
||||||
InviteSender(
|
|
||||||
userId = userId,
|
|
||||||
displayName = displayName ?: "",
|
|
||||||
avatarData = AvatarData(
|
|
||||||
id = userId.value,
|
|
||||||
name = displayName,
|
|
||||||
url = avatarUrl,
|
|
||||||
size = AvatarSize.InviteSender,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun InviteListInviteSummary.toInviteData() = InviteData(
|
|
||||||
roomId = roomId,
|
|
||||||
roomName = roomName,
|
|
||||||
isDirect = isDirect,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 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.invite.impl.invitelist
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
|
||||||
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
data class InviteListState(
|
|
||||||
val inviteList: ImmutableList<InviteListInviteSummary>,
|
|
||||||
val acceptDeclineInviteState: AcceptDeclineInviteState,
|
|
||||||
val eventSink: (InviteListEvents) -> Unit
|
|
||||||
)
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 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.invite.impl.invitelist
|
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|
||||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
|
||||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteStateProvider
|
|
||||||
import io.element.android.features.invite.api.response.anAcceptDeclineInviteState
|
|
||||||
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
|
||||||
import io.element.android.features.invite.impl.model.InviteSender
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
|
||||||
|
|
||||||
open class InviteListStateProvider : PreviewParameterProvider<InviteListState> {
|
|
||||||
private val acceptDeclineInviteStateProvider = AcceptDeclineInviteStateProvider()
|
|
||||||
|
|
||||||
override val values: Sequence<InviteListState>
|
|
||||||
get() = sequenceOf(
|
|
||||||
anInviteListState(),
|
|
||||||
anInviteListState(inviteList = persistentListOf()),
|
|
||||||
) + acceptDeclineInviteStateProvider.values.map { acceptDeclineInviteState ->
|
|
||||||
anInviteListState(acceptDeclineInviteState = acceptDeclineInviteState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun anInviteListState(
|
|
||||||
inviteList: ImmutableList<InviteListInviteSummary> = aInviteListInviteSummaryList(),
|
|
||||||
acceptDeclineInviteState: AcceptDeclineInviteState = anAcceptDeclineInviteState(),
|
|
||||||
eventSink: (InviteListEvents) -> Unit = {}
|
|
||||||
) = InviteListState(
|
|
||||||
inviteList = inviteList,
|
|
||||||
acceptDeclineInviteState = acceptDeclineInviteState,
|
|
||||||
eventSink = eventSink,
|
|
||||||
)
|
|
||||||
|
|
||||||
internal fun aInviteListInviteSummaryList(): ImmutableList<InviteListInviteSummary> {
|
|
||||||
return persistentListOf(
|
|
||||||
InviteListInviteSummary(
|
|
||||||
roomId = RoomId("!id1:example.com"),
|
|
||||||
roomName = "Room 1",
|
|
||||||
roomAlias = "#room:example.org",
|
|
||||||
sender = InviteSender(
|
|
||||||
userId = UserId("@alice:example.org"),
|
|
||||||
displayName = "Alice"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
InviteListInviteSummary(
|
|
||||||
roomId = RoomId("!id2:example.com"),
|
|
||||||
roomName = "Room 2",
|
|
||||||
sender = InviteSender(
|
|
||||||
userId = UserId("@bob:example.org"),
|
|
||||||
displayName = "Bob"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
InviteListInviteSummary(
|
|
||||||
roomId = RoomId("!id3:example.com"),
|
|
||||||
roomName = "Alice",
|
|
||||||
roomAlias = "@alice:example.com"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 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.invite.impl.invitelist
|
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import io.element.android.compound.theme.ElementTheme
|
|
||||||
import io.element.android.features.invite.impl.R
|
|
||||||
import io.element.android.features.invite.impl.components.InviteSummaryRow
|
|
||||||
import io.element.android.features.invite.impl.response.AcceptDeclineInviteView
|
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
|
||||||
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun InviteListView(
|
|
||||||
state: InviteListState,
|
|
||||||
onBackClicked: () -> Unit,
|
|
||||||
onInviteAccepted: (RoomId) -> Unit,
|
|
||||||
onInviteDeclined: (RoomId) -> Unit,
|
|
||||||
onInviteClicked: (RoomId) -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
InviteListContent(
|
|
||||||
state = state,
|
|
||||||
modifier = modifier,
|
|
||||||
onInviteClicked = onInviteClicked,
|
|
||||||
onBackClicked = onBackClicked,
|
|
||||||
)
|
|
||||||
AcceptDeclineInviteView(
|
|
||||||
state = state.acceptDeclineInviteState,
|
|
||||||
onInviteAccepted = onInviteAccepted,
|
|
||||||
onInviteDeclined = onInviteDeclined,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
private fun InviteListContent(
|
|
||||||
state: InviteListState,
|
|
||||||
onBackClicked: () -> Unit,
|
|
||||||
onInviteClicked: (RoomId) -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
Scaffold(
|
|
||||||
modifier = modifier,
|
|
||||||
topBar = {
|
|
||||||
TopAppBar(
|
|
||||||
navigationIcon = {
|
|
||||||
BackButton(onClick = onBackClicked)
|
|
||||||
},
|
|
||||||
title = {
|
|
||||||
Text(
|
|
||||||
text = stringResource(CommonStrings.action_invites_list),
|
|
||||||
style = ElementTheme.typography.aliasScreenTitle,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
content = { padding ->
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(padding)
|
|
||||||
.consumeWindowInsets(padding)
|
|
||||||
) {
|
|
||||||
if (state.inviteList.isEmpty()) {
|
|
||||||
Spacer(Modifier.size(80.dp))
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.screen_invites_empty_list),
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
color = MaterialTheme.colorScheme.tertiary,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
) {
|
|
||||||
itemsIndexed(
|
|
||||||
items = state.inviteList,
|
|
||||||
) { index, invite ->
|
|
||||||
InviteSummaryRow(
|
|
||||||
modifier = Modifier.clickable(
|
|
||||||
onClick = { onInviteClicked(invite.roomId) }
|
|
||||||
),
|
|
||||||
invite = invite,
|
|
||||||
onAcceptClicked = { state.eventSink(InviteListEvents.AcceptInvite(invite)) },
|
|
||||||
onDeclineClicked = { state.eventSink(InviteListEvents.DeclineInvite(invite)) },
|
|
||||||
)
|
|
||||||
|
|
||||||
if (index != state.inviteList.lastIndex) {
|
|
||||||
HorizontalDivider()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreviewsDayNight
|
|
||||||
@Composable
|
|
||||||
internal fun InviteListViewPreview(@PreviewParameter(InviteListStateProvider::class) state: InviteListState) = ElementPreview {
|
|
||||||
InviteListView(
|
|
||||||
state = state,
|
|
||||||
onBackClicked = {},
|
|
||||||
onInviteAccepted = {},
|
|
||||||
onInviteDeclined = {},
|
|
||||||
onInviteClicked = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.invite.impl.model
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
data class InviteListInviteSummary(
|
|
||||||
val roomId: RoomId,
|
|
||||||
val roomName: String = "",
|
|
||||||
val roomAlias: String? = null,
|
|
||||||
val roomAvatarData: AvatarData = AvatarData(roomId.value, roomName, size = AvatarSize.RoomInviteItem),
|
|
||||||
val sender: InviteSender? = null,
|
|
||||||
val isDirect: Boolean = false,
|
|
||||||
val isNew: Boolean = false,
|
|
||||||
)
|
|
||||||
|
|
||||||
data class InviteSender(
|
|
||||||
val userId: UserId,
|
|
||||||
val displayName: String,
|
|
||||||
val avatarData: AvatarData = AvatarData(userId.value, displayName, size = AvatarSize.InviteSender),
|
|
||||||
)
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.invite.impl.model
|
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
|
||||||
|
|
||||||
open class InviteListInviteSummaryProvider : PreviewParameterProvider<InviteListInviteSummary> {
|
|
||||||
override val values: Sequence<InviteListInviteSummary>
|
|
||||||
get() = sequenceOf(
|
|
||||||
aInviteListInviteSummary(),
|
|
||||||
aInviteListInviteSummary().copy(roomAlias = "#someroom-with-a-long-alias:example.com"),
|
|
||||||
aInviteListInviteSummary().copy(roomAlias = "#someroom-with-a-long-alias:example.com", isNew = true),
|
|
||||||
aInviteListInviteSummary().copy(roomName = "Alice", sender = null),
|
|
||||||
aInviteListInviteSummary().copy(isNew = true)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun aInviteListInviteSummary() = InviteListInviteSummary(
|
|
||||||
roomId = RoomId("!room1:example.com"),
|
|
||||||
roomName = "Some room with a long name that will truncate",
|
|
||||||
sender = InviteSender(
|
|
||||||
userId = UserId("@alice-with-a-long-mxid:example.org"),
|
|
||||||
displayName = "Alice with a long name"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
@ -21,7 +21,6 @@ import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.TurbineTestContext
|
import app.cash.turbine.TurbineTestContext
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.invite.api.SeenInvitesStore
|
|
||||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||||
import io.element.android.features.invite.api.response.anAcceptDeclineInviteState
|
import io.element.android.features.invite.api.response.anAcceptDeclineInviteState
|
||||||
import io.element.android.features.invite.test.FakeSeenInvitesStore
|
import io.element.android.features.invite.test.FakeSeenInvitesStore
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package io.element.android.features.invite.test
|
package io.element.android.features.invite.test
|
||||||
|
|
||||||
import io.element.android.features.invite.api.SeenInvitesStore
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ interface RoomListEntryPoint : FeatureEntryPoint {
|
||||||
fun onCreateRoomClicked()
|
fun onCreateRoomClicked()
|
||||||
fun onSettingsClicked()
|
fun onSettingsClicked()
|
||||||
fun onSessionConfirmRecoveryKeyClicked()
|
fun onSessionConfirmRecoveryKeyClicked()
|
||||||
fun onInvitesClicked()
|
|
||||||
fun onRoomSettingsClicked(roomId: RoomId)
|
fun onRoomSettingsClicked(roomId: RoomId)
|
||||||
fun onReportBugClicked()
|
fun onReportBugClicked()
|
||||||
fun onRoomDirectorySearchClicked()
|
fun onRoomDirectorySearchClicked()
|
||||||
|
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.roomlist.impl
|
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.heightIn
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.semantics.Role
|
|
||||||
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.designsystem.atomic.atoms.UnreadIndicatorAtom
|
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun InvitesEntryPointView(
|
|
||||||
onInvitesClicked: () -> Unit,
|
|
||||||
state: InvitesState,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = modifier.fillMaxWidth(),
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.clip(RoundedCornerShape(8.dp))
|
|
||||||
.clickable(role = Role.Button, onClick = onInvitesClicked)
|
|
||||||
.padding(start = 24.dp, end = 16.dp)
|
|
||||||
.align(Alignment.CenterEnd)
|
|
||||||
.heightIn(min = 40.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(CommonStrings.action_invites_list),
|
|
||||||
style = ElementTheme.typography.fontBodyMdMedium,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (state == InvitesState.NewInvites) {
|
|
||||||
Spacer(Modifier.width(8.dp))
|
|
||||||
UnreadIndicatorAtom()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreviewsDayNight
|
|
||||||
@Composable
|
|
||||||
internal fun InvitesEntryPointViewPreview(@PreviewParameter(InvitesStateProvider::class) state: InvitesState) = ElementPreview {
|
|
||||||
InvitesEntryPointView(
|
|
||||||
onInvitesClicked = {},
|
|
||||||
state = state,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -33,11 +33,9 @@ open class RoomListContentStateProvider : PreviewParameterProvider<RoomListConte
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun aRoomsContentState(
|
internal fun aRoomsContentState(
|
||||||
invitesState: InvitesState = InvitesState.NoInvites,
|
|
||||||
securityBannerState: SecurityBannerState = SecurityBannerState.None,
|
securityBannerState: SecurityBannerState = SecurityBannerState.None,
|
||||||
summaries: ImmutableList<RoomListRoomSummary> = aRoomListRoomSummaryList(),
|
summaries: ImmutableList<RoomListRoomSummary> = aRoomListRoomSummaryList(),
|
||||||
) = RoomListContentState.Rooms(
|
) = RoomListContentState.Rooms(
|
||||||
invitesState = invitesState,
|
|
||||||
securityBannerState = securityBannerState,
|
securityBannerState = securityBannerState,
|
||||||
summaries = summaries,
|
summaries = summaries,
|
||||||
)
|
)
|
||||||
|
|
@ -46,6 +44,4 @@ internal fun aMigrationContentState() = RoomListContentState.Migration
|
||||||
|
|
||||||
internal fun aSkeletonContentState() = RoomListContentState.Skeleton(16)
|
internal fun aSkeletonContentState() = RoomListContentState.Skeleton(16)
|
||||||
|
|
||||||
internal fun anEmptyContentState(
|
internal fun anEmptyContentState() = RoomListContentState.Empty
|
||||||
invitesState: InvitesState = InvitesState.NoInvites,
|
|
||||||
) = RoomListContentState.Empty(invitesState)
|
|
||||||
|
|
|
||||||
|
|
@ -70,10 +70,6 @@ class RoomListNode @AssistedInject constructor(
|
||||||
plugins<RoomListEntryPoint.Callback>().forEach { it.onSessionConfirmRecoveryKeyClicked() }
|
plugins<RoomListEntryPoint.Callback>().forEach { it.onSessionConfirmRecoveryKeyClicked() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onInvitesClicked() {
|
|
||||||
plugins<RoomListEntryPoint.Callback>().forEach { it.onInvitesClicked() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onRoomSettingsClicked(roomId: RoomId) {
|
private fun onRoomSettingsClicked(roomId: RoomId) {
|
||||||
plugins<RoomListEntryPoint.Callback>().forEach { it.onRoomSettingsClicked(roomId) }
|
plugins<RoomListEntryPoint.Callback>().forEach { it.onRoomSettingsClicked(roomId) }
|
||||||
}
|
}
|
||||||
|
|
@ -103,7 +99,6 @@ class RoomListNode @AssistedInject constructor(
|
||||||
onSettingsClicked = this::onOpenSettings,
|
onSettingsClicked = this::onOpenSettings,
|
||||||
onCreateRoomClicked = this::onCreateRoomClicked,
|
onCreateRoomClicked = this::onCreateRoomClicked,
|
||||||
onConfirmRecoveryKeyClicked = this::onSessionConfirmRecoveryKeyClicked,
|
onConfirmRecoveryKeyClicked = this::onSessionConfirmRecoveryKeyClicked,
|
||||||
onInvitesClicked = this::onInvitesClicked,
|
|
||||||
onRoomSettingsClicked = this::onRoomSettingsClicked,
|
onRoomSettingsClicked = this::onRoomSettingsClicked,
|
||||||
onMenuActionClicked = { onMenuActionClicked(activity, it) },
|
onMenuActionClicked = { onMenuActionClicked(activity, it) },
|
||||||
onRoomDirectorySearchClicked = this::onRoomDirectorySearchClicked,
|
onRoomDirectorySearchClicked = this::onRoomDirectorySearchClicked,
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ import io.element.android.features.leaveroom.api.LeaveRoomPresenter
|
||||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||||
import io.element.android.features.preferences.api.store.SessionPreferencesStore
|
import io.element.android.features.preferences.api.store.SessionPreferencesStore
|
||||||
import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource
|
|
||||||
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
|
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
|
||||||
import io.element.android.features.roomlist.impl.filters.RoomListFiltersState
|
import io.element.android.features.roomlist.impl.filters.RoomListFiltersState
|
||||||
import io.element.android.features.roomlist.impl.migration.MigrationScreenState
|
import io.element.android.features.roomlist.impl.migration.MigrationScreenState
|
||||||
|
|
@ -83,7 +82,6 @@ class RoomListPresenter @Inject constructor(
|
||||||
private val client: MatrixClient,
|
private val client: MatrixClient,
|
||||||
private val networkMonitor: NetworkMonitor,
|
private val networkMonitor: NetworkMonitor,
|
||||||
private val snackbarDispatcher: SnackbarDispatcher,
|
private val snackbarDispatcher: SnackbarDispatcher,
|
||||||
private val inviteStateDataSource: InviteStateDataSource,
|
|
||||||
private val leaveRoomPresenter: LeaveRoomPresenter,
|
private val leaveRoomPresenter: LeaveRoomPresenter,
|
||||||
private val roomListDataSource: RoomListDataSource,
|
private val roomListDataSource: RoomListDataSource,
|
||||||
private val featureFlagService: FeatureFlagService,
|
private val featureFlagService: FeatureFlagService,
|
||||||
|
|
@ -209,16 +207,11 @@ class RoomListPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
return when {
|
return when {
|
||||||
showMigration -> RoomListContentState.Migration
|
showMigration -> RoomListContentState.Migration
|
||||||
showEmpty -> {
|
showEmpty -> RoomListContentState.Empty
|
||||||
val invitesState = inviteStateDataSource.inviteState()
|
|
||||||
RoomListContentState.Empty(invitesState)
|
|
||||||
}
|
|
||||||
showSkeleton -> RoomListContentState.Skeleton(count = 16)
|
showSkeleton -> RoomListContentState.Skeleton(count = 16)
|
||||||
else -> {
|
else -> {
|
||||||
val invitesState = inviteStateDataSource.inviteState()
|
|
||||||
val securityBannerState by securityBannerState(securityBannerDismissed)
|
val securityBannerState by securityBannerState(securityBannerDismissed)
|
||||||
RoomListContentState.Rooms(
|
RoomListContentState.Rooms(
|
||||||
invitesState = invitesState,
|
|
||||||
securityBannerState = securityBannerState,
|
securityBannerState = securityBannerState,
|
||||||
summaries = roomSummaries.dataOrNull().orEmpty().toPersistentList()
|
summaries = roomSummaries.dataOrNull().orEmpty().toPersistentList()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -72,9 +72,8 @@ enum class SecurityBannerState {
|
||||||
sealed interface RoomListContentState {
|
sealed interface RoomListContentState {
|
||||||
data object Migration : RoomListContentState
|
data object Migration : RoomListContentState
|
||||||
data class Skeleton(val count: Int) : RoomListContentState
|
data class Skeleton(val count: Int) : RoomListContentState
|
||||||
data class Empty(val invitesState: InvitesState) : RoomListContentState
|
data object Empty : RoomListContentState
|
||||||
data class Rooms(
|
data class Rooms(
|
||||||
val invitesState: InvitesState,
|
|
||||||
val securityBannerState: SecurityBannerState,
|
val securityBannerState: SecurityBannerState,
|
||||||
val summaries: ImmutableList<RoomListRoomSummary>,
|
val summaries: ImmutableList<RoomListRoomSummary>,
|
||||||
) : RoomListContentState
|
) : RoomListContentState
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,6 @@ open class RoomListStateProvider : PreviewParameterProvider<RoomListState> {
|
||||||
aRoomListState(),
|
aRoomListState(),
|
||||||
aRoomListState(snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete)),
|
aRoomListState(snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete)),
|
||||||
aRoomListState(hasNetworkConnection = false),
|
aRoomListState(hasNetworkConnection = false),
|
||||||
aRoomListState(contentState = aRoomsContentState(invitesState = InvitesState.SeenInvites)),
|
|
||||||
aRoomListState(contentState = aRoomsContentState(invitesState = InvitesState.NewInvites)),
|
|
||||||
aRoomListState(contextMenu = aContextMenuShown(roomName = "A nice room name")),
|
aRoomListState(contextMenu = aContextMenuShown(roomName = "A nice room name")),
|
||||||
aRoomListState(contextMenu = aContextMenuShown(isFavorite = true)),
|
aRoomListState(contextMenu = aContextMenuShown(isFavorite = true)),
|
||||||
aRoomListState(contentState = aRoomsContentState(securityBannerState = SecurityBannerState.RecoveryKeyConfirmation)),
|
aRoomListState(contentState = aRoomsContentState(securityBannerState = SecurityBannerState.RecoveryKeyConfirmation)),
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.features.invite.api.response.AcceptDeclineInviteView
|
|
||||||
import io.element.android.features.leaveroom.api.LeaveRoomView
|
import io.element.android.features.leaveroom.api.LeaveRoomView
|
||||||
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorContainer
|
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorContainer
|
||||||
import io.element.android.features.roomlist.impl.components.RoomListContentView
|
import io.element.android.features.roomlist.impl.components.RoomListContentView
|
||||||
|
|
@ -56,12 +55,11 @@ fun RoomListView(
|
||||||
onSettingsClicked: () -> Unit,
|
onSettingsClicked: () -> Unit,
|
||||||
onConfirmRecoveryKeyClicked: () -> Unit,
|
onConfirmRecoveryKeyClicked: () -> Unit,
|
||||||
onCreateRoomClicked: () -> Unit,
|
onCreateRoomClicked: () -> Unit,
|
||||||
onInvitesClicked: () -> Unit,
|
|
||||||
onRoomSettingsClicked: (roomId: RoomId) -> Unit,
|
onRoomSettingsClicked: (roomId: RoomId) -> Unit,
|
||||||
onMenuActionClicked: (RoomListMenuAction) -> Unit,
|
onMenuActionClicked: (RoomListMenuAction) -> Unit,
|
||||||
onRoomDirectorySearchClicked: () -> Unit,
|
onRoomDirectorySearchClicked: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
acceptDeclineInviteView: @Composable ()->Unit,
|
acceptDeclineInviteView: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
ConnectivityIndicatorContainer(
|
ConnectivityIndicatorContainer(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -80,14 +78,13 @@ fun RoomListView(
|
||||||
LeaveRoomView(state = state.leaveRoomState)
|
LeaveRoomView(state = state.leaveRoomState)
|
||||||
|
|
||||||
RoomListScaffold(
|
RoomListScaffold(
|
||||||
modifier = Modifier.padding(top = topPadding),
|
|
||||||
state = state,
|
state = state,
|
||||||
onConfirmRecoveryKeyClicked = onConfirmRecoveryKeyClicked,
|
onConfirmRecoveryKeyClicked = onConfirmRecoveryKeyClicked,
|
||||||
onRoomClicked = onRoomClicked,
|
onRoomClicked = onRoomClicked,
|
||||||
onOpenSettings = onSettingsClicked,
|
onOpenSettings = onSettingsClicked,
|
||||||
onCreateRoomClicked = onCreateRoomClicked,
|
onCreateRoomClicked = onCreateRoomClicked,
|
||||||
onInvitesClicked = onInvitesClicked,
|
|
||||||
onMenuActionClicked = onMenuActionClicked,
|
onMenuActionClicked = onMenuActionClicked,
|
||||||
|
modifier = Modifier.padding(top = topPadding),
|
||||||
)
|
)
|
||||||
// This overlaid view will only be visible when state.displaySearchResults is true
|
// This overlaid view will only be visible when state.displaySearchResults is true
|
||||||
RoomListSearchView(
|
RoomListSearchView(
|
||||||
|
|
@ -114,7 +111,6 @@ private fun RoomListScaffold(
|
||||||
onRoomClicked: (RoomId) -> Unit,
|
onRoomClicked: (RoomId) -> Unit,
|
||||||
onOpenSettings: () -> Unit,
|
onOpenSettings: () -> Unit,
|
||||||
onCreateRoomClicked: () -> Unit,
|
onCreateRoomClicked: () -> Unit,
|
||||||
onInvitesClicked: () -> Unit,
|
|
||||||
onMenuActionClicked: (RoomListMenuAction) -> Unit,
|
onMenuActionClicked: (RoomListMenuAction) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
|
@ -150,7 +146,6 @@ private fun RoomListScaffold(
|
||||||
onConfirmRecoveryKeyClicked = onConfirmRecoveryKeyClicked,
|
onConfirmRecoveryKeyClicked = onConfirmRecoveryKeyClicked,
|
||||||
onRoomClicked = ::onRoomClicked,
|
onRoomClicked = ::onRoomClicked,
|
||||||
onCreateRoomClicked = onCreateRoomClicked,
|
onCreateRoomClicked = onCreateRoomClicked,
|
||||||
onInvitesClicked = onInvitesClicked,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(padding)
|
.padding(padding)
|
||||||
.consumeWindowInsets(padding)
|
.consumeWindowInsets(padding)
|
||||||
|
|
@ -186,7 +181,6 @@ internal fun RoomListViewPreview(@PreviewParameter(RoomListStateProvider::class)
|
||||||
onSettingsClicked = {},
|
onSettingsClicked = {},
|
||||||
onConfirmRecoveryKeyClicked = {},
|
onConfirmRecoveryKeyClicked = {},
|
||||||
onCreateRoomClicked = {},
|
onCreateRoomClicked = {},
|
||||||
onInvitesClicked = {},
|
|
||||||
onRoomSettingsClicked = {},
|
onRoomSettingsClicked = {},
|
||||||
onMenuActionClicked = {},
|
onMenuActionClicked = {},
|
||||||
onRoomDirectorySearchClicked = {},
|
onRoomDirectorySearchClicked = {},
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,6 @@ import androidx.compose.ui.unit.Velocity
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.features.roomlist.impl.InvitesEntryPointView
|
|
||||||
import io.element.android.features.roomlist.impl.InvitesState
|
|
||||||
import io.element.android.features.roomlist.impl.R
|
import io.element.android.features.roomlist.impl.R
|
||||||
import io.element.android.features.roomlist.impl.RoomListContentState
|
import io.element.android.features.roomlist.impl.RoomListContentState
|
||||||
import io.element.android.features.roomlist.impl.RoomListContentStateProvider
|
import io.element.android.features.roomlist.impl.RoomListContentStateProvider
|
||||||
|
|
@ -76,7 +74,6 @@ fun RoomListContentView(
|
||||||
onConfirmRecoveryKeyClicked: () -> Unit,
|
onConfirmRecoveryKeyClicked: () -> Unit,
|
||||||
onRoomClicked: (RoomListRoomSummary) -> Unit,
|
onRoomClicked: (RoomListRoomSummary) -> Unit,
|
||||||
onCreateRoomClicked: () -> Unit,
|
onCreateRoomClicked: () -> Unit,
|
||||||
onInvitesClicked: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
|
|
@ -91,8 +88,6 @@ fun RoomListContentView(
|
||||||
}
|
}
|
||||||
is RoomListContentState.Empty -> {
|
is RoomListContentState.Empty -> {
|
||||||
EmptyView(
|
EmptyView(
|
||||||
state = contentState,
|
|
||||||
onInvitesClicked = onInvitesClicked,
|
|
||||||
onCreateRoomClicked = onCreateRoomClicked,
|
onCreateRoomClicked = onCreateRoomClicked,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -103,7 +98,6 @@ fun RoomListContentView(
|
||||||
eventSink = eventSink,
|
eventSink = eventSink,
|
||||||
onConfirmRecoveryKeyClicked = onConfirmRecoveryKeyClicked,
|
onConfirmRecoveryKeyClicked = onConfirmRecoveryKeyClicked,
|
||||||
onRoomClicked = onRoomClicked,
|
onRoomClicked = onRoomClicked,
|
||||||
onInvitesClicked = onInvitesClicked,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,30 +120,21 @@ private fun SkeletonView(count: Int, modifier: Modifier = Modifier) {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun EmptyView(
|
private fun EmptyView(
|
||||||
state: RoomListContentState.Empty,
|
|
||||||
onCreateRoomClicked: () -> Unit,
|
onCreateRoomClicked: () -> Unit,
|
||||||
onInvitesClicked: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
Box(
|
EmptyScaffold(
|
||||||
|
title = R.string.screen_roomlist_empty_title,
|
||||||
|
subtitle = R.string.screen_roomlist_empty_message,
|
||||||
|
action = {
|
||||||
|
Button(
|
||||||
|
text = stringResource(CommonStrings.action_start_chat),
|
||||||
|
leadingIcon = IconSource.Vector(CompoundIcons.Compose()),
|
||||||
|
onClick = onCreateRoomClicked,
|
||||||
|
)
|
||||||
|
},
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
) {
|
)
|
||||||
if (state.invitesState != InvitesState.NoInvites) {
|
|
||||||
InvitesEntryPointView(onInvitesClicked, state.invitesState)
|
|
||||||
}
|
|
||||||
EmptyScaffold(
|
|
||||||
title = R.string.screen_roomlist_empty_title,
|
|
||||||
subtitle = R.string.screen_roomlist_empty_message,
|
|
||||||
action = {
|
|
||||||
Button(
|
|
||||||
text = stringResource(CommonStrings.action_start_chat),
|
|
||||||
leadingIcon = IconSource.Vector(CompoundIcons.Compose()),
|
|
||||||
onClick = onCreateRoomClicked,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -159,7 +144,6 @@ private fun RoomsView(
|
||||||
eventSink: (RoomListEvents) -> Unit,
|
eventSink: (RoomListEvents) -> Unit,
|
||||||
onConfirmRecoveryKeyClicked: () -> Unit,
|
onConfirmRecoveryKeyClicked: () -> Unit,
|
||||||
onRoomClicked: (RoomListRoomSummary) -> Unit,
|
onRoomClicked: (RoomListRoomSummary) -> Unit,
|
||||||
onInvitesClicked: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
if (state.summaries.isEmpty() && filtersState.hasAnyFilterSelected) {
|
if (state.summaries.isEmpty() && filtersState.hasAnyFilterSelected) {
|
||||||
|
|
@ -173,7 +157,6 @@ private fun RoomsView(
|
||||||
eventSink = eventSink,
|
eventSink = eventSink,
|
||||||
onConfirmRecoveryKeyClicked = onConfirmRecoveryKeyClicked,
|
onConfirmRecoveryKeyClicked = onConfirmRecoveryKeyClicked,
|
||||||
onRoomClicked = onRoomClicked,
|
onRoomClicked = onRoomClicked,
|
||||||
onInvitesClicked = onInvitesClicked,
|
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -185,7 +168,6 @@ private fun RoomsViewList(
|
||||||
eventSink: (RoomListEvents) -> Unit,
|
eventSink: (RoomListEvents) -> Unit,
|
||||||
onConfirmRecoveryKeyClicked: () -> Unit,
|
onConfirmRecoveryKeyClicked: () -> Unit,
|
||||||
onRoomClicked: (RoomListRoomSummary) -> Unit,
|
onRoomClicked: (RoomListRoomSummary) -> Unit,
|
||||||
onInvitesClicked: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
|
|
@ -223,11 +205,6 @@ private fun RoomsViewList(
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.invitesState != InvitesState.NoInvites) {
|
|
||||||
item {
|
|
||||||
InvitesEntryPointView(onInvitesClicked, state.invitesState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Note: do not use a key for the LazyColumn, or the scroll will not behave as expected if a room
|
// Note: do not use a key for the LazyColumn, or the scroll will not behave as expected if a room
|
||||||
// is moved to the top of the list.
|
// is moved to the top of the list.
|
||||||
itemsIndexed(
|
itemsIndexed(
|
||||||
|
|
@ -301,6 +278,5 @@ internal fun RoomListContentViewPreview(@PreviewParameter(RoomListContentStatePr
|
||||||
onConfirmRecoveryKeyClicked = {},
|
onConfirmRecoveryKeyClicked = {},
|
||||||
onRoomClicked = {},
|
onRoomClicked = {},
|
||||||
onCreateRoomClicked = {},
|
onCreateRoomClicked = {},
|
||||||
onInvitesClicked = {}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.roomlist.impl.datasource
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
|
||||||
import io.element.android.features.invite.api.SeenInvitesStore
|
|
||||||
import io.element.android.features.roomlist.impl.InvitesState
|
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
|
||||||
import io.element.android.libraries.di.SessionScope
|
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@ContributesBinding(SessionScope::class)
|
|
||||||
class DefaultInviteStateDataSource @Inject constructor(
|
|
||||||
private val client: MatrixClient,
|
|
||||||
private val seenInvitesStore: SeenInvitesStore,
|
|
||||||
private val coroutineDispatchers: CoroutineDispatchers,
|
|
||||||
) : InviteStateDataSource {
|
|
||||||
@Composable
|
|
||||||
override fun inviteState(): InvitesState {
|
|
||||||
val invites by client
|
|
||||||
.roomListService
|
|
||||||
.invites
|
|
||||||
.summaries
|
|
||||||
.collectAsState(initial = emptyList())
|
|
||||||
|
|
||||||
val seenInvites by seenInvitesStore
|
|
||||||
.seenRoomIds()
|
|
||||||
.collectAsState(initial = emptySet())
|
|
||||||
|
|
||||||
var state by remember { mutableStateOf(InvitesState.NoInvites) }
|
|
||||||
|
|
||||||
LaunchedEffect(invites, seenInvites) {
|
|
||||||
withContext(coroutineDispatchers.computation) {
|
|
||||||
state = when {
|
|
||||||
invites.isEmpty() -> InvitesState.NoInvites
|
|
||||||
seenInvites.containsAll(invites.roomIds) -> InvitesState.SeenInvites
|
|
||||||
else -> InvitesState.NewInvites
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val List<RoomSummary>.roomIds: Collection<RoomId>
|
|
||||||
get() = filterIsInstance<RoomSummary.Filled>().map { it.details.roomId }
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.roomlist.impl.datasource
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import io.element.android.features.roomlist.impl.InvitesState
|
|
||||||
|
|
||||||
interface InviteStateDataSource {
|
|
||||||
@Composable
|
|
||||||
fun inviteState(): InvitesState
|
|
||||||
}
|
|
||||||
|
|
@ -28,7 +28,6 @@ import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||||
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
|
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
|
||||||
import io.element.android.features.preferences.api.store.SessionPreferencesStore
|
import io.element.android.features.preferences.api.store.SessionPreferencesStore
|
||||||
import io.element.android.features.roomlist.impl.datasource.FakeInviteDataSource
|
import io.element.android.features.roomlist.impl.datasource.FakeInviteDataSource
|
||||||
import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource
|
|
||||||
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
|
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
|
||||||
import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory
|
import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory
|
||||||
import io.element.android.features.roomlist.impl.filters.RoomListFiltersState
|
import io.element.android.features.roomlist.impl.filters.RoomListFiltersState
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,10 @@ import android.content.Context
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import io.element.android.features.invite.impl.DefaultSeenInvitesStore
|
|
||||||
import io.element.android.features.leaveroom.impl.LeaveRoomPresenterImpl
|
import io.element.android.features.leaveroom.impl.LeaveRoomPresenterImpl
|
||||||
import io.element.android.features.networkmonitor.impl.NetworkMonitorImpl
|
import io.element.android.features.networkmonitor.impl.NetworkMonitorImpl
|
||||||
import io.element.android.features.roomlist.impl.RoomListPresenter
|
import io.element.android.features.roomlist.impl.RoomListPresenter
|
||||||
import io.element.android.features.roomlist.impl.RoomListView
|
import io.element.android.features.roomlist.impl.RoomListView
|
||||||
import io.element.android.features.roomlist.impl.datasource.DefaultInviteStateDataSource
|
|
||||||
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
|
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
|
||||||
import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory
|
import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory
|
||||||
import io.element.android.features.roomlist.impl.filters.RoomListFiltersPresenter
|
import io.element.android.features.roomlist.impl.filters.RoomListFiltersPresenter
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue