diff --git a/app/src/main/java/io/element/android/x/di/RoomComponent.kt b/app/src/main/java/io/element/android/x/di/RoomComponent.kt new file mode 100644 index 0000000000..d5964325ae --- /dev/null +++ b/app/src/main/java/io/element/android/x/di/RoomComponent.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 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.x.di + +import com.squareup.anvil.annotations.ContributesTo +import com.squareup.anvil.annotations.MergeSubcomponent +import dagger.BindsInstance +import dagger.Subcomponent +import io.element.android.x.architecture.NodeFactoriesBindings +import io.element.android.x.matrix.room.MatrixRoom + +@SingleIn(RoomScope::class) +@MergeSubcomponent(RoomScope::class) +interface RoomComponent : NodeFactoriesBindings { + + fun matrixRoom(): MatrixRoom + + @Subcomponent.Builder + interface Builder { + @BindsInstance + fun room(room: MatrixRoom): Builder + fun build(): RoomComponent + } + + @ContributesTo(SessionScope::class) + interface ParentBindings { + fun roomComponentBuilder(): Builder + } +} diff --git a/app/src/main/java/io/element/android/x/di/SessionComponent.kt b/app/src/main/java/io/element/android/x/di/SessionComponent.kt index 93e19d082b..0ba9a12d58 100644 --- a/app/src/main/java/io/element/android/x/di/SessionComponent.kt +++ b/app/src/main/java/io/element/android/x/di/SessionComponent.kt @@ -26,7 +26,7 @@ import io.element.android.x.matrix.MatrixClient @SingleIn(SessionScope::class) @MergeSubcomponent(SessionScope::class) -interface SessionComponent: NodeFactoriesBindings { +interface SessionComponent: NodeFactoriesBindings, RoomComponent.ParentBindings { fun matrixClient(): MatrixClient diff --git a/app/src/main/java/io/element/android/x/node/LoggedInFlowNode.kt b/app/src/main/java/io/element/android/x/node/LoggedInFlowNode.kt index 36e8416c16..281d9f5aef 100644 --- a/app/src/main/java/io/element/android/x/node/LoggedInFlowNode.kt +++ b/app/src/main/java/io/element/android/x/node/LoggedInFlowNode.kt @@ -1,7 +1,11 @@ package io.element.android.x.node import android.os.Parcelable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import coil.Coil import com.bumble.appyx.core.composable.Children @@ -9,6 +13,7 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.ParentNode +import com.bumble.appyx.core.node.node import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import io.element.android.x.architecture.bindings @@ -83,7 +88,17 @@ class LoggedInFlowNode( createNode(buildContext, plugins = listOf(roomListCallback)) } is NavTarget.Room -> { - RoomFlowNode(buildContext, navTarget.roomId) + val room = matrixClient.getRoom(roomId = navTarget.roomId) + if (room == null) { + // TODO CREATE UNKNOWN ROOM NODE + node(buildContext) { + Box(modifier = it.fillMaxSize(), contentAlignment = Alignment.Center) { + Text(text = "Unknown room with id = ${navTarget.roomId}") + } + } + } else { + RoomFlowNode(buildContext, room) + } } NavTarget.Settings -> { PreferencesFlowNode(buildContext, onOpenBugReport) diff --git a/app/src/main/java/io/element/android/x/node/RoomFlowNode.kt b/app/src/main/java/io/element/android/x/node/RoomFlowNode.kt index b3c4eebb62..4c9fd64e68 100644 --- a/app/src/main/java/io/element/android/x/node/RoomFlowNode.kt +++ b/app/src/main/java/io/element/android/x/node/RoomFlowNode.kt @@ -9,15 +9,18 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.navmodel.backstack.BackStack +import io.element.android.x.architecture.bindings import io.element.android.x.architecture.createNode +import io.element.android.x.core.di.DaggerComponentOwner +import io.element.android.x.di.RoomComponent import io.element.android.x.features.messages.MessagesNode -import io.element.android.x.matrix.core.RoomId +import io.element.android.x.matrix.room.MatrixRoom import kotlinx.parcelize.Parcelize import timber.log.Timber class RoomFlowNode( buildContext: BuildContext, - private val roomId: RoomId, + private val room: MatrixRoom, private val backstack: BackStack = BackStack( initialElement = NavTarget.Messages, savedStateMap = buildContext.savedStateMap, @@ -25,9 +28,11 @@ class RoomFlowNode( ) : ParentNode( navModel = backstack, buildContext = buildContext -) { - +), DaggerComponentOwner { + override val daggerComponent: Any by lazy { + parent!!.bindings().roomComponentBuilder().room(room).build() + } init { lifecycle.subscribe( diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesNode.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesNode.kt index 25bd74ec97..d556eecf04 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesNode.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesNode.kt @@ -1,10 +1,8 @@ package io.element.android.x.features.messages -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node @@ -12,29 +10,24 @@ import com.bumble.appyx.core.plugin.Plugin import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.x.anvilannotations.ContributesNode -import io.element.android.x.di.SessionScope +import io.element.android.x.architecture.presenterConnector +import io.element.android.x.di.RoomScope -@ContributesNode(SessionScope::class) +@ContributesNode(RoomScope::class) class MessagesNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, - //presenter: MessagesPresenter, + presenter: MessagesPresenter, ) : Node(buildContext, plugins = plugins) { + private val connector = presenterConnector(presenter) + @Composable override fun View(modifier: Modifier) { - /* val state by connector.stateFlow.collectAsState() MessagesView( state = state, onBackPressed = this::navigateUp, ) - */ - Box( - modifier = modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - Text(text = "MESSAGES NODE") - } } } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesPresenter.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesPresenter.kt index 253bc3c1c1..d78123ef3d 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesPresenter.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesPresenter.kt @@ -1,8 +1,16 @@ package io.element.android.x.features.messages import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.x.architecture.Presenter +import io.element.android.x.designsystem.components.avatar.AvatarData +import io.element.android.x.designsystem.components.avatar.AvatarSize import io.element.android.x.features.messages.actionlist.ActionListPresenter import io.element.android.x.features.messages.actionlist.TimelineItemAction import io.element.android.x.features.messages.model.MessagesTimelineItemState @@ -12,8 +20,8 @@ import io.element.android.x.features.messages.textcomposer.MessageComposerPresen import io.element.android.x.features.messages.textcomposer.MessageComposerState import io.element.android.x.features.messages.timeline.TimelinePresenter import io.element.android.x.matrix.MatrixClient -import io.element.android.x.matrix.core.RoomId import io.element.android.x.matrix.room.MatrixRoom +import io.element.android.x.matrix.ui.MatrixItemHelper import io.element.android.x.textcomposer.MessageComposerMode import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -21,14 +29,15 @@ import timber.log.Timber import javax.inject.Inject class MessagesPresenter @Inject constructor( - private val client: MatrixClient, - private val roomId: RoomId, + private val matrixClient: MatrixClient, private val room: MatrixRoom, private val composerPresenter: MessageComposerPresenter, private val timelinePresenter: TimelinePresenter, private val actionListPresenter: ActionListPresenter, ) : Presenter { + private val matrixItemHelper = MatrixItemHelper(matrixClient) + @Composable override fun present(): MessagesState { val localCoroutineScope = rememberCoroutineScope() @@ -36,13 +45,31 @@ class MessagesPresenter @Inject constructor( val timelineState = timelinePresenter.present() val actionListState = actionListPresenter.present() + val syncUpdateFlow = room.syncUpdateFlow().collectAsState(0L) + val roomName: MutableState = rememberSaveable { + mutableStateOf(null) + } + val roomAvatar: MutableState = remember { + mutableStateOf(null) + } + LaunchedEffect(syncUpdateFlow) { + roomAvatar.value = + matrixItemHelper.loadAvatarData( + room = room, + size = AvatarSize.SMALL + ) + roomName.value = room.name + } + fun handleEvents(event: MessagesEvents) { when (event) { is MessagesEvents.HandleAction -> localCoroutineScope.handleTimelineAction(event.action, event.messageEvent, composerState) } } return MessagesState( - roomId = roomId, + roomId = room.roomId, + roomName = roomName.value, + roomAvatar = roomAvatar.value, composerState = composerState, timelineState = timelineState, actionListState = actionListState, diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt index efca0e4de4..48bb8816d0 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt @@ -17,6 +17,7 @@ package io.element.android.x.matrix import io.element.android.x.core.coroutine.CoroutineDispatchers +import io.element.android.x.matrix.core.RoomId import io.element.android.x.matrix.core.SessionId import io.element.android.x.matrix.core.UserId import io.element.android.x.matrix.media.MediaResolver @@ -108,8 +109,8 @@ class MatrixClient internal constructor( slidingSyncObserverToken = slidingSync.sync() } - fun getRoom(roomId: String): MatrixRoom? { - val slidingSyncRoom = slidingSync.getRoom(roomId) ?: return null + fun getRoom(roomId: RoomId): MatrixRoom? { + val slidingSyncRoom = slidingSync.getRoom(roomId.value) ?: return null val room = slidingSyncRoom.fullRoom() ?: return null return MatrixRoom( slidingSyncUpdateFlow = slidingSyncObserverProxy.updateSummaryFlow,