Move the button to open the Showkase browser to the developer settings screen. (#389)

This commit is contained in:
Benoit Marty
2023-05-04 17:29:03 +02:00
parent 7e6341c4f3
commit e8049f81a4
12 changed files with 47 additions and 165 deletions

View File

@@ -3,6 +3,5 @@ appId: ${APP_ID}
- clearState
- launchApp:
clearKeychain: true
- tapOn: "Close showkase button"
- runFlow: ./assertions/assertInitDisplayed.yaml
- takeScreenshot: build/maestro/000-FirstScreen

View File

@@ -16,7 +16,6 @@
package io.element.android.appnav
import android.app.Activity
import android.content.Intent
import android.os.Parcelable
import androidx.compose.foundation.layout.Box
@@ -24,7 +23,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.modality.BuildContext
@@ -53,7 +51,6 @@ import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.tests.uitests.openShowkase
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -140,17 +137,11 @@ class RootFlowNode @AssistedInject constructor(
@Composable
override fun View(modifier: Modifier) {
val activity = LocalContext.current as Activity
fun openShowkase() {
openShowkase(activity)
}
val state = presenter.present()
RootView(
state = state,
modifier = modifier,
onOpenBugReport = this::onOpenBugReport,
onOpenShowkase = ::openShowkase
) {
Children(
navModel = backstack,

View File

@@ -16,6 +16,4 @@
package io.element.android.appnav.root
sealed interface RootEvents {
object HideShowkaseButton : RootEvents
}
sealed interface RootEvents

View File

@@ -17,8 +17,6 @@
package io.element.android.appnav.root
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.features.rageshake.api.crash.CrashDetectionPresenter
import io.element.android.features.rageshake.api.detection.RageshakeDetectionPresenter
import io.element.android.libraries.architecture.Presenter
@@ -31,20 +29,13 @@ class RootPresenter @Inject constructor(
@Composable
override fun present(): RootState {
val isShowkaseButtonVisible = rememberSaveable {
mutableStateOf(true)
}
val rageshakeDetectionState = rageshakeDetectionPresenter.present()
val crashDetectionState = crashDetectionPresenter.present()
fun handleEvent(event: RootEvents) {
when (event) {
RootEvents.HideShowkaseButton -> isShowkaseButtonVisible.value = false
}
fun handleEvent(@Suppress("UNUSED_PARAMETER") event: RootEvents) {
}
return RootState(
isShowkaseButtonVisible = isShowkaseButtonVisible.value,
rageshakeDetectionState = rageshakeDetectionState,
crashDetectionState = crashDetectionState,
eventSink = ::handleEvent

View File

@@ -22,7 +22,6 @@ import io.element.android.features.rageshake.api.detection.RageshakeDetectionSta
@Immutable
data class RootState(
val isShowkaseButtonVisible: Boolean,
val rageshakeDetectionState: RageshakeDetectionState,
val crashDetectionState: CrashDetectionState,
val eventSink: (RootEvents) -> Unit

View File

@@ -24,12 +24,10 @@ open class RootStateProvider : PreviewParameterProvider<RootState> {
override val values: Sequence<RootState>
get() = sequenceOf(
aRootState().copy(
isShowkaseButtonVisible = true,
rageshakeDetectionState = aRageshakeDetectionState().copy(showDialog = false),
crashDetectionState = aCrashDetectionState().copy(crashDetected = true),
),
aRootState().copy(
isShowkaseButtonVisible = true,
rageshakeDetectionState = aRageshakeDetectionState().copy(showDialog = true),
crashDetectionState = aCrashDetectionState().copy(crashDetected = false),
)
@@ -37,7 +35,6 @@ open class RootStateProvider : PreviewParameterProvider<RootState> {
}
fun aRootState() = RootState(
isShowkaseButtonVisible = false,
rageshakeDetectionState = aRageshakeDetectionState(),
crashDetectionState = aCrashDetectionState(),
eventSink = {}

View File

@@ -37,7 +37,6 @@ fun RootView(
state: RootState,
modifier: Modifier = Modifier,
onOpenBugReport: () -> Unit = {},
onOpenShowkase: () -> Unit = {},
children: @Composable BoxScope.() -> Unit,
) {
Box(
@@ -54,11 +53,6 @@ fun RootView(
onOpenBugReport.invoke()
}
ShowkaseButton(
isVisible = state.isShowkaseButtonVisible,
onCloseClicked = { eventSink(RootEvents.HideShowkaseButton) },
onClick = onOpenShowkase
)
RageshakeDetectionView(
state = state.rageshakeDetectionState,
onOpenBugReport = ::onOpenBugReport,

View File

@@ -1,71 +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.appnav.root
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Text
@Composable
internal fun ShowkaseButton(
isVisible: Boolean,
modifier: Modifier = Modifier,
onClick: () -> Unit = {},
onCloseClicked: () -> Unit = {},
) {
if (isVisible) {
Button(
modifier = modifier
.padding(top = 32.dp),
onClick = onClick
) {
Text(text = "Showkase Browser")
IconButton(
modifier = Modifier
.padding(start = 8.dp)
.size(16.dp),
onClick = onCloseClicked,
) {
Icon(imageVector = Icons.Filled.Close, contentDescription = "Close showkase button")
}
}
}
}
@Preview
@Composable
internal fun ShowkaseButtonLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun ShowkaseButtonDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
ShowkaseButton(isVisible = true)
}

View File

@@ -44,21 +44,7 @@ class RootPresenterTest {
}.test {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.isShowkaseButtonVisible).isTrue()
}
}
@Test
fun `present - hide showkase button`() = runTest {
val presenter = createPresenter()
moleculeFlow(RecompositionClock.Immediate) {
presenter.present()
}.test {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.isShowkaseButtonVisible).isTrue()
initialState.eventSink.invoke(RootEvents.HideShowkaseButton)
assertThat(awaitItem().isShowkaseButtonVisible).isFalse()
assertThat(initialState.crashDetectionState.crashDetected).isFalse()
}
}

View File

@@ -16,8 +16,12 @@
package io.element.android.features.preferences.impl.developer
import android.app.Activity
import android.content.Intent
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.airbnb.android.showkase.ui.ShowkaseBrowserActivity
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
@@ -35,11 +39,22 @@ class DeveloperSettingsNode @AssistedInject constructor(
@Composable
override fun View(modifier: Modifier) {
val activity = LocalContext.current as Activity
fun openShowkase() {
val intent = Intent(activity, ShowkaseBrowserActivity::class.java)
intent.putExtra(
"SHOWKASE_ROOT_MODULE",
"io.element.android.libraries.designsystem.showkase.DesignSystemShowkaseRootModule"
)
activity.startActivity(intent)
}
val state = presenter.present()
DeveloperSettingsView(
state = state,
modifier = modifier,
onBackPressed = this::navigateUp
onOpenShowkase = ::openShowkase,
onBackPressed = ::navigateUp
)
}
}

View File

@@ -14,28 +14,18 @@
* limitations under the License.
*/
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.features.preferences.impl.developer
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.libraries.designsystem.components.preferences.PreferenceTopAppBar
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
import io.element.android.libraries.designsystem.components.preferences.PreferenceView
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.featureflag.ui.FeatureListView
import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
import io.element.android.libraries.ui.strings.R
@@ -44,44 +34,41 @@ import io.element.android.libraries.ui.strings.R
fun DeveloperSettingsView(
state: DeveloperSettingsState,
modifier: Modifier = Modifier,
onOpenShowkase: () -> Unit,
onBackPressed: () -> Unit,
) {
Scaffold(
modifier = modifier
.fillMaxSize()
.systemBarsPadding()
.imePadding(),
contentWindowInsets = WindowInsets.statusBars,
topBar = {
PreferenceTopAppBar(
title = stringResource(id = R.string.common_developer_options),
onBackPressed = onBackPressed,
)
},
content = {
FeatureListContent(it, state)
PreferenceView(
modifier = modifier,
onBackPressed = onBackPressed,
title = stringResource(id = R.string.common_developer_options)
) {
// Note: this is OK to hardcode strings in this debug screen.
PreferenceCategory(title = "Feature flags") {
FeatureListContent(state)
}
)
PreferenceCategory(title = "Showkase") {
PreferenceText(
title = "Open Showkase browser",
onClick = onOpenShowkase
)
}
}
}
@Composable
fun FeatureListContent(
paddingValues: PaddingValues,
state: DeveloperSettingsState,
modifier: Modifier = Modifier
) {
fun onFeatureEnabled(feature: FeatureUiModel, isEnabled: Boolean) {
state.eventSink(DeveloperSettingsEvents.UpdateEnabledFeature(feature, isEnabled))
}
Box(
modifier = modifier
.padding(paddingValues)
.fillMaxSize()
) {
FeatureListView(features = state.features, onCheckedChange = ::onFeatureEnabled)
}
FeatureListView(
modifier = modifier,
features = state.features,
onCheckedChange = ::onFeatureEnabled,
)
}
@Preview
@@ -98,6 +85,7 @@ fun DeveloperSettingsViewDarkPreview(@PreviewParameter(DeveloperSettingsStatePro
private fun ContentToPreview(state: DeveloperSettingsState) {
DeveloperSettingsView(
state = state,
onOpenShowkase = {},
onBackPressed = {}
)
}

View File

@@ -16,8 +16,7 @@
package io.element.android.libraries.featureflag.ui
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
@@ -34,14 +33,10 @@ fun FeatureListView(
onCheckedChange: (FeatureUiModel, Boolean) -> Unit,
modifier: Modifier = Modifier
) {
LazyColumn(
Column(
modifier = modifier,
) {
items(
items = features,
key = { it.key }
) { feature ->
features.forEach { feature ->
fun onCheckedChange(isChecked: Boolean) {
onCheckedChange(feature, isChecked)
}