Compare commits

...

7 Commits

Author SHA1 Message Date
Adam Brown 30d6654745 showing failed screenshot verification 2022-08-30 17:05:58 +01:00
Adam Brown eb8931c6da adding unit test to execute screenshot test with custom state 2022-08-30 16:32:45 +01:00
ganfra ace01876dd Try mvrx mock/launcher on RoomProfileFragment #2 2022-08-24 18:21:32 +02:00
ganfra 05d8f1b355 Try mvrx mock/launcher on RoomProfileFragment 2022-08-24 10:48:13 +02:00
ganfra 45e3af9a81 Add Entry in debug to launch Mock activity 2022-08-23 19:04:23 +02:00
ganfra 1151dca9c9 Add MaverciskMockPrinter tool 2022-08-23 19:03:37 +02:00
ganfra e21400a940 Add mavericks mock and launcher dependencies 2022-08-23 19:03:28 +02:00
26 changed files with 323 additions and 48 deletions

View File

@ -32,6 +32,7 @@ buildscript {
classpath 'org.owasp:dependency-check-gradle:7.1.1' classpath 'org.owasp:dependency-check-gradle:7.1.1'
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.10" classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.10"
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0" classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
classpath "com.karumi:shot:5.14.1"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
} }

View File

@ -126,6 +126,8 @@ ext.libs = [
'epoxyProcessor' : "com.airbnb.android:epoxy-processor:$epoxy", 'epoxyProcessor' : "com.airbnb.android:epoxy-processor:$epoxy",
'epoxyPaging' : "com.airbnb.android:epoxy-paging:$epoxy", 'epoxyPaging' : "com.airbnb.android:epoxy-paging:$epoxy",
'mavericks' : "com.airbnb.android:mavericks:$mavericks", 'mavericks' : "com.airbnb.android:mavericks:$mavericks",
'mavericksMocking' : "com.airbnb.android:mavericks-mocking:$mavericks",
'mavericksLauncher' : "com.airbnb.android:mavericks-launcher:$mavericks",
'mavericksTesting' : "com.airbnb.android:mavericks-testing:$mavericks" 'mavericksTesting' : "com.airbnb.android:mavericks-testing:$mavericks"
], ],
maplibre : [ maplibre : [

View File

@ -57,6 +57,7 @@ ext.groups = [
'com.facebook.flipper', 'com.facebook.flipper',
'com.facebook.fresco', 'com.facebook.fresco',
'com.facebook.infer.annotation', 'com.facebook.infer.annotation',
'com.facebook.testing.screenshot',
'com.facebook.soloader', 'com.facebook.soloader',
'com.facebook.stetho', 'com.facebook.stetho',
'com.facebook.yoga', 'com.facebook.yoga',
@ -97,6 +98,7 @@ ext.groups = [
'com.ibm.icu', 'com.ibm.icu',
'com.jakewharton.android.repackaged', 'com.jakewharton.android.repackaged',
'com.jakewharton.timber', 'com.jakewharton.timber',
'com.karumi',
'com.kgurgul.flipper', 'com.kgurgul.flipper',
'com.linkedin.dexmaker', 'com.linkedin.dexmaker',
'com.mapbox.mapboxsdk', 'com.mapbox.mapboxsdk',

View File

@ -37,7 +37,7 @@ import org.matrix.android.sdk.api.util.toOptional
typealias ThreadRootEvent = TimelineEvent typealias ThreadRootEvent = TimelineEvent
class FlowRoom(private val room: Room) { class FlowRoom(val room: Room) {
fun liveRoomSummary(): Flow<Optional<RoomSummary>> { fun liveRoomSummary(): Flow<Optional<RoomSummary>> {
return room.getRoomSummaryLive().asFlow() return room.getRoomSummaryLive().asFlow()

BIN
tools/MavericksMockPrinter Executable file

Binary file not shown.

View File

@ -9,6 +9,7 @@ apply plugin: 'kotlin-kapt'
apply plugin: 'com.likethesalad.stem' apply plugin: 'com.likethesalad.stem'
apply plugin: 'dagger.hilt.android.plugin' apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'kotlinx-knit' apply plugin: 'kotlinx-knit'
apply plugin: 'shot'
if (project.hasProperty("coverage")) { if (project.hasProperty("coverage")) {
apply plugin: 'jacoco' apply plugin: 'jacoco'
@ -156,7 +157,8 @@ android {
buildConfigField "String", "GIT_BRANCH_NAME", "\"${gitBranchName()}\"" buildConfigField "String", "GIT_BRANCH_NAME", "\"${gitBranchName()}\""
buildConfigField "String", "BUILD_NUMBER", "\"${buildNumber}\"" buildConfigField "String", "BUILD_NUMBER", "\"${buildNumber}\""
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "com.karumi.shot.ShotTestRunner"
// Keep abiFilter for the universalApk // Keep abiFilter for the universalApk
ndk { ndk {
@ -424,6 +426,8 @@ dependencies {
kapt libs.airbnb.epoxyProcessor kapt libs.airbnb.epoxyProcessor
implementation libs.airbnb.epoxyPaging implementation libs.airbnb.epoxyPaging
implementation libs.airbnb.mavericks implementation libs.airbnb.mavericks
implementation libs.airbnb.mavericksMocking
implementation libs.airbnb.mavericksLauncher
// Snap Helper https://github.com/rubensousa/GravitySnapHelper // Snap Helper https://github.com/rubensousa/GravitySnapHelper
implementation 'com.github.rubensousa:gravitysnaphelper:2.2.2' implementation 'com.github.rubensousa:gravitysnaphelper:2.2.2'
@ -599,5 +603,5 @@ dependencies {
androidTestImplementation libs.mockk.mockkAndroid androidTestImplementation libs.mockk.mockkAndroid
androidTestUtil libs.androidx.orchestrator androidTestUtil libs.androidx.orchestrator
debugImplementation libs.androidx.fragmentTesting debugImplementation libs.androidx.fragmentTesting
androidTestImplementation "org.jetbrains.kotlin:kotlin-reflect:1.7.10" androidTestImplementation "org.jetbrains.kotlin:kotlin-reflect:1.6.10"
} }

View File

@ -0,0 +1,85 @@
/*
* 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 im.vector.app.screenshot
import android.os.Bundle
import androidx.test.core.app.ActivityScenario
import androidx.test.platform.app.InstrumentationRegistry
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.launcher.MavericksLauncherMockActivity
import com.airbnb.mvrx.mocking.MockableMavericksView
import com.airbnb.mvrx.mocking.getMockVariants
import com.airbnb.mvrx.mocking.mockSingleViewModel
import com.karumi.shot.ActivityScenarioUtils.waitForActivity
import com.karumi.shot.ScreenshotTest
import im.vector.app.features.roomprofile.RoomProfileFragment
import im.vector.app.features.roomprofile.mocks.mockRoomProfileArgs
import im.vector.app.features.roomprofile.mocks.mockRoomProfileViewState
import org.junit.Test
import kotlin.reflect.KClass
class MvRxScreenshotTest : ScreenshotTest {
private val context = InstrumentationRegistry.getInstrumentation().targetContext
@Test
fun defaultRoomProfileFragmentScreenshotTest() {
val mock = MockRoomProfileFragment::class.getMock("Default state")
val scenario = ActivityScenario.launch<MavericksLauncherMockActivity>(
MavericksLauncherMockActivity.intent(context, mock)
)
compareScreenshot(scenario.waitForActivity())
}
@Test
fun anotherRoomProfileFragmentScreenshotTest() {
val mock = MockRoomProfileFragment::class.getMock("my-state")
val scenario = ActivityScenario.launch<MavericksLauncherMockActivity>(
MavericksLauncherMockActivity.intent(context, mock)
)
compareScreenshot(scenario.waitForActivity())
}
}
fun <T : MockableMavericksView> KClass<T>.getMock(name: String) = getMockVariants(this.java)!!.first { it.mock.name == name }
class MockRoomProfileFragment : RoomProfileFragment(), MockableMavericksView {
override fun provideMocks() = mockSingleViewModel(
viewModelReference = MockRoomProfileFragment::roomProfileViewModel,
defaultState = mockRoomProfileViewState,
defaultArgs = mockRoomProfileArgs,
) {
state("my-state") {
val summary = (mockRoomProfileViewState.roomSummary as Success)().copy(
displayName = "a wrong name"
)
mockRoomProfileViewState.copy(
roomSummary = Success(summary)
)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
registerMockPrinter()
super.onCreate(savedInstanceState)
}
}

View File

@ -11,6 +11,7 @@
<activity android:name=".features.debug.features.DebugFeaturesSettingsActivity" /> <activity android:name=".features.debug.features.DebugFeaturesSettingsActivity" />
<activity android:name=".features.debug.DebugMenuActivity" /> <activity android:name=".features.debug.DebugMenuActivity" />
<activity android:name=".features.debug.leak.DebugMemoryLeaksActivity" /> <activity android:name=".features.debug.leak.DebugMemoryLeaksActivity" />
<activity android:name=".features.debug.VectorLauncherMockActivity" />
<activity <activity
android:name="com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity" android:name="com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity"

View File

@ -24,6 +24,8 @@ import android.os.Build
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.Person import androidx.core.app.Person
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import com.airbnb.mvrx.launcher.MavericksLauncherActivity
import com.airbnb.mvrx.launcher.MavericksLauncherMockActivity
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
@ -82,6 +84,10 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
} }
private fun setupViews() { private fun setupViews() {
views.debugMock.setOnClickListener {
MavericksLauncherActivity.show(this)
}
views.debugFeatures.setOnClickListener { startActivity(Intent(this, DebugFeaturesSettingsActivity::class.java)) } views.debugFeatures.setOnClickListener { startActivity(Intent(this, DebugFeaturesSettingsActivity::class.java)) }
views.debugPrivateSetting.setOnClickListener { openPrivateSettings() } views.debugPrivateSetting.setOnClickListener { openPrivateSettings() }
views.debugAnalytics.setOnClickListener { views.debugAnalytics.setOnClickListener {

View File

@ -0,0 +1,54 @@
/*
* 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 im.vector.app.features.debug
import android.os.Bundle
import androidx.fragment.app.Fragment
import com.airbnb.mvrx.launcher.MavericksLauncherMockActivity
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityLauncherMockBinding
@AndroidEntryPoint
class VectorLauncherMockActivity : VectorBaseActivity<ActivityLauncherMockBinding>() {
override fun getBinding() = ActivityLauncherMockBinding.inflate(layoutInflater)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
MavericksLauncherMockActivity.showNextMockFromActivity(
activity = this,
showView = { view ->
// Use commit now to catch errors on initialization.
setFragment(view as Fragment, commitNow = true)
}
)
}
}
protected fun setFragment(fragment: Fragment, commitNow: Boolean = false) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.setPrimaryNavigationFragment(fragment)
.apply {
if (commitNow) commitNow() else commit()
}
}
}

View File

@ -24,6 +24,12 @@
android:padding="@dimen/layout_horizontal_margin" android:padding="@dimen/layout_horizontal_margin"
android:showDividers="middle"> android:showDividers="middle">
<Button
android:id="@+id/debug_mock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Mocks" />
<Button <Button
android:id="@+id/debug_features" android:id="@+id/debug_features"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -37,6 +37,9 @@ import com.airbnb.epoxy.Carousel
import com.airbnb.epoxy.EpoxyAsyncUtil import com.airbnb.epoxy.EpoxyAsyncUtil
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.Mavericks
import com.airbnb.mvrx.launcher.MavericksLauncherMockActivity
import com.airbnb.mvrx.mocking.MavericksMock
import com.airbnb.mvrx.mocking.MockableMavericks
import com.facebook.stetho.Stetho import com.facebook.stetho.Stetho
import com.gabrielittner.threetenbp.LazyThreeTen import com.gabrielittner.threetenbp.LazyThreeTen
import com.github.rubensousa.gravitysnaphelper.GravitySnapHelper import com.github.rubensousa.gravitysnaphelper.GravitySnapHelper
@ -53,6 +56,7 @@ import im.vector.app.core.resources.BuildMeta
import im.vector.app.features.analytics.VectorAnalytics import im.vector.app.features.analytics.VectorAnalytics
import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.configuration.VectorConfiguration import im.vector.app.features.configuration.VectorConfiguration
import im.vector.app.features.debug.VectorLauncherMockActivity
import im.vector.app.features.disclaimer.doNotShowDisclaimerDialog import im.vector.app.features.disclaimer.doNotShowDisclaimerDialog
import im.vector.app.features.invite.InvitesAcceptor import im.vector.app.features.invite.InvitesAcceptor
import im.vector.app.features.lifecycle.VectorActivityLifecycleCallbacks import im.vector.app.features.lifecycle.VectorActivityLifecycleCallbacks
@ -146,7 +150,9 @@ class VectorApplication :
} }
logInfo() logInfo()
LazyThreeTen.init(this) LazyThreeTen.init(this)
Mavericks.initialize(debugMode = false) MockableMavericks.initialize(mocksEnabled = buildMeta.isDebug, debugMode = false, applicationContext = applicationContext)
MavericksLauncherMockActivity.activityToShowMock = VectorLauncherMockActivity::class
//Mavericks.initialize(debugMode = false)
configureEpoxy() configureEpoxy()

View File

@ -121,7 +121,6 @@ import im.vector.app.features.roomdirectory.roompreview.RoomPreviewNoPreviewFrag
import im.vector.app.features.roommemberprofile.RoomMemberProfileFragment import im.vector.app.features.roommemberprofile.RoomMemberProfileFragment
import im.vector.app.features.roommemberprofile.devices.DeviceListFragment import im.vector.app.features.roommemberprofile.devices.DeviceListFragment
import im.vector.app.features.roommemberprofile.devices.DeviceTrustInfoActionFragment import im.vector.app.features.roommemberprofile.devices.DeviceTrustInfoActionFragment
import im.vector.app.features.roomprofile.RoomProfileFragment
import im.vector.app.features.roomprofile.alias.RoomAliasFragment import im.vector.app.features.roomprofile.alias.RoomAliasFragment
import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment
import im.vector.app.features.roomprofile.members.RoomMemberListFragment import im.vector.app.features.roomprofile.members.RoomMemberListFragment
@ -547,11 +546,6 @@ interface FragmentModule {
@FragmentKey(PublicRoomsFragment::class) @FragmentKey(PublicRoomsFragment::class)
fun bindPublicRoomsFragment(fragment: PublicRoomsFragment): Fragment fun bindPublicRoomsFragment(fragment: PublicRoomsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomProfileFragment::class)
fun bindRoomProfileFragment(fragment: RoomProfileFragment): Fragment
@Binds @Binds
@IntoMap @IntoMap
@FragmentKey(RoomMemberListFragment::class) @FragmentKey(RoomMemberListFragment::class)

View File

@ -31,6 +31,7 @@ import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.animations.AppBarStateChangeListener import im.vector.app.core.animations.AppBarStateChangeListener
import im.vector.app.core.animations.MatrixItemAppBarStateChangeListener import im.vector.app.core.animations.MatrixItemAppBarStateChangeListener
@ -65,21 +66,22 @@ data class RoomProfileArgs(
val roomId: String val roomId: String
) : Parcelable ) : Parcelable
class RoomProfileFragment @Inject constructor( @AndroidEntryPoint
private val roomProfileController: RoomProfileController, open class RoomProfileFragment :
private val avatarRenderer: AvatarRenderer,
private val roomDetailPendingActionStore: RoomDetailPendingActionStore,
) :
VectorBaseFragment<FragmentMatrixProfileBinding>(), VectorBaseFragment<FragmentMatrixProfileBinding>(),
RoomProfileController.Callback, RoomProfileController.Callback,
VectorMenuProvider { VectorMenuProvider {
@Inject lateinit var roomProfileController: RoomProfileController
@Inject lateinit var avatarRenderer: AvatarRenderer
@Inject lateinit var roomDetailPendingActionStore: RoomDetailPendingActionStore
private lateinit var headerViews: ViewStubRoomProfileHeaderBinding private lateinit var headerViews: ViewStubRoomProfileHeaderBinding
private val roomProfileArgs: RoomProfileArgs by args() private val roomProfileArgs: RoomProfileArgs by args()
private lateinit var roomListQuickActionsSharedActionViewModel: RoomListQuickActionsSharedActionViewModel private lateinit var roomListQuickActionsSharedActionViewModel: RoomListQuickActionsSharedActionViewModel
private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel
private val roomProfileViewModel: RoomProfileViewModel by fragmentViewModel() val roomProfileViewModel: RoomProfileViewModel by fragmentViewModel()
private var appBarStateChangeListener: AppBarStateChangeListener? = null private var appBarStateChangeListener: AppBarStateChangeListener? = null

View File

@ -22,6 +22,7 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
@ -35,11 +36,13 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.getStateEvent import org.matrix.android.sdk.api.session.room.getStateEvent
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
@ -56,7 +59,7 @@ class RoomProfileViewModel @AssistedInject constructor(
@Assisted private val initialState: RoomProfileViewState, @Assisted private val initialState: RoomProfileViewState,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val shortcutCreator: ShortcutCreator, private val shortcutCreator: ShortcutCreator,
private val session: Session, sessionHolder: ActiveSessionHolder,
private val analyticsTracker: AnalyticsTracker private val analyticsTracker: AnalyticsTracker
) : VectorViewModel<RoomProfileViewState, RoomProfileAction, RoomProfileViewEvents>(initialState) { ) : VectorViewModel<RoomProfileViewState, RoomProfileAction, RoomProfileViewEvents>(initialState) {
@ -67,23 +70,27 @@ class RoomProfileViewModel @AssistedInject constructor(
companion object : MavericksViewModelFactory<RoomProfileViewModel, RoomProfileViewState> by hiltMavericksViewModelFactory() companion object : MavericksViewModelFactory<RoomProfileViewModel, RoomProfileViewState> by hiltMavericksViewModelFactory()
private val room = session.getRoom(initialState.roomId)!! private val session = sessionHolder.getSafeActiveSession()
private val room = session?.getRoom(initialState.roomId)
private val myUserId = session?.myUserId ?: ""
init { init {
room?.also { room ->
val flowRoom = room.flow() val flowRoom = room.flow()
observeRoomSummary(flowRoom) observeRoomSummary(flowRoom)
observeRoomCreateContent(flowRoom) observeRoomCreateContent(flowRoom)
observeBannedRoomMembers(flowRoom) observeBannedRoomMembers(flowRoom)
observePermissions() observePermissions(room)
observePowerLevels() observePowerLevels(room)
}
} }
private fun observePowerLevels() { private fun observePowerLevels(room: Room) {
val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow()
powerLevelsContentLive powerLevelsContentLive
.onEach { .onEach {
val powerLevelsHelper = PowerLevelsHelper(it) val powerLevelsHelper = PowerLevelsHelper(it)
val canUpdateRoomState = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) val canUpdateRoomState = powerLevelsHelper.isUserAllowedToSend(myUserId, true, EventType.STATE_ROOM_ENCRYPTION)
setState { setState {
copy(canUpdateRoomState = canUpdateRoomState) copy(canUpdateRoomState = canUpdateRoomState)
} }
@ -98,10 +105,10 @@ class RoomProfileViewModel @AssistedInject constructor(
copy( copy(
roomCreateContent = async, roomCreateContent = async,
// This is a shortcut, we should do the next lines elsewhere, but keep it like that for the moment. // This is a shortcut, we should do the next lines elsewhere, but keep it like that for the moment.
recommendedRoomVersion = room.roomVersionService().getRecommendedVersion(), recommendedRoomVersion = flowRoom.room.roomVersionService().getRecommendedVersion(),
isUsingUnstableRoomVersion = room.roomVersionService().isUsingUnstableRoomVersion(), isUsingUnstableRoomVersion = flowRoom.room.roomVersionService().isUsingUnstableRoomVersion(),
canUpgradeRoom = room.roomVersionService().userMayUpgradeRoom(session.myUserId), canUpgradeRoom = flowRoom.room.roomVersionService().userMayUpgradeRoom(myUserId),
isTombstoned = room.getStateEvent(EventType.STATE_ROOM_TOMBSTONE, QueryStringValue.IsEmpty) != null isTombstoned = flowRoom.room.getStateEvent(EventType.STATE_ROOM_TOMBSTONE, QueryStringValue.IsEmpty) != null
) )
} }
} }
@ -121,34 +128,36 @@ class RoomProfileViewModel @AssistedInject constructor(
} }
} }
private fun observePermissions() { private fun observePermissions(room: Room) {
PowerLevelsFlowFactory(room) PowerLevelsFlowFactory(room)
.createFlow() .createFlow()
.setOnEach { .setOnEach {
val powerLevelsHelper = PowerLevelsHelper(it) val powerLevelsHelper = PowerLevelsHelper(it)
val permissions = RoomProfileViewState.ActionPermissions( val permissions = RoomProfileViewState.ActionPermissions(
canEnableEncryption = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) canEnableEncryption = powerLevelsHelper.isUserAllowedToSend(myUserId, true, EventType.STATE_ROOM_ENCRYPTION)
) )
copy(actionPermissions = permissions) copy(actionPermissions = permissions)
} }
} }
override fun handle(action: RoomProfileAction) { override fun handle(action: RoomProfileAction) {
val session = this.session ?: throw IllegalStateException("Action with no active session")
val room = this.room ?: throw IllegalStateException("Action with unknown room")
when (action) { when (action) {
is RoomProfileAction.EnableEncryption -> handleEnableEncryption() is RoomProfileAction.EnableEncryption -> handleEnableEncryption(room)
RoomProfileAction.LeaveRoom -> handleLeaveRoom() RoomProfileAction.LeaveRoom -> handleLeaveRoom(session, room)
is RoomProfileAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action) is RoomProfileAction.ChangeRoomNotificationState -> handleChangeNotificationMode(room, action)
is RoomProfileAction.ShareRoomProfile -> handleShareRoomProfile() is RoomProfileAction.ShareRoomProfile -> handleShareRoomProfile(session)
RoomProfileAction.CreateShortcut -> handleCreateShortcut() RoomProfileAction.CreateShortcut -> handleCreateShortcut()
RoomProfileAction.RestoreEncryptionState -> restoreEncryptionState() RoomProfileAction.RestoreEncryptionState -> restoreEncryptionState(session, room)
} }
} }
fun isPublicRoom(): Boolean { fun isPublicRoom(): Boolean {
return room.stateService().isPublic() return room?.stateService()?.isPublic().orFalse()
} }
private fun handleEnableEncryption() { private fun handleEnableEncryption(room: Room) {
postLoading(true) postLoading(true)
viewModelScope.launch { viewModelScope.launch {
@ -168,15 +177,14 @@ class RoomProfileViewModel @AssistedInject constructor(
private fun handleCreateShortcut() { private fun handleCreateShortcut() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
withState { state -> val state = awaitState()
state.roomSummary() state.roomSummary()
?.let { shortcutCreator.create(it) } ?.let { shortcutCreator.create(it) }
?.let { _viewEvents.post(RoomProfileViewEvents.OnShortcutReady(it)) } ?.let { _viewEvents.post(RoomProfileViewEvents.OnShortcutReady(it)) }
} }
} }
}
private fun handleChangeNotificationMode(action: RoomProfileAction.ChangeRoomNotificationState) { private fun handleChangeNotificationMode(room: Room, action: RoomProfileAction.ChangeRoomNotificationState) {
viewModelScope.launch { viewModelScope.launch {
try { try {
room.roomPushRuleService().setRoomNotificationState(action.notificationState) room.roomPushRuleService().setRoomNotificationState(action.notificationState)
@ -186,7 +194,7 @@ class RoomProfileViewModel @AssistedInject constructor(
} }
} }
private fun handleLeaveRoom() { private fun handleLeaveRoom(session: Session, room: Room) {
_viewEvents.post(RoomProfileViewEvents.Loading(stringProvider.getString(R.string.room_profile_leaving_room))) _viewEvents.post(RoomProfileViewEvents.Loading(stringProvider.getString(R.string.room_profile_leaving_room)))
viewModelScope.launch { viewModelScope.launch {
try { try {
@ -205,14 +213,14 @@ class RoomProfileViewModel @AssistedInject constructor(
} }
} }
private fun handleShareRoomProfile() { private fun handleShareRoomProfile(session: Session) {
session.permalinkService().createRoomPermalink(initialState.roomId) session.permalinkService().createRoomPermalink(initialState.roomId)
?.let { permalink -> ?.let { permalink ->
_viewEvents.post(RoomProfileViewEvents.ShareRoomProfile(permalink)) _viewEvents.post(RoomProfileViewEvents.ShareRoomProfile(permalink))
} }
} }
private fun restoreEncryptionState() { private fun restoreEncryptionState(session: Session, room: Room) {
_viewEvents.post(RoomProfileViewEvents.Loading()) _viewEvents.post(RoomProfileViewEvents.Loading())
session.coroutineScope.launch { session.coroutineScope.launch {
try { try {

View File

@ -0,0 +1,6 @@
package im.vector.app.features.roomprofile.mocks
import im.vector.app.features.roomprofile.RoomProfileArgs
import kotlin.String
val mockRoomProfileArgs by lazy { RoomProfileArgs(roomId = "!CWLUCoEWXSFyTCOtfL:matrix.org") }

View File

@ -0,0 +1,93 @@
package im.vector.app.features.roomprofile.mocks
import com.airbnb.mvrx.Success
import im.vector.app.features.roomprofile.RoomProfileViewState
import kotlin.Boolean
import kotlin.Int
import kotlin.Long
import kotlin.String
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.UnsignedData
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomEncryptionAlgorithm
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.VersioningState
import org.matrix.android.sdk.api.session.room.model.create.Predecessor
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
val mockRoomProfileViewState by lazy {
RoomProfileViewState(
roomId = "!CWLUCoEWXSFyTCOtfL:matrix.org",
roomSummary = Success(
value = RoomSummary(
roomId = "!CWLUCoEWXSFyTCOtfL:matrix.org",
displayName = "A wrong name",
name = "Megolm test (E2E Encryption Testfest)",
topic = "Test room! Please discuss issues at #e2e:matrix.org. Requires synapse 0.18.0 or later. (0.18.4 for encrypted images); Latest version is recommended.",
avatarUrl = "",
canonicalAlias = "#megolm:matrix.org",
joinRules = RoomJoinRules.PUBLIC,
joinedMembersCount = 199,
latestPreviewableEvent = TimelineEvent(
root = Event(
type = "m.room.encrypted",
eventId = "\$zo2MIeXtdl9dWNhz1cbY3YT55rlpQLG8XpYBTFN-JXI",
content = mutableMapOf(
"algorithm" to "m.megolm.v1.aes-sha2",
"ciphertext" to "AwgAEoADprWrq3ZJ4BLD/w0+Lx9H/3mXZpnEQdgbF2FoWOPB1fuki6/1w7vikL1Sb6rP5AwMuA4TPJQ14Pl++/TCGCUzRoZE9m8TsYe0nQXKb2VWRDB6u7i1ib27l3epbtQAIOYfKuCg6MnbMmadef9fEA1K4lHl3xxf0SdWF9PQKGq3nf9rhcZwa3AZW+FDepI094NO98bTHmssKJ5YTp1ChStjALy4FwdcfanlD5WWPHbKqij/RIC63FNAspIOUeb3AkBmcAdoUctBrxXml+NpJKKCGKxXvFudeSkiqHMG",
"device_id" to "QBOXOIATIX",
"sender_key" to "dGWOgnrcFYOdsYAfwvKJQcDr4dwuZ0R743B3ZlGSMWU",
"session_id" to "Eg1c1yyhczbhGt2baH4m6qI4TpJxN0Kb59TxZ3JY5lE"
),
originServerTs = 1661232614808,
senderId = "@DatseMultimedia:matrix.org",
roomId = "!CWLUCoEWXSFyTCOtfL:matrix.org",
unsignedData = UnsignedData(age = 7112957)
),
localId = 133,
eventId = "\$zo2MIeXtdl9dWNhz1cbY3YT55rlpQLG8XpYBTFN-JXI",
displayIndex = 11,
senderInfo = SenderInfo(
userId = "@DatseMultimedia:matrix.org",
displayName = "Jigme Datse (they/them)",
isUniqueDisplayName = true,
avatarUrl = "mxc://matrix.org/aIPbYElyzjKrOWPaWkxbWEqy"
)
),
otherMemberIds = listOf(
"@zocker1999:matrix.org",
"@denisea:element.io",
"@+:jae.fi"
),
membership = Membership.JOIN,
versioningState = VersioningState.NONE,
readMarkerId = "\$-wLehwJjk4gGG8igOfp-giNzGQruvlZMI-Vti1YYxHA",
isEncrypted = true,
encryptionEventTs = 1574382534883,
typingUsers = listOf(
),
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Default,
roomEncryptionAlgorithm = RoomEncryptionAlgorithm.Megolm
)
),
roomCreateContent = Success(
value = RoomCreateContent(
creator = "@abuse:matrix.org",
roomVersion = "5",
predecessor = Predecessor(roomId = "!UCnwUWwIKhcpaPTHtR:sw1v.org", eventId = "\$1574382534206ivbfT:matrix.org")
)
),
bannedMembership = Success(
value = listOf(
)
),
actionPermissions = RoomProfileViewState.ActionPermissions(),
recommendedRoomVersion = "9"
)
}