Merge pull request #742 from vector-im/feature/bma/settingsUi
Settings UI
This commit is contained in:
commit
e7331e8be0
182 changed files with 1736 additions and 715 deletions
|
|
@ -18,6 +18,7 @@ package io.element.android.features.analytics.api.preferences
|
|||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -27,7 +28,6 @@ import androidx.compose.ui.text.style.TextDecoration
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvents
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
|
|
@ -43,20 +43,21 @@ fun AnalyticsPreferencesView(
|
|||
state.eventSink(AnalyticsOptInEvents.EnableAnalytics(isEnabled = isEnabled))
|
||||
}
|
||||
|
||||
PreferenceCategory(title = stringResource(id = CommonStrings.screen_analytics_settings_share_data)) {
|
||||
val firstPart = stringResource(id = CommonStrings.screen_analytics_settings_help_us_improve, state.applicationName)
|
||||
val secondPart = buildAnnotatedStringWithColoredPart(
|
||||
CommonStrings.screen_analytics_settings_read_terms,
|
||||
CommonStrings.screen_analytics_settings_read_terms_content_link
|
||||
)
|
||||
val title = "$firstPart\n\n$secondPart"
|
||||
val firstPart = stringResource(id = CommonStrings.screen_analytics_settings_help_us_improve, state.applicationName)
|
||||
val secondPart = buildAnnotatedStringWithColoredPart(
|
||||
CommonStrings.screen_analytics_settings_read_terms,
|
||||
CommonStrings.screen_analytics_settings_read_terms_content_link
|
||||
)
|
||||
val subtitle = "$firstPart\n\n$secondPart"
|
||||
|
||||
PreferenceSwitch(
|
||||
title = title,
|
||||
isChecked = state.isEnabled,
|
||||
onCheckedChange = ::onEnabledChanged
|
||||
)
|
||||
}
|
||||
PreferenceSwitch(
|
||||
modifier = modifier,
|
||||
title = stringResource(id = CommonStrings.screen_analytics_settings_share_data),
|
||||
subtitle = subtitle,
|
||||
isChecked = state.isEnabled,
|
||||
onCheckedChange = ::onEnabledChanged,
|
||||
switchAlignment = Alignment.Top,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ private fun AnalyticsOptInContentRow(
|
|||
.padding(2.dp),
|
||||
imageVector = Icons.Rounded.Check,
|
||||
contentDescription = null,
|
||||
tint = ElementTheme.colors.iconSuccessPrimary,
|
||||
tint = ElementTheme.colors.textActionAccent,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
|
|
|
|||
|
|
@ -21,9 +21,8 @@ import app.cash.molecule.moleculeFlow
|
|||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvents
|
||||
import io.element.android.features.analytics.impl.preferences.DefaultAnalyticsPreferencesPresenter
|
||||
import io.element.android.features.analytics.test.A_BUILD_META
|
||||
import io.element.android.features.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
|
@ -33,7 +32,7 @@ class AnalyticsOptInPresenterTest {
|
|||
fun `present - enable`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService(isEnabled = false)
|
||||
val presenter = AnalyticsOptInPresenter(
|
||||
A_BUILD_META,
|
||||
aBuildMeta(),
|
||||
analyticsService
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
|
|
@ -51,7 +50,7 @@ class AnalyticsOptInPresenterTest {
|
|||
fun `present - not now`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService(isEnabled = false)
|
||||
val presenter = AnalyticsOptInPresenter(
|
||||
A_BUILD_META,
|
||||
aBuildMeta(),
|
||||
analyticsService
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import app.cash.molecule.moleculeFlow
|
|||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.analytics.api.AnalyticsOptInEvents
|
||||
import io.element.android.features.analytics.test.A_BUILD_META
|
||||
import io.element.android.features.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ class AnalyticsPreferencesPresenterTest {
|
|||
fun `present - initial state available`() = runTest {
|
||||
val presenter = DefaultAnalyticsPreferencesPresenter(
|
||||
FakeAnalyticsService(isEnabled = true, didAskUserConsent = true),
|
||||
A_BUILD_META
|
||||
aBuildMeta()
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
|
|
@ -46,7 +46,7 @@ class AnalyticsPreferencesPresenterTest {
|
|||
fun `present - initial state not available`() = runTest {
|
||||
val presenter = DefaultAnalyticsPreferencesPresenter(
|
||||
FakeAnalyticsService(isEnabled = false, didAskUserConsent = false),
|
||||
A_BUILD_META
|
||||
aBuildMeta()
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
|
|
@ -60,7 +60,7 @@ class AnalyticsPreferencesPresenterTest {
|
|||
fun `present - enable and disable`() = runTest {
|
||||
val presenter = DefaultAnalyticsPreferencesPresenter(
|
||||
FakeAnalyticsService(isEnabled = true, didAskUserConsent = true),
|
||||
A_BUILD_META
|
||||
aBuildMeta()
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import io.element.android.libraries.matrix.api.core.UserId
|
|||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.test.A_THROWABLE
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.usersearch.test.FakeUserRepository
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -183,18 +184,3 @@ class CreateRoomRootPresenterTests {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun aBuildMeta() =
|
||||
BuildMeta(
|
||||
buildType = BuildType.DEBUG,
|
||||
isDebuggable = true,
|
||||
applicationId = "",
|
||||
applicationName = "An Application",
|
||||
lowPrivacyLoggingEnabled = true,
|
||||
versionName = "",
|
||||
gitRevision = "",
|
||||
gitBranchName = "",
|
||||
gitRevisionDate = "",
|
||||
flavorDescription = "",
|
||||
flavorShortDescription = "",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -27,11 +27,9 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.designsystem.components.ProgressDialog
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun LogoutPreferenceView(
|
||||
|
|
@ -81,13 +79,11 @@ fun LogoutPreferenceView(
|
|||
fun LogoutPreferenceContent(
|
||||
onClick: () -> Unit = {},
|
||||
) {
|
||||
PreferenceCategory(title = stringResource(id = CommonStrings.settings_title_general)) {
|
||||
PreferenceText(
|
||||
title = stringResource(id = R.string.screen_signout_preference_item),
|
||||
icon = Icons.Default.Logout,
|
||||
onClick = onClick
|
||||
)
|
||||
}
|
||||
PreferenceText(
|
||||
title = stringResource(id = R.string.screen_signout_preference_item),
|
||||
icon = Icons.Filled.Logout,
|
||||
onClick = onClick
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
|||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID_2
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import io.element.android.libraries.mediapickers.test.FakePickerProvider
|
||||
|
|
@ -566,19 +567,7 @@ class MessagesPresenterTest {
|
|||
timelineItemsFactory = aTimelineItemsFactory(),
|
||||
room = matrixRoom,
|
||||
)
|
||||
val buildMeta = BuildMeta(
|
||||
buildType = BuildType.DEBUG,
|
||||
isDebuggable = true,
|
||||
applicationId = "",
|
||||
applicationName = "",
|
||||
lowPrivacyLoggingEnabled = true,
|
||||
versionName = "",
|
||||
gitRevision = "",
|
||||
gitBranchName = "",
|
||||
gitRevisionDate = "",
|
||||
flavorDescription = "",
|
||||
flavorShortDescription = "",
|
||||
)
|
||||
val buildMeta = aBuildMeta()
|
||||
val actionListPresenter = ActionListPresenter(buildMeta = buildMeta)
|
||||
val customReactionPresenter = CustomReactionPresenter()
|
||||
val retrySendMenuPresenter = RetrySendMenuPresenter(room = matrixRoom)
|
||||
|
|
|
|||
|
|
@ -25,13 +25,11 @@ import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
|||
import io.element.android.features.messages.impl.actionlist.ActionListPresenter
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListState
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.core.meta.BuildType
|
||||
import io.element.android.libraries.matrix.test.A_MESSAGE
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
|
@ -74,7 +72,6 @@ class ActionListPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `present - compute for message from others redacted`() = runTest {
|
||||
val presenter = anActionListPresenter(isBuildDebuggable = true)
|
||||
|
|
@ -231,31 +228,5 @@ class ActionListPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
private fun aBuildMeta(
|
||||
buildType: BuildType = BuildType.DEBUG,
|
||||
isDebuggable: Boolean = true,
|
||||
applicationName: String = "",
|
||||
applicationId: String = "",
|
||||
lowPrivacyLoggingEnabled: Boolean = true,
|
||||
versionName: String = "",
|
||||
gitRevision: String = "",
|
||||
gitRevisionDate: String = "",
|
||||
gitBranchName: String = "",
|
||||
flavorDescription: String = "",
|
||||
flavorShortDescription: String = "",
|
||||
) = BuildMeta(
|
||||
buildType,
|
||||
isDebuggable,
|
||||
applicationName,
|
||||
applicationId,
|
||||
lowPrivacyLoggingEnabled,
|
||||
versionName,
|
||||
gitRevision,
|
||||
gitRevisionDate,
|
||||
gitBranchName,
|
||||
flavorDescription,
|
||||
flavorShortDescription
|
||||
)
|
||||
|
||||
private fun anActionListPresenter(isBuildDebuggable: Boolean) = ActionListPresenter(buildMeta = aBuildMeta(isDebuggable = isBuildDebuggable))
|
||||
|
||||
|
|
|
|||
|
|
@ -32,5 +32,6 @@ interface PreferencesEntryPoint : FeatureEntryPoint {
|
|||
|
||||
interface Callback : Plugin {
|
||||
fun onOpenBugReport()
|
||||
fun onVerifyClicked()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,9 +46,11 @@ dependencies {
|
|||
implementation(projects.features.analytics.api)
|
||||
implementation(projects.libraries.matrixui)
|
||||
implementation(projects.features.logout.api)
|
||||
implementation(projects.services.toolbox.api)
|
||||
implementation(libs.datetime)
|
||||
implementation(libs.accompanist.placeholder)
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.androidx.browser)
|
||||
api(projects.features.preferences.api)
|
||||
ksp(libs.showkase.processor)
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ import dagger.assisted.Assisted
|
|||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.preferences.api.PreferencesEntryPoint
|
||||
import io.element.android.features.preferences.impl.about.AboutNode
|
||||
import io.element.android.features.preferences.impl.analytics.AnalyticsSettingsNode
|
||||
import io.element.android.features.preferences.impl.developer.DeveloperSettingsNode
|
||||
import io.element.android.features.preferences.impl.root.PreferencesRootNode
|
||||
import io.element.android.libraries.architecture.BackstackNode
|
||||
|
|
@ -57,6 +59,12 @@ class PreferencesFlowNode @AssistedInject constructor(
|
|||
|
||||
@Parcelize
|
||||
object DeveloperSettings : NavTarget
|
||||
|
||||
@Parcelize
|
||||
object AnalyticsSettings : NavTarget
|
||||
|
||||
@Parcelize
|
||||
object About : NavTarget
|
||||
}
|
||||
|
||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||
|
|
@ -67,6 +75,18 @@ class PreferencesFlowNode @AssistedInject constructor(
|
|||
plugins<PreferencesEntryPoint.Callback>().forEach { it.onOpenBugReport() }
|
||||
}
|
||||
|
||||
override fun onVerifyClicked() {
|
||||
plugins<PreferencesEntryPoint.Callback>().forEach { it.onVerifyClicked() }
|
||||
}
|
||||
|
||||
override fun onOpenAnalytics() {
|
||||
backstack.push(NavTarget.AnalyticsSettings)
|
||||
}
|
||||
|
||||
override fun onOpenAbout() {
|
||||
backstack.push(NavTarget.About)
|
||||
}
|
||||
|
||||
override fun onOpenDeveloperSettings() {
|
||||
backstack.push(NavTarget.DeveloperSettings)
|
||||
}
|
||||
|
|
@ -76,6 +96,12 @@ class PreferencesFlowNode @AssistedInject constructor(
|
|||
NavTarget.DeveloperSettings -> {
|
||||
createNode<DeveloperSettingsNode>(buildContext)
|
||||
}
|
||||
NavTarget.About -> {
|
||||
createNode<AboutNode>(buildContext)
|
||||
}
|
||||
NavTarget.AnalyticsSettings -> {
|
||||
createNode<AnalyticsSettingsNode>(buildContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.preferences.impl.about
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
class AboutNode @AssistedInject constructor(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val presenter: AboutPresenter,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
|
||||
private fun onElementLegalClicked(
|
||||
activity: Activity,
|
||||
darkTheme: Boolean,
|
||||
elementLegal: ElementLegal,
|
||||
) {
|
||||
activity.openUrlInChromeCustomTab(null, darkTheme, elementLegal.url)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val activity = LocalContext.current as Activity
|
||||
val isDark = ElementTheme.isLightTheme.not()
|
||||
val state = presenter.present()
|
||||
AboutView(
|
||||
state = state,
|
||||
onBackPressed = ::navigateUp,
|
||||
onElementLegalClicked = { elementLegal ->
|
||||
onElementLegalClicked(activity, isDark, elementLegal)
|
||||
},
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.preferences.impl.about
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import javax.inject.Inject
|
||||
|
||||
class AboutPresenter @Inject constructor() : Presenter<AboutState> {
|
||||
|
||||
@Composable
|
||||
override fun present(): AboutState {
|
||||
|
||||
return AboutState(
|
||||
elementLegals = getAllLegals(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.preferences.impl.about
|
||||
|
||||
// Do not use default value, so no member get forgotten in the presenters.
|
||||
data class AboutState(
|
||||
val elementLegals: List<ElementLegal>,
|
||||
)
|
||||
|
|
@ -14,22 +14,17 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.analytics.test
|
||||
package io.element.android.features.preferences.impl.about
|
||||
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.core.meta.BuildType
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
|
||||
val A_BUILD_META = BuildMeta(
|
||||
isDebuggable = true,
|
||||
buildType = BuildType.DEBUG,
|
||||
applicationName = "Element X test",
|
||||
applicationId = "",
|
||||
lowPrivacyLoggingEnabled = false,
|
||||
versionName = "",
|
||||
gitRevision = "",
|
||||
gitRevisionDate = "",
|
||||
gitBranchName = "",
|
||||
flavorDescription = "",
|
||||
flavorShortDescription = "",
|
||||
open class AboutStateProvider : PreviewParameterProvider<AboutState> {
|
||||
override val values: Sequence<AboutState>
|
||||
get() = sequenceOf(
|
||||
aAboutState(),
|
||||
)
|
||||
}
|
||||
|
||||
fun aAboutState() = AboutState(
|
||||
elementLegals = getAllLegals(),
|
||||
)
|
||||
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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.preferences.impl.about
|
||||
|
||||
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.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.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun AboutView(
|
||||
state: AboutState,
|
||||
onElementLegalClicked: (ElementLegal) -> Unit,
|
||||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
PreferenceView(
|
||||
modifier = modifier,
|
||||
onBackPressed = onBackPressed,
|
||||
title = stringResource(id = CommonStrings.common_about)
|
||||
) {
|
||||
state.elementLegals.forEach { elementLegal ->
|
||||
PreferenceText(
|
||||
title = stringResource(id = elementLegal.titleRes),
|
||||
onClick = { onElementLegalClicked(elementLegal) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun AboutViewLightPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) =
|
||||
ElementPreviewLight { ContentToPreview(state) }
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun AboutViewDarkPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) =
|
||||
ElementPreviewDark { ContentToPreview(state) }
|
||||
|
||||
@Composable
|
||||
private fun ContentToPreview(state: AboutState) {
|
||||
AboutView(
|
||||
state = state,
|
||||
onElementLegalClicked = {},
|
||||
onBackPressed = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.preferences.impl.about
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
private const val CopyrightUrl = "https://element.io/copyright"
|
||||
private const val UsePolicyUrl = "https://element.io/acceptable-use-policy-terms"
|
||||
private const val PrivacyUrl = "https://element.io/privacy"
|
||||
|
||||
sealed class ElementLegal(
|
||||
@StringRes val titleRes: Int,
|
||||
val url: String,
|
||||
) {
|
||||
object Copyright : ElementLegal(CommonStrings.common_copyright, CopyrightUrl)
|
||||
object AcceptableUsePolicy : ElementLegal(CommonStrings.common_acceptable_use_policy, UsePolicyUrl)
|
||||
object PrivacyPolicy : ElementLegal(CommonStrings.common_privacy_policy, PrivacyUrl)
|
||||
}
|
||||
|
||||
fun getAllLegals(): List<ElementLegal> {
|
||||
return listOf(
|
||||
ElementLegal.Copyright,
|
||||
ElementLegal.AcceptableUsePolicy,
|
||||
ElementLegal.PrivacyPolicy,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.preferences.impl.analytics
|
||||
|
||||
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 dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
class AnalyticsSettingsNode @AssistedInject constructor(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val presenter: AnalyticsSettingsPresenter,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
AnalyticsSettingsView(
|
||||
state = state,
|
||||
onBackPressed = ::navigateUp,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.preferences.impl.analytics
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesPresenter
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import javax.inject.Inject
|
||||
|
||||
class AnalyticsSettingsPresenter @Inject constructor(
|
||||
private val analyticsPresenter: AnalyticsPreferencesPresenter,
|
||||
) : Presenter<AnalyticsSettingsState> {
|
||||
|
||||
@Composable
|
||||
override fun present(): AnalyticsSettingsState {
|
||||
val analyticsState = analyticsPresenter.present()
|
||||
|
||||
return AnalyticsSettingsState(
|
||||
analyticsState = analyticsState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.preferences.impl.analytics
|
||||
|
||||
import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesState
|
||||
|
||||
// Do not use default value, so no member get forgotten in the presenters.
|
||||
data class AnalyticsSettingsState(
|
||||
val analyticsState: AnalyticsPreferencesState,
|
||||
)
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.preferences.impl.analytics
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.analytics.api.preferences.aAnalyticsPreferencesState
|
||||
|
||||
open class AnalyticsSettingsStateProvider : PreviewParameterProvider<AnalyticsSettingsState> {
|
||||
override val values: Sequence<AnalyticsSettingsState>
|
||||
get() = sequenceOf(
|
||||
aAnalyticsSettingsState(),
|
||||
)
|
||||
}
|
||||
|
||||
fun aAnalyticsSettingsState() = AnalyticsSettingsState(
|
||||
analyticsState = aAnalyticsPreferencesState(),
|
||||
)
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.preferences.impl.analytics
|
||||
|
||||
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.features.analytics.api.preferences.AnalyticsPreferencesView
|
||||
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.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun AnalyticsSettingsView(
|
||||
state: AnalyticsSettingsState,
|
||||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
PreferenceView(
|
||||
modifier = modifier,
|
||||
onBackPressed = onBackPressed,
|
||||
title = stringResource(id = CommonStrings.common_analytics)
|
||||
) {
|
||||
AnalyticsPreferencesView(
|
||||
state = state.analyticsState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun AnalyticsSettingsViewLightPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) =
|
||||
ElementPreviewLight { ContentToPreview(state) }
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun AnalyticsSettingsViewDarkPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) =
|
||||
ElementPreviewDark { ContentToPreview(state) }
|
||||
|
||||
@Composable
|
||||
private fun ContentToPreview(state: AnalyticsSettingsState) {
|
||||
AnalyticsSettingsView(
|
||||
state = state,
|
||||
onBackPressed = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||
import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase
|
||||
import io.element.android.features.preferences.impl.tasks.ComputeCacheSizeUseCase
|
||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||
|
|
@ -44,10 +45,12 @@ class DeveloperSettingsPresenter @Inject constructor(
|
|||
private val featureFlagService: FeatureFlagService,
|
||||
private val computeCacheSizeUseCase: ComputeCacheSizeUseCase,
|
||||
private val clearCacheUseCase: ClearCacheUseCase,
|
||||
private val rageshakePresenter: RageshakePreferencesPresenter,
|
||||
) : Presenter<DeveloperSettingsState> {
|
||||
|
||||
@Composable
|
||||
override fun present(): DeveloperSettingsState {
|
||||
val rageshakeState = rageshakePresenter.present()
|
||||
|
||||
val features = remember {
|
||||
mutableStateMapOf<String, Feature>()
|
||||
|
|
@ -90,6 +93,7 @@ class DeveloperSettingsPresenter @Inject constructor(
|
|||
features = featureUiModels.toImmutableList(),
|
||||
cacheSize = cacheSize.value,
|
||||
clearCacheAction = clearCacheAction.value,
|
||||
rageshakeState = rageshakeState,
|
||||
eventSink = ::handleEvents
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package io.element.android.features.preferences.impl.developer
|
||||
|
||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -23,6 +24,7 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
data class DeveloperSettingsState constructor(
|
||||
val features: ImmutableList<FeatureUiModel>,
|
||||
val cacheSize: Async<String>,
|
||||
val rageshakeState: RageshakePreferencesState,
|
||||
val clearCacheAction: Async<Unit>,
|
||||
val eventSink: (DeveloperSettingsEvents) -> Unit
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package io.element.android.features.preferences.impl.developer
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.rageshake.api.preferences.aRageshakePreferencesState
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.featureflag.ui.model.aFeatureUiModelList
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ open class DeveloperSettingsStateProvider : PreviewParameterProvider<DeveloperSe
|
|||
|
||||
fun aDeveloperSettingsState() = DeveloperSettingsState(
|
||||
features = aFeatureUiModelList(),
|
||||
rageshakeState = aRageshakePreferencesState(),
|
||||
cacheSize = Async.Success("1.2 MB"),
|
||||
clearCacheAction = Async.Uninitialized,
|
||||
eventSink = {}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,12 @@
|
|||
|
||||
package io.element.android.features.preferences.impl.developer
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
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.features.rageshake.api.preferences.RageshakePreferencesView
|
||||
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
|
||||
|
|
@ -54,11 +53,13 @@ fun DeveloperSettingsView(
|
|||
onClick = onOpenShowkase
|
||||
)
|
||||
}
|
||||
RageshakePreferencesView(
|
||||
state = state.rageshakeState,
|
||||
)
|
||||
val cache = state.cacheSize
|
||||
PreferenceCategory(title = "Cache") {
|
||||
PreferenceCategory(title = "Cache", showDivider = false) {
|
||||
PreferenceText(
|
||||
title = "Clear cache",
|
||||
icon = Icons.Default.Delete,
|
||||
currentValue = cache.dataOrNull(),
|
||||
loadingCurrentValue = state.cacheSize.isLoading() || state.clearCacheAction.isLoading(),
|
||||
onClick = {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ class PreferencesRootNode @AssistedInject constructor(
|
|||
|
||||
interface Callback : Plugin {
|
||||
fun onOpenBugReport()
|
||||
fun onVerifyClicked()
|
||||
fun onOpenAnalytics()
|
||||
fun onOpenAbout()
|
||||
fun onOpenDeveloperSettings()
|
||||
}
|
||||
|
||||
|
|
@ -43,10 +46,22 @@ class PreferencesRootNode @AssistedInject constructor(
|
|||
plugins<Callback>().forEach { it.onOpenBugReport() }
|
||||
}
|
||||
|
||||
private fun onVerifyClicked() {
|
||||
plugins<Callback>().forEach { it.onVerifyClicked() }
|
||||
}
|
||||
|
||||
private fun onOpenDeveloperSettings() {
|
||||
plugins<Callback>().forEach { it.onOpenDeveloperSettings() }
|
||||
}
|
||||
|
||||
private fun onOpenAnalytics() {
|
||||
plugins<Callback>().forEach { it.onOpenAnalytics() }
|
||||
}
|
||||
|
||||
private fun onOpenAbout() {
|
||||
plugins<Callback>().forEach { it.onOpenAbout() }
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
|
|
@ -55,8 +70,10 @@ class PreferencesRootNode @AssistedInject constructor(
|
|||
modifier = modifier,
|
||||
onBackPressed = this::navigateUp,
|
||||
onOpenRageShake = this::onOpenBugReport,
|
||||
onOpenAnalytics = this::onOpenAnalytics,
|
||||
onOpenAbout = this::onOpenAbout,
|
||||
onVerifyClicked = this::onVerifyClicked,
|
||||
onOpenDeveloperSettings = this::onOpenDeveloperSettings
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,33 +17,61 @@
|
|||
package io.element.android.features.preferences.impl.root
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesPresenter
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import io.element.android.features.logout.api.LogoutPreferencePresenter
|
||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.meta.BuildType
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.api.user.getCurrentUser
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class PreferencesRootPresenter @Inject constructor(
|
||||
private val logoutPresenter: LogoutPreferencePresenter,
|
||||
private val rageshakePresenter: RageshakePreferencesPresenter,
|
||||
private val analyticsPresenter: AnalyticsPreferencesPresenter,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val sessionVerificationService: SessionVerificationService,
|
||||
private val buildType: BuildType,
|
||||
private val versionFormatter: VersionFormatter,
|
||||
) : Presenter<PreferencesRootState> {
|
||||
|
||||
@Composable
|
||||
override fun present(): PreferencesRootState {
|
||||
val matrixUser: MutableState<MatrixUser?> = rememberSaveable {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
initialLoad(matrixUser)
|
||||
}
|
||||
|
||||
// Session verification status (unknown, not verified, verified)
|
||||
val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState()
|
||||
val sessionIsNotVerified by remember {
|
||||
derivedStateOf { sessionVerifiedStatus == SessionVerifiedStatus.NotVerified }
|
||||
}
|
||||
|
||||
val logoutState = logoutPresenter.present()
|
||||
val rageshakeState = rageshakePresenter.present()
|
||||
val analyticsState = analyticsPresenter.present()
|
||||
val showDeveloperSettings = buildType != BuildType.RELEASE
|
||||
return PreferencesRootState(
|
||||
logoutState = logoutState,
|
||||
rageshakeState = rageshakeState,
|
||||
analyticsState = analyticsState,
|
||||
myUser = Async.Uninitialized,
|
||||
myUser = matrixUser.value,
|
||||
version = versionFormatter.get(),
|
||||
showCompleteVerification = sessionIsNotVerified,
|
||||
showDeveloperSettings = showDeveloperSettings
|
||||
)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.initialLoad(matrixUser: MutableState<MatrixUser?>) = launch {
|
||||
matrixUser.value = matrixClient.getCurrentUser()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,16 +16,13 @@
|
|||
|
||||
package io.element.android.features.preferences.impl.root
|
||||
|
||||
import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesState
|
||||
import io.element.android.features.logout.api.LogoutPreferenceState
|
||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
||||
data class PreferencesRootState(
|
||||
val logoutState: LogoutPreferenceState,
|
||||
val rageshakeState: RageshakePreferencesState,
|
||||
val analyticsState: AnalyticsPreferencesState,
|
||||
val myUser: Async<MatrixUser>,
|
||||
val myUser: MatrixUser?,
|
||||
val version: String,
|
||||
val showCompleteVerification: Boolean,
|
||||
val showDeveloperSettings: Boolean
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,15 +16,12 @@
|
|||
|
||||
package io.element.android.features.preferences.impl.root
|
||||
|
||||
import io.element.android.features.analytics.api.preferences.aAnalyticsPreferencesState
|
||||
import io.element.android.features.logout.api.aLogoutPreferenceState
|
||||
import io.element.android.features.rageshake.api.preferences.aRageshakePreferencesState
|
||||
import io.element.android.libraries.architecture.Async
|
||||
|
||||
fun aPreferencesRootState() = PreferencesRootState(
|
||||
logoutState = aLogoutPreferenceState(),
|
||||
rageshakeState = aRageshakePreferencesState(),
|
||||
analyticsState = aAnalyticsPreferencesState(),
|
||||
myUser = Async.Uninitialized,
|
||||
myUser = null,
|
||||
version = "Version 1.1 (1)",
|
||||
showCompleteVerification = true,
|
||||
showDeveloperSettings = true
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,36 +16,45 @@
|
|||
|
||||
package io.element.android.features.preferences.impl.root
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.DeveloperMode
|
||||
import androidx.compose.material.icons.outlined.BugReport
|
||||
import androidx.compose.material.icons.outlined.DeveloperMode
|
||||
import androidx.compose.material.icons.outlined.Help
|
||||
import androidx.compose.material.icons.outlined.InsertChart
|
||||
import androidx.compose.material.icons.outlined.VerifiedUser
|
||||
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.features.logout.api.LogoutPreferenceView
|
||||
import io.element.android.features.preferences.impl.user.UserPreferences
|
||||
import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesView
|
||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesView
|
||||
import io.element.android.libraries.architecture.Async
|
||||
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.preview.LargeHeightPreview
|
||||
import io.element.android.libraries.designsystem.theme.components.Divider
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.components.MatrixUserProvider
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun PreferencesRootView(
|
||||
state: PreferencesRootState,
|
||||
onBackPressed: () -> Unit,
|
||||
onVerifyClicked: () -> Unit,
|
||||
onOpenAnalytics: () -> Unit,
|
||||
onOpenRageShake: () -> Unit,
|
||||
onOpenAbout: () -> Unit,
|
||||
onOpenDeveloperSettings: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onBackPressed: () -> Unit = {},
|
||||
onOpenRageShake: () -> Unit = {},
|
||||
onOpenDeveloperSettings: () -> Unit = {},
|
||||
) {
|
||||
// TODO Hierarchy!
|
||||
// Include pref from other modules
|
||||
PreferenceView(
|
||||
modifier = modifier,
|
||||
|
|
@ -53,31 +62,55 @@ fun PreferencesRootView(
|
|||
title = stringResource(id = CommonStrings.common_settings)
|
||||
) {
|
||||
UserPreferences(state.myUser)
|
||||
AnalyticsPreferencesView(
|
||||
state = state.analyticsState,
|
||||
if (state.showCompleteVerification) {
|
||||
PreferenceText(
|
||||
title = stringResource(id = CommonStrings.action_complete_verification),
|
||||
icon = Icons.Outlined.VerifiedUser,
|
||||
onClick = onVerifyClicked,
|
||||
)
|
||||
Divider()
|
||||
}
|
||||
PreferenceText(
|
||||
title = stringResource(id = CommonStrings.common_analytics),
|
||||
icon = Icons.Outlined.InsertChart,
|
||||
onClick = onOpenAnalytics,
|
||||
)
|
||||
RageshakePreferencesView(
|
||||
state = state.rageshakeState,
|
||||
onOpenRageshake = onOpenRageShake,
|
||||
PreferenceText(
|
||||
title = stringResource(id = CommonStrings.action_report_bug),
|
||||
icon = Icons.Outlined.BugReport,
|
||||
onClick = onOpenRageShake
|
||||
)
|
||||
LogoutPreferenceView(
|
||||
state = state.logoutState,
|
||||
PreferenceText(
|
||||
title = stringResource(id = CommonStrings.common_about),
|
||||
icon = Icons.Outlined.Help,
|
||||
onClick = onOpenAbout,
|
||||
)
|
||||
if (state.showDeveloperSettings) {
|
||||
DeveloperPreferencesView(onOpenDeveloperSettings)
|
||||
}
|
||||
Divider()
|
||||
LogoutPreferenceView(
|
||||
state = state.logoutState,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 40.dp, bottom = 24.dp),
|
||||
textAlign = TextAlign.Center,
|
||||
text = state.version,
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
color = ElementTheme.materialColors.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DeveloperPreferencesView(onOpenDeveloperSettings: () -> Unit) {
|
||||
PreferenceCategory(title = stringResource(id = CommonStrings.common_developer_options)) {
|
||||
PreferenceText(
|
||||
title = stringResource(id = CommonStrings.common_developer_options),
|
||||
icon = Icons.Default.DeveloperMode,
|
||||
onClick = onOpenDeveloperSettings
|
||||
)
|
||||
}
|
||||
PreferenceText(
|
||||
title = stringResource(id = CommonStrings.common_developer_options),
|
||||
icon = Icons.Outlined.DeveloperMode,
|
||||
onClick = onOpenDeveloperSettings
|
||||
)
|
||||
}
|
||||
|
||||
@LargeHeightPreview
|
||||
|
|
@ -92,5 +125,13 @@ fun PreferencesRootViewDarkPreview(@PreviewParameter(MatrixUserProvider::class)
|
|||
|
||||
@Composable
|
||||
private fun ContentToPreview(matrixUser: MatrixUser) {
|
||||
PreferencesRootView(aPreferencesRootState().copy(myUser = Async.Success(matrixUser)))
|
||||
PreferencesRootView(
|
||||
state = aPreferencesRootState().copy(myUser = matrixUser),
|
||||
onBackPressed = {},
|
||||
onOpenAnalytics = {},
|
||||
onOpenRageShake = {},
|
||||
onOpenDeveloperSettings = {},
|
||||
onOpenAbout = {},
|
||||
onVerifyClicked = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.preferences.impl.root
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
interface VersionFormatter {
|
||||
fun get(): String
|
||||
}
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultVersionFormatter @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val buildMeta: BuildMeta,
|
||||
) : VersionFormatter {
|
||||
override fun get(): String {
|
||||
return stringProvider.getString(
|
||||
CommonStrings.settings_version_number,
|
||||
buildMeta.versionName,
|
||||
buildMeta.versionCode.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FakeVersionFormatter : VersionFormatter {
|
||||
override fun get(): String {
|
||||
return "A Version"
|
||||
}
|
||||
}
|
||||
|
|
@ -16,14 +16,10 @@
|
|||
|
||||
package io.element.android.features.preferences.impl.user
|
||||
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
|
@ -32,16 +28,13 @@ import io.element.android.libraries.matrix.ui.components.MatrixUserWithNullProvi
|
|||
|
||||
@Composable
|
||||
fun UserPreferences(
|
||||
user: Async<MatrixUser>,
|
||||
user: MatrixUser?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (val userData = user.dataOrNull()) {
|
||||
null -> Spacer(modifier = modifier.height(1.dp))
|
||||
else -> MatrixUserHeader(
|
||||
modifier = modifier,
|
||||
matrixUser = userData
|
||||
)
|
||||
}
|
||||
MatrixUserHeader(
|
||||
modifier = modifier,
|
||||
matrixUser = user
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
|
|
@ -56,9 +49,5 @@ internal fun UserPreferencesDarkPreview(@PreviewParameter(MatrixUserWithNullProv
|
|||
|
||||
@Composable
|
||||
private fun ContentToPreview(matrixUser: MatrixUser?) {
|
||||
if (matrixUser == null) {
|
||||
UserPreferences(Async.Uninitialized)
|
||||
} else {
|
||||
UserPreferences(Async.Success(matrixUser))
|
||||
}
|
||||
UserPreferences(matrixUser)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.preferences.impl.about
|
||||
|
||||
import app.cash.molecule.RecompositionClock
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class AboutPresenterTest {
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = AboutPresenter()
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.elementLegals).isEqualTo(getAllLegals())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.preferences.impl.analytics
|
||||
|
||||
import app.cash.molecule.RecompositionClock
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.analytics.impl.preferences.DefaultAnalyticsPreferencesPresenter
|
||||
import io.element.android.features.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class AnalyticsAnalyticsSettingsPresenterTest {
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val analyticsPresenter = DefaultAnalyticsPreferencesPresenter(FakeAnalyticsService(), aBuildMeta())
|
||||
val presenter = AnalyticsSettingsPresenter(
|
||||
analyticsPresenter,
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.analyticsState.isEnabled).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,9 @@ import app.cash.turbine.test
|
|||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.preferences.impl.tasks.FakeClearCacheUseCase
|
||||
import io.element.android.features.preferences.impl.tasks.FakeComputeCacheSizeUseCase
|
||||
import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePreferencesPresenter
|
||||
import io.element.android.features.rageshake.test.rageshake.FakeRageShake
|
||||
import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
|
|
@ -31,10 +34,12 @@ import org.junit.Test
|
|||
class DeveloperSettingsPresenterTest {
|
||||
@Test
|
||||
fun `present - ensures initial state is correct`() = runTest {
|
||||
val rageshakePresenter = DefaultRageshakePreferencesPresenter(FakeRageShake(), FakeRageshakeDataStore())
|
||||
val presenter = DeveloperSettingsPresenter(
|
||||
FakeFeatureFlagService(),
|
||||
FakeComputeCacheSizeUseCase(),
|
||||
FakeClearCacheUseCase(),
|
||||
rageshakePresenter
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
|
|
@ -43,16 +48,22 @@ class DeveloperSettingsPresenterTest {
|
|||
assertThat(initialState.features).isEmpty()
|
||||
assertThat(initialState.clearCacheAction).isEqualTo(Async.Uninitialized)
|
||||
assertThat(initialState.cacheSize).isEqualTo(Async.Uninitialized)
|
||||
val loadedState = awaitItem()
|
||||
assertThat(loadedState.rageshakeState.isEnabled).isFalse()
|
||||
assertThat(loadedState.rageshakeState.isSupported).isTrue()
|
||||
assertThat(loadedState.rageshakeState.sensitivity).isEqualTo(1.0f)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - ensures feature list is loaded`() = runTest {
|
||||
val rageshakePresenter = DefaultRageshakePreferencesPresenter(FakeRageShake(), FakeRageshakeDataStore())
|
||||
val presenter = DeveloperSettingsPresenter(
|
||||
FakeFeatureFlagService(),
|
||||
FakeComputeCacheSizeUseCase(),
|
||||
FakeClearCacheUseCase(),
|
||||
rageshakePresenter,
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
|
|
@ -66,10 +77,12 @@ class DeveloperSettingsPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `present - ensures state is updated when enabled feature event is triggered`() = runTest {
|
||||
val rageshakePresenter = DefaultRageshakePreferencesPresenter(FakeRageShake(), FakeRageshakeDataStore())
|
||||
val presenter = DeveloperSettingsPresenter(
|
||||
FakeFeatureFlagService(),
|
||||
FakeComputeCacheSizeUseCase(),
|
||||
FakeClearCacheUseCase(),
|
||||
rageshakePresenter,
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
|
|
@ -88,11 +101,13 @@ class DeveloperSettingsPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `present - clear cache`() = runTest {
|
||||
val rageshakePresenter = DefaultRageshakePreferencesPresenter(FakeRageShake(), FakeRageshakeDataStore())
|
||||
val clearCacheUseCase = FakeClearCacheUseCase()
|
||||
val presenter = DeveloperSettingsPresenter(
|
||||
FakeFeatureFlagService(),
|
||||
FakeComputeCacheSizeUseCase(),
|
||||
clearCacheUseCase,
|
||||
rageshakePresenter,
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
|
|
|
|||
|
|
@ -20,42 +20,45 @@ import app.cash.molecule.RecompositionClock
|
|||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.analytics.impl.preferences.DefaultAnalyticsPreferencesPresenter
|
||||
import io.element.android.features.analytics.test.A_BUILD_META
|
||||
import io.element.android.features.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.features.logout.impl.DefaultLogoutPreferencePresenter
|
||||
import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePreferencesPresenter
|
||||
import io.element.android.features.rageshake.test.rageshake.FakeRageShake
|
||||
import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.core.meta.BuildType
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class PreferencesRootPresenterTest {
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val logoutPresenter = DefaultLogoutPreferencePresenter(FakeMatrixClient())
|
||||
val rageshakePresenter = DefaultRageshakePreferencesPresenter(FakeRageShake(), FakeRageshakeDataStore())
|
||||
val analyticsPresenter = DefaultAnalyticsPreferencesPresenter(FakeAnalyticsService(), A_BUILD_META)
|
||||
val matrixClient = FakeMatrixClient()
|
||||
val logoutPresenter = DefaultLogoutPreferencePresenter(matrixClient)
|
||||
val presenter = PreferencesRootPresenter(
|
||||
logoutPresenter,
|
||||
rageshakePresenter,
|
||||
analyticsPresenter,
|
||||
A_BUILD_META.buildType
|
||||
matrixClient,
|
||||
FakeSessionVerificationService(),
|
||||
BuildType.DEBUG,
|
||||
FakeVersionFormatter()
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.logoutState.logoutAction).isEqualTo(Async.Uninitialized)
|
||||
assertThat(initialState.analyticsState.isEnabled).isFalse()
|
||||
assertThat(initialState.rageshakeState.isEnabled).isTrue()
|
||||
assertThat(initialState.rageshakeState.isSupported).isTrue()
|
||||
assertThat(initialState.rageshakeState.sensitivity).isEqualTo(1.0f)
|
||||
assertThat(initialState.myUser).isEqualTo(Async.Uninitialized)
|
||||
assertThat(initialState.showDeveloperSettings).isEqualTo(true)
|
||||
assertThat(initialState.myUser).isNull()
|
||||
assertThat(initialState.version).isEqualTo("A Version")
|
||||
val loadedState = awaitItem()
|
||||
assertThat(loadedState.logoutState.logoutAction).isEqualTo(Async.Uninitialized)
|
||||
assertThat(loadedState.myUser).isEqualTo(
|
||||
MatrixUser(
|
||||
userId = matrixClient.sessionId,
|
||||
displayName = A_USER_NAME,
|
||||
avatarUrl = AN_AVATAR_URL
|
||||
)
|
||||
)
|
||||
assertThat(loadedState.showDeveloperSettings).isEqualTo(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
package io.element.android.features.rageshake.api.preferences
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.BugReport
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -36,7 +34,6 @@ import io.element.android.libraries.ui.strings.CommonStrings
|
|||
fun RageshakePreferencesView(
|
||||
state: RageshakePreferencesState,
|
||||
modifier: Modifier = Modifier,
|
||||
onOpenRageshake: () -> Unit = {},
|
||||
) {
|
||||
fun onSensitivityChanged(sensitivity: Float) {
|
||||
state.eventSink(RageshakePreferencesEvents.SetSensitivity(sensitivity = sensitivity))
|
||||
|
|
@ -47,13 +44,6 @@ fun RageshakePreferencesView(
|
|||
}
|
||||
|
||||
Column(modifier = modifier) {
|
||||
PreferenceCategory(title = stringResource(id = CommonStrings.action_report_bug)) {
|
||||
PreferenceText(
|
||||
title = stringResource(id = CommonStrings.action_report_bug),
|
||||
icon = Icons.Default.BugReport,
|
||||
onClick = onOpenRageshake
|
||||
)
|
||||
}
|
||||
PreferenceCategory(title = stringResource(id = CommonStrings.settings_rageshake)) {
|
||||
if (state.isSupported) {
|
||||
PreferenceSwitch(
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ sealed interface BugReportEvents {
|
|||
|
||||
data class SetDescription(val description: String) : BugReportEvents
|
||||
data class SetSendLog(val sendLog: Boolean) : BugReportEvents
|
||||
data class SetSendCrashLog(val sendCrashlog: Boolean) : BugReportEvents
|
||||
data class SetCanContact(val canContact: Boolean) : BugReportEvents
|
||||
data class SetSendScreenshot(val sendScreenshot: Boolean) : BugReportEvents
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@
|
|||
|
||||
package io.element.android.features.rageshake.impl.bugreport
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
|
|
@ -26,8 +28,9 @@ import dagger.assisted.Assisted
|
|||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
|
||||
import io.element.android.features.rageshake.impl.bugreport.BugReportPresenter
|
||||
import io.element.android.libraries.androidutils.system.toast
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@ContributesNode(AppScope::class)
|
||||
class BugReportNode @AssistedInject constructor(
|
||||
|
|
@ -39,10 +42,15 @@ class BugReportNode @AssistedInject constructor(
|
|||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
val activity = LocalContext.current as? Activity
|
||||
BugReportView(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
onDone = this::onDone
|
||||
onBackPressed = { navigateUp() },
|
||||
onDone = {
|
||||
activity?.toast(CommonStrings.common_report_submitted)
|
||||
onDone()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -100,9 +100,6 @@ class BugReportPresenter @Inject constructor(
|
|||
is BugReportEvents.SetCanContact -> updateFormState(formState) {
|
||||
copy(canContact = event.canContact)
|
||||
}
|
||||
is BugReportEvents.SetSendCrashLog -> updateFormState(formState) {
|
||||
copy(sendCrashLogs = event.sendCrashlog)
|
||||
}
|
||||
is BugReportEvents.SetSendLog -> updateFormState(formState) {
|
||||
copy(sendLogs = event.sendLog)
|
||||
}
|
||||
|
|
@ -138,7 +135,7 @@ class BugReportPresenter @Inject constructor(
|
|||
bugReporter.sendBugReport(
|
||||
reportType = ReportType.BUG_REPORT,
|
||||
withDevicesLogs = formState.sendLogs,
|
||||
withCrashLogs = hasCrashLogs && formState.sendCrashLogs,
|
||||
withCrashLogs = hasCrashLogs && formState.sendLogs,
|
||||
withKeyRequestHistory = false,
|
||||
withScreenshot = formState.sendScreenshot,
|
||||
theBugDescription = formState.description,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ data class BugReportState(
|
|||
data class BugReportFormState(
|
||||
val description: String,
|
||||
val sendLogs: Boolean,
|
||||
val sendCrashLogs: Boolean,
|
||||
val canContact: Boolean,
|
||||
val sendScreenshot: Boolean
|
||||
) : Parcelable {
|
||||
|
|
@ -44,7 +43,6 @@ data class BugReportFormState(
|
|||
val Default = BugReportFormState(
|
||||
description = "",
|
||||
sendLogs = true,
|
||||
sendCrashLogs = true,
|
||||
canContact = false,
|
||||
sendScreenshot = false
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,16 +17,11 @@
|
|||
package io.element.android.features.rageshake.impl.bugreport
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -35,21 +30,20 @@ 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.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import io.element.android.features.rageshake.impl.R
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.designsystem.components.LabelledCheckbox
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||
import io.element.android.libraries.designsystem.components.form.textFieldState
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceRow
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
|
||||
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.preview.debugPlaceholderBackground
|
||||
|
|
@ -63,8 +57,9 @@ import io.element.android.libraries.ui.strings.CommonStrings
|
|||
@Composable
|
||||
fun BugReportView(
|
||||
state: BugReportState,
|
||||
onDone: () -> Unit,
|
||||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onDone: () -> Unit = { },
|
||||
) {
|
||||
LogCompositions(tag = "Rageshake", msg = "Root")
|
||||
val eventSink = state.eventSink
|
||||
|
|
@ -75,56 +70,27 @@ fun BugReportView(
|
|||
}
|
||||
return
|
||||
}
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding()
|
||||
.imePadding()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(state = rememberScrollState())
|
||||
.padding(horizontal = 16.dp),
|
||||
|
||||
Box(modifier = modifier) {
|
||||
PreferenceView(
|
||||
title = stringResource(id = CommonStrings.common_report_a_bug),
|
||||
onBackPressed = onBackPressed
|
||||
) {
|
||||
val isError = state.sending is Async.Failure
|
||||
val isFormEnabled = state.sending !is Async.Loading
|
||||
// Title
|
||||
Text(
|
||||
text = stringResource(id = CommonStrings.action_report_bug),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp),
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 24.sp,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
// Form
|
||||
Text(
|
||||
text = stringResource(id = R.string.screen_bug_report_editor_description),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp),
|
||||
fontSize = 16.sp,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
var descriptionFieldState by textFieldState(
|
||||
stateValue = state.formState.description
|
||||
)
|
||||
Column(
|
||||
// modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
PreferenceRow {
|
||||
OutlinedTextField(
|
||||
value = descriptionFieldState,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = isFormEnabled,
|
||||
label = {
|
||||
Text(text = stringResource(id = R.string.screen_bug_report_editor_placeholder))
|
||||
},
|
||||
supportingText = {
|
||||
Text(text = stringResource(id = R.string.screen_bug_report_editor_supporting))
|
||||
Text(text = stringResource(id = R.string.screen_bug_report_editor_description))
|
||||
},
|
||||
onValueChange = {
|
||||
descriptionFieldState = it
|
||||
|
|
@ -134,35 +100,31 @@ fun BugReportView(
|
|||
keyboardType = KeyboardType.Text,
|
||||
imeAction = ImeAction.Next
|
||||
),
|
||||
minLines = 3,
|
||||
// TODO Error text too short
|
||||
)
|
||||
}
|
||||
LabelledCheckbox(
|
||||
checked = state.formState.sendLogs,
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
PreferenceSwitch(
|
||||
isChecked = state.formState.sendLogs,
|
||||
onCheckedChange = { eventSink(BugReportEvents.SetSendLog(it)) },
|
||||
enabled = isFormEnabled,
|
||||
text = stringResource(id = R.string.screen_bug_report_include_logs)
|
||||
title = stringResource(id = R.string.screen_bug_report_include_logs),
|
||||
subtitle = stringResource(id = R.string.screen_bug_report_logs_description),
|
||||
)
|
||||
if (state.hasCrashLogs) {
|
||||
LabelledCheckbox(
|
||||
checked = state.formState.sendCrashLogs,
|
||||
onCheckedChange = { eventSink(BugReportEvents.SetSendCrashLog(it)) },
|
||||
enabled = isFormEnabled,
|
||||
text = stringResource(id = R.string.screen_bug_report_include_crash_logs)
|
||||
)
|
||||
}
|
||||
LabelledCheckbox(
|
||||
checked = state.formState.canContact,
|
||||
PreferenceSwitch(
|
||||
isChecked = state.formState.canContact,
|
||||
onCheckedChange = { eventSink(BugReportEvents.SetCanContact(it)) },
|
||||
enabled = isFormEnabled,
|
||||
text = stringResource(id = R.string.screen_bug_report_contact_me)
|
||||
title = stringResource(id = R.string.screen_bug_report_contact_me_title),
|
||||
subtitle = stringResource(id = R.string.screen_bug_report_contact_me),
|
||||
)
|
||||
if (state.screenshotUri != null) {
|
||||
LabelledCheckbox(
|
||||
checked = state.formState.sendScreenshot,
|
||||
PreferenceSwitch(
|
||||
isChecked = state.formState.sendScreenshot,
|
||||
onCheckedChange = { eventSink(BugReportEvents.SetSendScreenshot(it)) },
|
||||
enabled = isFormEnabled,
|
||||
text = stringResource(id = R.string.screen_bug_report_include_screenshot)
|
||||
title = stringResource(id = R.string.screen_bug_report_include_screenshot)
|
||||
)
|
||||
if (state.formState.sendScreenshot) {
|
||||
Box(
|
||||
|
|
@ -183,16 +145,19 @@ fun BugReportView(
|
|||
}
|
||||
}
|
||||
// Submit
|
||||
Button(
|
||||
onClick = { eventSink(BugReportEvents.SendBugReport) },
|
||||
enabled = state.submitEnabled,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 32.dp)
|
||||
) {
|
||||
Text(text = stringResource(id = CommonStrings.action_send))
|
||||
PreferenceRow {
|
||||
Button(
|
||||
onClick = { eventSink(BugReportEvents.SendBugReport) },
|
||||
enabled = state.submitEnabled,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 24.dp, bottom = 16.dp)
|
||||
) {
|
||||
Text(text = stringResource(id = CommonStrings.action_send))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when (state.sending) {
|
||||
is Async.Loading -> {
|
||||
// Indeterminate indicator, to avoid the freeze effect if the connection takes time to initialize.
|
||||
|
|
@ -219,5 +184,9 @@ fun BugReportViewDarkPreview(@PreviewParameter(BugReportStateProvider::class) st
|
|||
|
||||
@Composable
|
||||
private fun ContentToPreview(state: BugReportState) {
|
||||
BugReportView(state = state)
|
||||
BugReportView(
|
||||
state = state,
|
||||
onDone = {},
|
||||
onBackPressed = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class DefaultBugReportEntryPoint @Inject constructor() : BugReportEntryPoint {
|
|||
}
|
||||
|
||||
override fun build(): Node {
|
||||
return parentNode.createNode<BugReportNode>(buildContext)
|
||||
return parentNode.createNode<BugReportNode>(buildContext, plugins)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import androidx.datastore.preferences.core.floatPreferencesKey
|
|||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.features.rageshake.api.rageshake.RageshakeDataStore
|
||||
import io.element.android.libraries.core.bool.orTrue
|
||||
import io.element.android.libraries.core.bool.orFalse
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -45,7 +45,7 @@ class PreferencesRageshakeDataStore @Inject constructor(
|
|||
|
||||
override fun isEnabled(): Flow<Boolean> {
|
||||
return store.data.map { prefs ->
|
||||
prefs[enabledKey].orTrue()
|
||||
prefs[enabledKey].orFalse()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -92,26 +92,6 @@ class BugReportPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - send crash logs`() = runTest {
|
||||
val presenter = BugReportPresenter(
|
||||
FakeBugReporter(),
|
||||
FakeCrashDataStore(),
|
||||
FakeScreenshotHolder(),
|
||||
this,
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
// Since this is true by default, start by disabling
|
||||
initialState.eventSink.invoke(BugReportEvents.SetSendCrashLog(false))
|
||||
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendCrashLogs = false))
|
||||
initialState.eventSink.invoke(BugReportEvents.SetSendCrashLog(true))
|
||||
assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(sendCrashLogs = true))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - send logs`() = runTest {
|
||||
val presenter = BugReportPresenter(
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
const val A_SENSITIVITY = 1f
|
||||
|
||||
class FakeRageshakeDataStore(
|
||||
isEnabled: Boolean = true,
|
||||
isEnabled: Boolean = false,
|
||||
sensitivity: Float = A_SENSITIVITY,
|
||||
) : RageshakeDataStore {
|
||||
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ fun RoomDetailsView(
|
|||
RoomMemberMainActionsSection(onShareUser = ::onShareMember)
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(26.dp))
|
||||
Spacer(Modifier.height(18.dp))
|
||||
|
||||
if (state.roomTopic !is RoomTopicState.Hidden) {
|
||||
TopicSection(
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomPresenter
|
||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
|
||||
import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryPlaceholders
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
|
|
@ -43,9 +43,9 @@ import io.element.android.libraries.designsystem.utils.collectSnackbarMessageAsS
|
|||
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.RoomSummary
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.api.user.getCurrentUser
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -162,13 +162,7 @@ class RoomListPresenter @Inject constructor(
|
|||
}
|
||||
|
||||
private fun CoroutineScope.initialLoad(matrixUser: MutableState<MatrixUser?>) = launch {
|
||||
val userAvatarUrl = client.loadUserAvatarURLString().getOrNull()
|
||||
val userDisplayName = client.loadUserDisplayName().getOrNull()
|
||||
matrixUser.value = MatrixUser(
|
||||
userId = UserId(client.sessionId.value),
|
||||
displayName = userDisplayName,
|
||||
avatarUrl = userAvatarUrl,
|
||||
)
|
||||
matrixUser.value = client.getCurrentUser()
|
||||
}
|
||||
|
||||
private fun updateVisibleRange(range: IntRange) {
|
||||
|
|
|
|||
|
|
@ -50,8 +50,9 @@ class RoomListPresenterTests {
|
|||
|
||||
@Test
|
||||
fun `present - should start with no user and then load user with success`() = runTest {
|
||||
val matrixClient = FakeMatrixClient()
|
||||
val presenter = RoomListPresenter(
|
||||
FakeMatrixClient(),
|
||||
matrixClient,
|
||||
createDateFormatter(),
|
||||
FakeRoomLastMessageFormatter(),
|
||||
FakeSessionVerificationService(),
|
||||
|
|
@ -75,11 +76,12 @@ class RoomListPresenterTests {
|
|||
|
||||
@Test
|
||||
fun `present - should start with no user and then load user with error`() = runTest {
|
||||
val matrixClient = FakeMatrixClient(
|
||||
userDisplayName = Result.failure(AN_EXCEPTION),
|
||||
userAvatarURLString = Result.failure(AN_EXCEPTION),
|
||||
)
|
||||
val presenter = RoomListPresenter(
|
||||
FakeMatrixClient(
|
||||
userDisplayName = Result.failure(AN_EXCEPTION),
|
||||
userAvatarURLString = Result.failure(AN_EXCEPTION),
|
||||
),
|
||||
matrixClient,
|
||||
createDateFormatter(),
|
||||
FakeRoomLastMessageFormatter(),
|
||||
FakeSessionVerificationService(),
|
||||
|
|
@ -100,8 +102,9 @@ class RoomListPresenterTests {
|
|||
|
||||
@Test
|
||||
fun `present - should filter room with success`() = runTest {
|
||||
val matrixClient = FakeMatrixClient()
|
||||
val presenter = RoomListPresenter(
|
||||
FakeMatrixClient(),
|
||||
matrixClient,
|
||||
createDateFormatter(),
|
||||
FakeRoomLastMessageFormatter(),
|
||||
FakeSessionVerificationService(),
|
||||
|
|
@ -127,10 +130,11 @@ class RoomListPresenterTests {
|
|||
@Test
|
||||
fun `present - load 1 room with success`() = runTest {
|
||||
val roomSummaryDataSource = FakeRoomSummaryDataSource()
|
||||
val matrixClient = FakeMatrixClient(
|
||||
roomSummaryDataSource = roomSummaryDataSource
|
||||
)
|
||||
val presenter = RoomListPresenter(
|
||||
FakeMatrixClient(
|
||||
roomSummaryDataSource = roomSummaryDataSource
|
||||
),
|
||||
matrixClient,
|
||||
createDateFormatter(),
|
||||
FakeRoomLastMessageFormatter(),
|
||||
FakeSessionVerificationService(),
|
||||
|
|
@ -159,10 +163,11 @@ class RoomListPresenterTests {
|
|||
@Test
|
||||
fun `present - load 1 room with success and filter rooms`() = runTest {
|
||||
val roomSummaryDataSource = FakeRoomSummaryDataSource()
|
||||
val matrixClient = FakeMatrixClient(
|
||||
roomSummaryDataSource = roomSummaryDataSource
|
||||
)
|
||||
val presenter = RoomListPresenter(
|
||||
FakeMatrixClient(
|
||||
roomSummaryDataSource = roomSummaryDataSource
|
||||
),
|
||||
matrixClient,
|
||||
createDateFormatter(),
|
||||
FakeRoomLastMessageFormatter(),
|
||||
FakeSessionVerificationService(),
|
||||
|
|
@ -197,10 +202,11 @@ class RoomListPresenterTests {
|
|||
@Test
|
||||
fun `present - update visible range`() = runTest {
|
||||
val roomSummaryDataSource = FakeRoomSummaryDataSource()
|
||||
val matrixClient = FakeMatrixClient(
|
||||
roomSummaryDataSource = roomSummaryDataSource
|
||||
)
|
||||
val presenter = RoomListPresenter(
|
||||
FakeMatrixClient(
|
||||
roomSummaryDataSource = roomSummaryDataSource
|
||||
),
|
||||
matrixClient,
|
||||
createDateFormatter(),
|
||||
FakeRoomLastMessageFormatter(),
|
||||
FakeSessionVerificationService(),
|
||||
|
|
@ -245,10 +251,11 @@ class RoomListPresenterTests {
|
|||
@Test
|
||||
fun `present - handle DismissRequestVerificationPrompt`() = runTest {
|
||||
val roomSummaryDataSource = FakeRoomSummaryDataSource()
|
||||
val matrixClient = FakeMatrixClient(
|
||||
roomSummaryDataSource = roomSummaryDataSource
|
||||
)
|
||||
val presenter = RoomListPresenter(
|
||||
FakeMatrixClient(
|
||||
roomSummaryDataSource = roomSummaryDataSource
|
||||
),
|
||||
matrixClient,
|
||||
createDateFormatter(),
|
||||
FakeRoomLastMessageFormatter(),
|
||||
FakeSessionVerificationService().apply {
|
||||
|
|
@ -274,8 +281,9 @@ class RoomListPresenterTests {
|
|||
@Test
|
||||
fun `present - sets invite state`() = runTest {
|
||||
val inviteStateFlow = MutableStateFlow(InvitesState.NoInvites)
|
||||
val matrixClient = FakeMatrixClient()
|
||||
val presenter = RoomListPresenter(
|
||||
FakeMatrixClient(),
|
||||
matrixClient,
|
||||
createDateFormatter(),
|
||||
FakeRoomLastMessageFormatter(),
|
||||
FakeSessionVerificationService(),
|
||||
|
|
@ -304,8 +312,9 @@ class RoomListPresenterTests {
|
|||
|
||||
@Test
|
||||
fun `present - show context menu`() = runTest {
|
||||
val matrixClient = FakeMatrixClient()
|
||||
val presenter = RoomListPresenter(
|
||||
FakeMatrixClient(),
|
||||
matrixClient,
|
||||
createDateFormatter(),
|
||||
FakeRoomLastMessageFormatter(),
|
||||
FakeSessionVerificationService(),
|
||||
|
|
@ -331,8 +340,9 @@ class RoomListPresenterTests {
|
|||
|
||||
@Test
|
||||
fun `present - hide context menu`() = runTest {
|
||||
val matrixClient = FakeMatrixClient()
|
||||
val presenter = RoomListPresenter(
|
||||
FakeMatrixClient(),
|
||||
matrixClient,
|
||||
createDateFormatter(),
|
||||
FakeRoomLastMessageFormatter(),
|
||||
FakeSessionVerificationService(),
|
||||
|
|
@ -363,8 +373,9 @@ class RoomListPresenterTests {
|
|||
@Test
|
||||
fun `present - leave room calls into leave room presenter`() = runTest {
|
||||
val leaveRoomPresenter = LeaveRoomPresenterFake()
|
||||
val matrixClient = FakeMatrixClient()
|
||||
val presenter = RoomListPresenter(
|
||||
FakeMatrixClient(),
|
||||
matrixClient,
|
||||
createDateFormatter(),
|
||||
FakeRoomLastMessageFormatter(),
|
||||
FakeSessionVerificationService(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue