First Commit
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
plugins {
|
||||
id("io.element.android-compose-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.libraries.troubleshoot.api"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(libs.androidx.corektx)
|
||||
implementation(libs.coroutines.core)
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.api
|
||||
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||
|
||||
interface NotificationTroubleShootEntryPoint : FeatureEntryPoint {
|
||||
fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
callback: Callback,
|
||||
): Node
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun onDone()
|
||||
fun navigateToBlockedUsers()
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.api
|
||||
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
interface PushHistoryEntryPoint : FeatureEntryPoint {
|
||||
fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
callback: Callback,
|
||||
): Node
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun onDone()
|
||||
fun navigateToEvent(roomId: RoomId, eventId: EventId)
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.api.test
|
||||
|
||||
interface NotificationTroubleshootNavigator {
|
||||
fun navigateToBlockedUsers()
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.api.test
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface NotificationTroubleshootTest {
|
||||
val order: Int
|
||||
val state: StateFlow<NotificationTroubleshootTestState>
|
||||
fun isRelevant(data: TestFilterData): Boolean = true
|
||||
suspend fun run(coroutineScope: CoroutineScope)
|
||||
suspend fun reset()
|
||||
suspend fun quickFix(
|
||||
coroutineScope: CoroutineScope,
|
||||
navigator: NotificationTroubleshootNavigator,
|
||||
) {
|
||||
error("Quick fix not implemented, you need to override this method in your test")
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.api.test
|
||||
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
/**
|
||||
* A NotificationTroubleshootTest delegate, with common pattern for running and resetting.
|
||||
*/
|
||||
class NotificationTroubleshootTestDelegate(
|
||||
private val defaultName: String,
|
||||
private val defaultDescription: String,
|
||||
private val visibleWhenIdle: Boolean = true,
|
||||
private val hasQuickFix: Boolean = false,
|
||||
private val fakeDelay: Long = 0L,
|
||||
) {
|
||||
private val _state: MutableStateFlow<NotificationTroubleshootTestState> = MutableStateFlow(
|
||||
NotificationTroubleshootTestState(
|
||||
name = defaultName,
|
||||
description = defaultDescription,
|
||||
status = NotificationTroubleshootTestState.Status.Idle(visibleWhenIdle),
|
||||
)
|
||||
)
|
||||
|
||||
val state: StateFlow<NotificationTroubleshootTestState> = _state.asStateFlow()
|
||||
|
||||
suspend fun updateState(
|
||||
status: NotificationTroubleshootTestState.Status,
|
||||
name: String = defaultName,
|
||||
description: String = defaultDescription,
|
||||
) {
|
||||
_state.emit(
|
||||
NotificationTroubleshootTestState(
|
||||
name = name,
|
||||
description = description,
|
||||
status = status,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun reset() {
|
||||
updateState(NotificationTroubleshootTestState.Status.Idle(visibleWhenIdle))
|
||||
}
|
||||
|
||||
suspend fun start() {
|
||||
updateState(NotificationTroubleshootTestState.Status.InProgress)
|
||||
delay(fakeDelay)
|
||||
}
|
||||
|
||||
suspend fun done(isSuccess: Boolean = true) {
|
||||
updateState(
|
||||
if (isSuccess) {
|
||||
NotificationTroubleshootTestState.Status.Success
|
||||
} else {
|
||||
NotificationTroubleshootTestState.Status.Failure(hasQuickFix = hasQuickFix)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SHORT_DELAY = 300L
|
||||
const val LONG_DELAY = 500L
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.api.test
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
||||
data class NotificationTroubleshootTestState(
|
||||
val name: String,
|
||||
val description: String,
|
||||
val status: Status,
|
||||
) {
|
||||
@Immutable
|
||||
sealed interface Status {
|
||||
data class Idle(val visible: Boolean) : Status
|
||||
data object InProgress : Status
|
||||
data object WaitingForUser : Status
|
||||
data object Success : Status
|
||||
data class Failure(
|
||||
val hasQuickFix: Boolean = false,
|
||||
val isCritical: Boolean = true,
|
||||
val quickFixButtonString: String? = null,
|
||||
) : Status
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.api.test
|
||||
|
||||
data class TestFilterData(
|
||||
val currentPushProviderName: String?,
|
||||
)
|
||||
@@ -0,0 +1,42 @@
|
||||
import extension.setupDependencyInjection
|
||||
import extension.testCommonDependencies
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023, 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
plugins {
|
||||
id("io.element.android-compose-library")
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.libraries.troubleshoot.impl"
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
isIncludeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupDependencyInjection()
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.di)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
api(projects.libraries.troubleshoot.api)
|
||||
api(projects.libraries.push.api)
|
||||
implementation(projects.services.analytics.api)
|
||||
|
||||
testCommonDependencies(libs, true)
|
||||
testImplementation(projects.services.analytics.test)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.push.test)
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultNotificationTroubleShootEntryPoint : NotificationTroubleShootEntryPoint {
|
||||
override fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
callback: NotificationTroubleShootEntryPoint.Callback,
|
||||
): Node {
|
||||
return parentNode.createNode<TroubleshootNotificationsNode>(buildContext, listOf(callback))
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
sealed interface TroubleshootNotificationsEvents {
|
||||
data object StartTests : TroubleshootNotificationsEvents
|
||||
data object RetryFailedTests : TroubleshootNotificationsEvents
|
||||
data class QuickFix(val testIndex: Int) : TroubleshootNotificationsEvents
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
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 dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import im.vector.app.features.analytics.plan.MobileScreen
|
||||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.libraries.architecture.callback
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.services.analytics.api.ScreenTracker
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
@AssistedInject
|
||||
class TroubleshootNotificationsNode(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val screenTracker: ScreenTracker,
|
||||
factory: TroubleshootNotificationsPresenter.Factory,
|
||||
) : Node(buildContext, plugins = plugins),
|
||||
NotificationTroubleshootNavigator {
|
||||
private val callback: NotificationTroubleShootEntryPoint.Callback = callback()
|
||||
private val presenter = factory.create(
|
||||
navigator = this,
|
||||
)
|
||||
|
||||
override fun navigateToBlockedUsers() {
|
||||
callback.navigateToBlockedUsers()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
screenTracker.TrackScreen(MobileScreen.ScreenName.NotificationTroubleshoot)
|
||||
val state = presenter.present()
|
||||
TroubleshootNotificationsView(
|
||||
state = state,
|
||||
onBackClick = callback::onDone,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedFactory
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@AssistedInject
|
||||
class TroubleshootNotificationsPresenter(
|
||||
@Assisted private val navigator: NotificationTroubleshootNavigator,
|
||||
private val troubleshootTestSuite: TroubleshootTestSuite,
|
||||
) : Presenter<TroubleshootNotificationsState> {
|
||||
@AssistedFactory
|
||||
fun interface Factory {
|
||||
fun create(navigator: NotificationTroubleshootNavigator): TroubleshootNotificationsPresenter
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun present(): TroubleshootNotificationsState {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
LaunchedEffect(Unit) {
|
||||
troubleshootTestSuite.start(this)
|
||||
}
|
||||
|
||||
val testSuiteState by troubleshootTestSuite.state.collectAsState()
|
||||
fun handleEvent(event: TroubleshootNotificationsEvents) {
|
||||
when (event) {
|
||||
TroubleshootNotificationsEvents.StartTests -> coroutineScope.launch {
|
||||
troubleshootTestSuite.runTestSuite(this)
|
||||
}
|
||||
is TroubleshootNotificationsEvents.QuickFix -> coroutineScope.launch {
|
||||
troubleshootTestSuite.quickFix(
|
||||
testIndex = event.testIndex,
|
||||
coroutineScope = this,
|
||||
navigator = navigator,
|
||||
)
|
||||
}
|
||||
TroubleshootNotificationsEvents.RetryFailedTests -> coroutineScope.launch {
|
||||
troubleshootTestSuite.retryFailedTest(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TroubleshootNotificationsState(
|
||||
testSuiteState = testSuiteState,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
data class TroubleshootNotificationsState(
|
||||
val testSuiteState: TroubleshootTestSuiteState,
|
||||
val eventSink: (TroubleshootNotificationsEvents) -> Unit,
|
||||
) {
|
||||
val hasFailedTests: Boolean = testSuiteState.mainState.isFailure()
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
open class TroubleshootNotificationsStateProvider : PreviewParameterProvider<TroubleshootNotificationsState> {
|
||||
override val values: Sequence<TroubleshootNotificationsState>
|
||||
get() = sequenceOf(
|
||||
aTroubleshootNotificationsState(
|
||||
listOf(
|
||||
aTroubleshootTestStateIdle(),
|
||||
aTroubleshootTestStateIdle(),
|
||||
aTroubleshootTestStateIdle(visible = false),
|
||||
)
|
||||
),
|
||||
aTroubleshootNotificationsState(
|
||||
listOf(
|
||||
aTroubleshootTestStateInProgress(),
|
||||
aTroubleshootTestStateIdle(),
|
||||
aTroubleshootTestStateIdle(),
|
||||
)
|
||||
),
|
||||
aTroubleshootNotificationsState(
|
||||
listOf(
|
||||
aTroubleshootTestStateSuccess(),
|
||||
aTroubleshootTestStateFailure(
|
||||
isCritical = false,
|
||||
hasQuickFix = true,
|
||||
quickFixButtonString = "Custom quick fix",
|
||||
),
|
||||
aTroubleshootTestStateInProgress(),
|
||||
)
|
||||
),
|
||||
aTroubleshootNotificationsState(
|
||||
listOf(
|
||||
aTroubleshootTestStateSuccess(),
|
||||
aTroubleshootTestStateWaitingForUser(),
|
||||
aTroubleshootTestStateIdle(),
|
||||
)
|
||||
),
|
||||
aTroubleshootNotificationsState(
|
||||
listOf(
|
||||
aTroubleshootTestStateSuccess(),
|
||||
aTroubleshootTestStateFailure(hasQuickFix = true),
|
||||
aTroubleshootTestStateInProgress(),
|
||||
)
|
||||
),
|
||||
aTroubleshootNotificationsState(
|
||||
listOf(
|
||||
aTroubleshootTestStateSuccess(),
|
||||
aTroubleshootTestStateFailure(hasQuickFix = true),
|
||||
aTroubleshootTestStateFailure(hasQuickFix = false),
|
||||
)
|
||||
),
|
||||
aTroubleshootNotificationsState(
|
||||
listOf(
|
||||
aTroubleshootTestStateSuccess(),
|
||||
aTroubleshootTestStateSuccess(),
|
||||
aTroubleshootTestStateSuccess(),
|
||||
)
|
||||
),
|
||||
aTroubleshootNotificationsState(
|
||||
listOf(
|
||||
aTroubleshootTestStateWaitingForUser(),
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun aTroubleshootNotificationsState(
|
||||
tests: List<NotificationTroubleshootTestState> = emptyList(),
|
||||
eventSink: (TroubleshootNotificationsEvents) -> Unit = {},
|
||||
) = TroubleshootNotificationsState(
|
||||
eventSink = eventSink,
|
||||
testSuiteState = TroubleshootTestSuiteState(
|
||||
mainState = tests.computeMainState(),
|
||||
tests = tests.toImmutableList(),
|
||||
),
|
||||
)
|
||||
|
||||
fun aTroubleshootTestState(
|
||||
status: NotificationTroubleshootTestState.Status,
|
||||
name: String = "Test",
|
||||
description: String = "Description",
|
||||
): NotificationTroubleshootTestState {
|
||||
return NotificationTroubleshootTestState(
|
||||
name = name,
|
||||
description = description,
|
||||
status = status,
|
||||
)
|
||||
}
|
||||
|
||||
fun aTroubleshootTestStateIdle(visible: Boolean = true) =
|
||||
aTroubleshootTestState(status = NotificationTroubleshootTestState.Status.Idle(visible = visible))
|
||||
|
||||
fun aTroubleshootTestStateInProgress() =
|
||||
aTroubleshootTestState(status = NotificationTroubleshootTestState.Status.InProgress)
|
||||
|
||||
fun aTroubleshootTestStateWaitingForUser() =
|
||||
aTroubleshootTestState(status = NotificationTroubleshootTestState.Status.WaitingForUser)
|
||||
|
||||
fun aTroubleshootTestStateSuccess() =
|
||||
aTroubleshootTestState(status = NotificationTroubleshootTestState.Status.Success)
|
||||
|
||||
fun aTroubleshootTestStateFailure(
|
||||
hasQuickFix: Boolean = false,
|
||||
isCritical: Boolean = true,
|
||||
quickFixButtonString: String? = null,
|
||||
) = aTroubleshootTestState(
|
||||
status = NotificationTroubleshootTestState.Status.Failure(
|
||||
hasQuickFix = hasQuickFix,
|
||||
isCritical = isCritical,
|
||||
quickFixButtonString = quickFixButtonString,
|
||||
)
|
||||
)
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.progressSemantics
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferencePage
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.ListItem
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState.Status
|
||||
|
||||
@Composable
|
||||
fun TroubleshootNotificationsView(
|
||||
state: TroubleshootNotificationsState,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
OnLifecycleEvent { _, event ->
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_RESUME -> {
|
||||
if (state.hasFailedTests) {
|
||||
state.eventSink(TroubleshootNotificationsEvents.RetryFailedTests)
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
PreferencePage(
|
||||
modifier = modifier,
|
||||
onBackClick = onBackClick,
|
||||
title = stringResource(id = R.string.troubleshoot_notifications_screen_title),
|
||||
) {
|
||||
TroubleshootNotificationsContent(state)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.TroubleshootTestView(
|
||||
testState: NotificationTroubleshootTestState,
|
||||
onQuickFixClick: () -> Unit,
|
||||
) {
|
||||
val status = testState.status
|
||||
if ((status as? Status.Idle)?.visible == false) return
|
||||
ListItem(
|
||||
headlineContent = { Text(text = testState.name) },
|
||||
supportingContent = { Text(text = testState.description) },
|
||||
trailingContent = when (status) {
|
||||
is Status.Idle -> null
|
||||
Status.InProgress -> ListItemContent.Custom {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.progressSemantics()
|
||||
.size(20.dp),
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
}
|
||||
Status.WaitingForUser -> ListItemContent.Custom {
|
||||
Icon(
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
imageVector = CompoundIcons.Info(),
|
||||
tint = ElementTheme.colors.iconAccentTertiary
|
||||
)
|
||||
}
|
||||
Status.Success -> ListItemContent.Custom {
|
||||
Icon(
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
imageVector = CompoundIcons.Check(),
|
||||
tint = ElementTheme.colors.iconAccentTertiary
|
||||
)
|
||||
}
|
||||
is Status.Failure -> ListItemContent.Custom {
|
||||
Icon(
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
imageVector = if (status.isCritical) CompoundIcons.ErrorSolid() else CompoundIcons.Warning(),
|
||||
tint = ElementTheme.colors.iconCriticalPrimary,
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
if (status is Status.Failure && status.hasQuickFix) {
|
||||
ListItem(
|
||||
headlineContent = { },
|
||||
trailingContent = ListItemContent.Custom {
|
||||
Button(
|
||||
text = status.quickFixButtonString ?: stringResource(id = R.string.troubleshoot_notifications_screen_quick_fix_action),
|
||||
onClick = onQuickFixClick,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.TroubleshootNotificationsContent(state: TroubleshootNotificationsState) {
|
||||
when (state.testSuiteState.mainState) {
|
||||
AsyncAction.Loading,
|
||||
is AsyncAction.Confirming,
|
||||
is AsyncAction.Success,
|
||||
is AsyncAction.Failure -> {
|
||||
TestSuiteView(
|
||||
testSuiteState = state.testSuiteState,
|
||||
onQuickFixClick = {
|
||||
state.eventSink(TroubleshootNotificationsEvents.QuickFix(it))
|
||||
}
|
||||
)
|
||||
}
|
||||
AsyncAction.Uninitialized -> Unit
|
||||
}
|
||||
when (state.testSuiteState.mainState) {
|
||||
AsyncAction.Uninitialized -> {
|
||||
ListItem(headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.troubleshoot_notifications_screen_notice)
|
||||
)
|
||||
})
|
||||
RunTestButton(state = state)
|
||||
}
|
||||
AsyncAction.Loading -> Unit
|
||||
is AsyncAction.Failure -> {
|
||||
ListItem(headlineContent = {
|
||||
Text(text = stringResource(id = R.string.troubleshoot_notifications_screen_failure))
|
||||
})
|
||||
RunTestButton(state = state)
|
||||
}
|
||||
is AsyncAction.Confirming -> {
|
||||
ListItem(headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.troubleshoot_notifications_screen_waiting)
|
||||
)
|
||||
})
|
||||
}
|
||||
is AsyncAction.Success -> {
|
||||
ListItem(headlineContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.troubleshoot_notifications_screen_success)
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RunTestButton(state: TroubleshootNotificationsState) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Button(
|
||||
text = stringResource(
|
||||
id = if (state.testSuiteState.mainState is AsyncAction.Failure) {
|
||||
R.string.troubleshoot_notifications_screen_action_again
|
||||
} else {
|
||||
R.string.troubleshoot_notifications_screen_action
|
||||
}
|
||||
),
|
||||
onClick = {
|
||||
state.eventSink(TroubleshootNotificationsEvents.StartTests)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.TestSuiteView(
|
||||
testSuiteState: TroubleshootTestSuiteState,
|
||||
onQuickFixClick: (Int) -> Unit,
|
||||
) {
|
||||
testSuiteState.tests.forEachIndexed { index, testState ->
|
||||
TroubleshootTestView(
|
||||
testState = testState,
|
||||
onQuickFixClick = {
|
||||
onQuickFixClick(index)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun TroubleshootNotificationsViewPreview(
|
||||
@PreviewParameter(TroubleshootNotificationsStateProvider::class) state: TroubleshootNotificationsState,
|
||||
) = ElementPreview {
|
||||
TroubleshootNotificationsView(
|
||||
state = state,
|
||||
onBackClick = {},
|
||||
)
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
import dev.zacsweers.metro.Inject
|
||||
import im.vector.app.features.analytics.plan.NotificationTroubleshoot
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.push.api.GetCurrentPushProvider
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.libraries.troubleshoot.api.test.TestFilterData
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
@Inject
|
||||
class TroubleshootTestSuite(
|
||||
private val sessionId: SessionId,
|
||||
private val notificationTroubleshootTests: Set<@JvmSuppressWildcards NotificationTroubleshootTest>,
|
||||
private val getCurrentPushProvider: GetCurrentPushProvider,
|
||||
private val analyticsService: AnalyticsService,
|
||||
) {
|
||||
lateinit var tests: List<NotificationTroubleshootTest>
|
||||
|
||||
private val _state: MutableStateFlow<TroubleshootTestSuiteState> = MutableStateFlow(
|
||||
TroubleshootTestSuiteState(
|
||||
mainState = AsyncAction.Uninitialized,
|
||||
tests = emptyList<NotificationTroubleshootTestState>().toImmutableList()
|
||||
)
|
||||
)
|
||||
val state: StateFlow<TroubleshootTestSuiteState> = _state
|
||||
|
||||
suspend fun start(coroutineScope: CoroutineScope) {
|
||||
val testFilterData = TestFilterData(
|
||||
currentPushProviderName = getCurrentPushProvider.getCurrentPushProvider(sessionId)
|
||||
)
|
||||
tests = notificationTroubleshootTests
|
||||
.filter { it.isRelevant(testFilterData) }
|
||||
.sortedBy { it.order }
|
||||
tests.forEach {
|
||||
// Observe the state of the tests
|
||||
it.state.onEach {
|
||||
emitState()
|
||||
}.launchIn(coroutineScope)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun runTestSuite(coroutineScope: CoroutineScope) {
|
||||
tests.forEach {
|
||||
it.reset()
|
||||
}
|
||||
tests.forEach {
|
||||
it.run(coroutineScope)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun retryFailedTest(coroutineScope: CoroutineScope) {
|
||||
tests
|
||||
.filter { it.state.value.status is NotificationTroubleshootTestState.Status.Failure }
|
||||
.forEach {
|
||||
it.run(coroutineScope)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun emitState() {
|
||||
val states = tests.map { it.state.value }
|
||||
val mainState = states.computeMainState()
|
||||
when (mainState) {
|
||||
is AsyncAction.Success -> {
|
||||
analyticsService.capture(NotificationTroubleshoot(hasError = false))
|
||||
}
|
||||
is AsyncAction.Failure -> {
|
||||
analyticsService.capture(NotificationTroubleshoot(hasError = true))
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
_state.emit(
|
||||
TroubleshootTestSuiteState(
|
||||
mainState = states.computeMainState(),
|
||||
tests = states.toImmutableList()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun quickFix(
|
||||
testIndex: Int,
|
||||
coroutineScope: CoroutineScope,
|
||||
navigator: NotificationTroubleshootNavigator,
|
||||
) {
|
||||
tests[testIndex].quickFix(coroutineScope, navigator)
|
||||
}
|
||||
}
|
||||
|
||||
fun List<NotificationTroubleshootTestState>.computeMainState(): AsyncAction<Unit> {
|
||||
val isIdle = all { it.status is NotificationTroubleshootTestState.Status.Idle }
|
||||
val isRunning = any { it.status is NotificationTroubleshootTestState.Status.InProgress }
|
||||
return when {
|
||||
isIdle -> AsyncAction.Uninitialized
|
||||
isRunning -> AsyncAction.Loading
|
||||
else -> {
|
||||
if (any { it.status is NotificationTroubleshootTestState.Status.WaitingForUser }) {
|
||||
AsyncAction.ConfirmingNoParams
|
||||
} else if (any { it.status.let { status -> status is NotificationTroubleshootTestState.Status.Failure && status.isCritical } }) {
|
||||
AsyncAction.Failure(Exception("Some tests failed"))
|
||||
} else {
|
||||
AsyncAction.Success(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class TroubleshootTestSuiteState(
|
||||
val mainState: AsyncAction<Unit>,
|
||||
val tests: ImmutableList<NotificationTroubleshootTestState>,
|
||||
)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl.history
|
||||
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultPushHistoryEntryPoint : PushHistoryEntryPoint {
|
||||
override fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
callback: PushHistoryEntryPoint.Callback,
|
||||
): Node {
|
||||
return parentNode.createNode<PushHistoryNode>(buildContext, listOf(callback))
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl.history
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
|
||||
sealed interface PushHistoryEvents {
|
||||
data class SetShowOnlyErrors(val showOnlyErrors: Boolean) : PushHistoryEvents
|
||||
data class Reset(val requiresConfirmation: Boolean) : PushHistoryEvents
|
||||
data class NavigateTo(val sessionId: SessionId, val roomId: RoomId, val eventId: EventId) : PushHistoryEvents
|
||||
data object ClearDialog : PushHistoryEvents
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl.history
|
||||
|
||||
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 dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import im.vector.app.features.analytics.plan.MobileScreen
|
||||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.libraries.architecture.callback
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint
|
||||
import io.element.android.services.analytics.api.ScreenTracker
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
@AssistedInject
|
||||
class PushHistoryNode(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
presenterFactory: PushHistoryPresenter.Factory,
|
||||
private val screenTracker: ScreenTracker,
|
||||
) : Node(buildContext, plugins = plugins), PushHistoryNavigator {
|
||||
private val callback: PushHistoryEntryPoint.Callback = callback()
|
||||
|
||||
override fun navigateTo(roomId: RoomId, eventId: EventId) {
|
||||
callback.navigateToEvent(roomId, eventId)
|
||||
}
|
||||
|
||||
private val presenter = presenterFactory.create(this)
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
screenTracker.TrackScreen(MobileScreen.ScreenName.NotificationTroubleshoot)
|
||||
val state = presenter.present()
|
||||
PushHistoryView(
|
||||
state = state,
|
||||
onBackClick = callback::onDone,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl.history
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedFactory
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.push.api.PushService
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
fun interface PushHistoryNavigator {
|
||||
fun navigateTo(roomId: RoomId, eventId: EventId)
|
||||
}
|
||||
|
||||
@AssistedInject
|
||||
class PushHistoryPresenter(
|
||||
@Assisted private val pushHistoryNavigator: PushHistoryNavigator,
|
||||
private val pushService: PushService,
|
||||
matrixClient: MatrixClient,
|
||||
) : Presenter<PushHistoryState> {
|
||||
@AssistedFactory
|
||||
fun interface Factory {
|
||||
fun create(pushHistoryNavigator: PushHistoryNavigator): PushHistoryPresenter
|
||||
}
|
||||
|
||||
private val sessionId = matrixClient.sessionId
|
||||
|
||||
@Composable
|
||||
override fun present(): PushHistoryState {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val pushCounter by pushService.pushCounter.collectAsState(0)
|
||||
var showOnlyErrors: Boolean by remember { mutableStateOf(false) }
|
||||
val pushHistory by remember(showOnlyErrors) {
|
||||
pushService.getPushHistoryItemsFlow().map {
|
||||
if (showOnlyErrors) {
|
||||
it.filter { item -> item.hasBeenResolved.not() }
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}.collectAsState(emptyList())
|
||||
var resetAction: AsyncAction<Unit> by remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
var showNotSameAccountError by remember { mutableStateOf(false) }
|
||||
|
||||
fun handleEvent(event: PushHistoryEvents) {
|
||||
when (event) {
|
||||
is PushHistoryEvents.SetShowOnlyErrors -> {
|
||||
showOnlyErrors = event.showOnlyErrors
|
||||
}
|
||||
is PushHistoryEvents.Reset -> {
|
||||
if (event.requiresConfirmation) {
|
||||
resetAction = AsyncAction.ConfirmingNoParams
|
||||
} else {
|
||||
resetAction = AsyncAction.Loading
|
||||
coroutineScope.launch {
|
||||
pushService.resetPushHistory()
|
||||
resetAction = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
}
|
||||
PushHistoryEvents.ClearDialog -> {
|
||||
resetAction = AsyncAction.Uninitialized
|
||||
showNotSameAccountError = false
|
||||
}
|
||||
is PushHistoryEvents.NavigateTo -> {
|
||||
if (event.sessionId != sessionId) {
|
||||
showNotSameAccountError = true
|
||||
} else {
|
||||
pushHistoryNavigator.navigateTo(event.roomId, event.eventId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PushHistoryState(
|
||||
pushCounter = pushCounter,
|
||||
pushHistoryItems = pushHistory.toImmutableList(),
|
||||
showOnlyErrors = showOnlyErrors,
|
||||
resetAction = resetAction,
|
||||
showNotSameAccountError = showNotSameAccountError,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl.history
|
||||
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.push.api.history.PushHistoryItem
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class PushHistoryState(
|
||||
val pushCounter: Int,
|
||||
val pushHistoryItems: ImmutableList<PushHistoryItem>,
|
||||
val showOnlyErrors: Boolean,
|
||||
val resetAction: AsyncAction<Unit>,
|
||||
val showNotSameAccountError: Boolean,
|
||||
val eventSink: (PushHistoryEvents) -> Unit,
|
||||
)
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl.history
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.push.api.history.PushHistoryItem
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
open class PushHistoryStateProvider : PreviewParameterProvider<PushHistoryState> {
|
||||
override val values: Sequence<PushHistoryState>
|
||||
get() = sequenceOf(
|
||||
aPushHistoryState(),
|
||||
aPushHistoryState(
|
||||
pushCounter = 123,
|
||||
pushHistoryItems = listOf(
|
||||
aPushHistoryItem(
|
||||
hasBeenResolved = false,
|
||||
comment = "An error description"
|
||||
),
|
||||
aPushHistoryItem(
|
||||
pushDate = 1,
|
||||
providerInfo = "providerInfo2",
|
||||
eventId = EventId("\$anEventId"),
|
||||
roomId = RoomId("!roomId:domain"),
|
||||
sessionId = SessionId("@alice:server.org"),
|
||||
hasBeenResolved = true,
|
||||
comment = "A comment"
|
||||
)
|
||||
)
|
||||
),
|
||||
aPushHistoryState(
|
||||
resetAction = AsyncAction.ConfirmingNoParams,
|
||||
),
|
||||
aPushHistoryState(
|
||||
showNotSameAccountError = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun aPushHistoryState(
|
||||
pushCounter: Int = 0,
|
||||
pushHistoryItems: List<PushHistoryItem> = emptyList(),
|
||||
showOnlyErrors: Boolean = false,
|
||||
resetAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
showNotSameAccountError: Boolean = false,
|
||||
eventSink: (PushHistoryEvents) -> Unit = {},
|
||||
) = PushHistoryState(
|
||||
pushCounter = pushCounter,
|
||||
pushHistoryItems = pushHistoryItems.toImmutableList(),
|
||||
showOnlyErrors = showOnlyErrors,
|
||||
resetAction = resetAction,
|
||||
showNotSameAccountError = showNotSameAccountError,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
||||
fun aPushHistoryItem(
|
||||
pushDate: Long = 0,
|
||||
formattedDate: String = "formattedDate",
|
||||
providerInfo: String = "providerInfo",
|
||||
eventId: EventId? = null,
|
||||
roomId: RoomId? = null,
|
||||
sessionId: SessionId? = null,
|
||||
hasBeenResolved: Boolean = false,
|
||||
comment: String? = null,
|
||||
): PushHistoryItem {
|
||||
return PushHistoryItem(
|
||||
pushDate = pushDate,
|
||||
formattedDate = formattedDate,
|
||||
providerInfo = providerInfo,
|
||||
eventId = eventId,
|
||||
roomId = roomId,
|
||||
sessionId = sessionId,
|
||||
hasBeenResolved = hasBeenResolved,
|
||||
comment = comment
|
||||
)
|
||||
}
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl.history
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.components.async.AsyncActionView
|
||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
|
||||
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
|
||||
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.ListItem
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.push.api.history.PushHistoryItem
|
||||
import io.element.android.libraries.troubleshoot.impl.R
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PushHistoryView(
|
||||
state: PushHistoryState,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding()
|
||||
.imePadding(),
|
||||
contentWindowInsets = WindowInsets.statusBars,
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
navigationIcon = {
|
||||
BackButton(onClick = onBackClick)
|
||||
},
|
||||
titleStr = stringResource(R.string.screen_push_history_title),
|
||||
actions = {
|
||||
IconButton(onClick = { showMenu = !showMenu }) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.OverflowVertical(),
|
||||
contentDescription = stringResource(id = CommonStrings.a11y_user_menu),
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false },
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Show only errors") },
|
||||
trailingIcon = if (state.showOnlyErrors) {
|
||||
{
|
||||
Icon(
|
||||
imageVector = CompoundIcons.CheckCircleSolid(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onClick = {
|
||||
showMenu = false
|
||||
state.eventSink(PushHistoryEvents.SetShowOnlyErrors(state.showOnlyErrors.not()))
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(id = CommonStrings.action_reset)) },
|
||||
onClick = {
|
||||
showMenu = false
|
||||
state.eventSink(PushHistoryEvents.Reset(requiresConfirmation = true))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
) { padding ->
|
||||
PushHistoryContent(
|
||||
modifier = Modifier
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding),
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
||||
AsyncActionView(
|
||||
async = state.resetAction,
|
||||
onSuccess = {},
|
||||
confirmationDialog = {
|
||||
ConfirmationDialog(
|
||||
content = "",
|
||||
title = stringResource(CommonStrings.dialog_title_confirmation),
|
||||
submitText = stringResource(CommonStrings.action_reset),
|
||||
cancelText = stringResource(CommonStrings.action_cancel),
|
||||
onSubmitClick = { state.eventSink(PushHistoryEvents.Reset(requiresConfirmation = false)) },
|
||||
onDismiss = { state.eventSink(PushHistoryEvents.ClearDialog) },
|
||||
)
|
||||
},
|
||||
onErrorDismiss = {},
|
||||
)
|
||||
|
||||
if (state.showNotSameAccountError) {
|
||||
ErrorDialog(
|
||||
content = "Please switch account first to navigate to the event.",
|
||||
onSubmit = { state.eventSink(PushHistoryEvents.ClearDialog) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PushHistoryContent(
|
||||
state: PushHistoryState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth()
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = { Text("Total number of received push") },
|
||||
trailingContent = ListItemContent.Text(state.pushCounter.toString()),
|
||||
)
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
items(
|
||||
items = state.pushHistoryItems,
|
||||
key = {
|
||||
it.pushDate.toString() + it.sessionId + it.roomId + it.eventId
|
||||
},
|
||||
) { pushHistory ->
|
||||
PushHistoryItem(
|
||||
pushHistory,
|
||||
onClick = {
|
||||
val sessionId = pushHistory.sessionId
|
||||
val roomId = pushHistory.roomId
|
||||
val eventId = pushHistory.eventId
|
||||
if (sessionId != null && roomId != null && eventId != null) {
|
||||
state.eventSink(PushHistoryEvents.NavigateTo(sessionId, roomId, eventId))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PushHistoryItem(
|
||||
pushHistoryItem: PushHistoryItem,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
onClick()
|
||||
},
|
||||
) {
|
||||
HorizontalDivider()
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(end = 8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = pushHistoryItem.formattedDate,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
Text(
|
||||
text = pushHistoryItem.providerInfo,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 8.dp, top = 8.dp),
|
||||
text = pushHistoryItem.sessionId?.value ?: "No sessionId",
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
text = pushHistoryItem.roomId?.value ?: "No roomId",
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
text = pushHistoryItem.eventId?.value ?: "No eventId",
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
)
|
||||
pushHistoryItem.comment?.let {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = it,
|
||||
color = if (pushHistoryItem.hasBeenResolved) {
|
||||
ElementTheme.colors.textSecondary
|
||||
} else {
|
||||
ElementTheme.colors.textCriticalPrimary
|
||||
},
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (pushHistoryItem.hasBeenResolved) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.CheckCircleSolid(),
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = ElementTheme.colors.iconSuccessPrimary,
|
||||
contentDescription = null,
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Error(),
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = ElementTheme.colors.iconCriticalPrimary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun PushHistoryViewPreview(
|
||||
@PreviewParameter(PushHistoryStateProvider::class) state: PushHistoryState,
|
||||
) = ElementPreview {
|
||||
PushHistoryView(
|
||||
state = state,
|
||||
onBackClick = {},
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_screen_action">"Запусціць тэсты"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Запусціце тэсты яшчэ раз"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Некаторыя тэсты не ўдаліся. Калі ласка, праглядзіце дэталі."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Запусціце тэсты, каб выявіць праблемы ў вашай канфігурацыі, з-за якіх апавяшчэння могуць паводзіць сябе не так, як чакалася."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Спроба выпраўлення"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Усе тэсты паспяхова пройдзены."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Выпраўленне непаладак з апавяшчэннямі"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Некаторыя тэсты патрабуюць вашай увагі. Калі ласка, праглядзіце дэталі."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_screen_action">"Изпълняване на тестове"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Изпълняване на тестовете отново"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Всички тестове преминаха успешно."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Отстраняване на неизправности с известията"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Historie push oznámení"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Spustit testy"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Spustit testy znovu"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Některé testy selhaly. Zkontrolujte prosím podrobnosti."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Spusťte testy, abyste zjistili jakýkoli problém ve vaší konfiguraci, který může způsobit, že se oznámení nebudou chovat podle očekávání."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Pokus o opravu"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Všechny testy proběhly úspěšně."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Odstraňování problémů s upozorněními"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Některé testy vyžadují vaši pozornost. Zkontrolujte prosím podrobnosti."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Hanes gwthio"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Rhedeg profion"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Rhedeg profion eto"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Methodd rhai profion. Gwiriwch y manylion."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Rhedwch y profion i ganfod unrhyw broblem yn eich ffurfweddiad a allai wneud i hysbysiadau beidio ag ymddwyn yn ôl y disgwyl."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Ceisio trwsio"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Pasiwyd pob prawf yn llwyddiannus."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Hysbysiadau datrys problemau"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Mae rhai profion angen eich sylw. Gwiriwch y manylion."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Push-historik"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Kør test"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Kør test igen"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Nogle tests mislykkedes. Tjek venligst detaljerne."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Kør testene for at registrere de problemer i din konfiguration, der kan medføre, at meddelelser ikke opfører sig som forventet."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Forsøg på at reparere"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Alle tests bestået med succes."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Fejlfinding af meddelelser"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Nogle tests kræver din opmærksomhed. Tjek venligst detaljerne."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Verlauf pushen"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Tests durchführen"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Tests erneut durchführen"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Einige Tests sind fehlgeschlagen. Bitte überprüfe die Details."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Führe die Tests durch, um Probleme zu erkennen, die dazu führen können, dass sich die Benachrichtigungen nicht wie erwartet verhalten."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Problem beheben"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Alle Tests wurden erfolgreich bestanden."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Fehlerbehebung für Benachrichtigungen"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Einige Tests erfordern deine Aufmerksamkeit. Bitte überprüfe die Details."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Ιστορικό push"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Εκτέλεση δοκιμών"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Επανεκτέλεση δοκιμών"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Ορισμένες δοκιμές απέτυχαν. Παρακαλώ έλεγξε τις λεπτομέρειες."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Εκτέλεσε τις δοκιμές για να εντοπίσεις οποιοδήποτε πρόβλημα στη διαμόρφωσή σου που ενδέχεται να κάνει τις ειδοποιήσεις να μην συμπεριφέρονται όπως αναμένεται."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Προσπάθεια διόρθωσης"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Όλες οι δοκιμές έγιναν με επιτυχία."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Αντιμετώπιση προβλημάτων ειδοποιήσεων"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Ορισμένες δοκιμές απαιτούν την προσοχή σου. Έλεγξε τις λεπτομέρειες."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Historial de notificaciones push"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Ejecutar pruebas"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Volver a ejecutar pruebas"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Algunas pruebas fallaron. Por favor, verifica los detalles."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Ejecuta las pruebas para detectar cualquier problema en tu configuración que pueda hacer que las notificaciones no se comporten como es debido."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Intentar arreglar"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Todas las pruebas se han superado con éxito."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Solucionar problemas con las notificaciones"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Algunas pruebas requieren tu atención. Por favor, verifica los detalles."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Tõuketeadete ajalugu"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Käivita testid"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Käivita testid uuesti"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Mõned testid tuvastasid vigu. Palun vaata üksikasjalikku teavet."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Tuvastamaks kas teavituste toimimiseks on vigu, palun käivita mõned asjakohased testid."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Proovi seda lahendada"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Testid ei tuvastanud vigu."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Teavituste veaotsing"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Palun pööra tähelepanu mõnede testide tulemustele. Palun vaata üksikasjalikku teavet."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_screen_action">"Exekutatu testak"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Egin probak berriro"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Proba batzuek huts egin dute. Aztertu xehetasunak."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Saiatu konpontzen"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Proba guztiak arrakastaz gainditu dira."</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Proba batzuek zure arreta eskatzen dute. Aztertu xehetasunak."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"تاریخچهٔ آگاهیهای ارسالی"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"اجرای آزمونها"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"اجرای دوبارهٔ آزمونها"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"برخی آزمونها شکست خوردند. بررسی جزییات."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"تلاش برای تعمیر"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"همهٔ آزمونها با موفّقیت گذرانده شدند."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"رفعاشکال آگاهیها"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Push-historia"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Suorita testit"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Suorita testit uudelleen"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Osa testeistä epäonnistui. Tarkista tiedot."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Suorita testit havaitaksesi konfiguraatiossasi olevat ongelmat, joiden vuoksi ilmoitukset eivät ehkä toimi odotetulla tavalla."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Yritä korjata"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Kaikki testit läpäistiin onnistuneesti."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Ilmoitusten vianmääritys"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Jotkin testit vaativat huomiotasi. Tarkista tiedot."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Historique des Push"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Exécuter les tests"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Relancer les tests"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Certains tests ont échoué. Veuillez vérifier les détails."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Exécuter les tests pour détecter tout problème dans votre configuration susceptible de provoquer un dysfonctionnement des notifications."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Tenter de corriger"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Tous les tests ont réussi."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Dépanner les notifications"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Certains tests nécessitent votre attention. Veuillez vérifier les détails."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Leküldéses értesítések előzmények"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Tesztek futtatása"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Tesztek újbóli futtatása"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Egyes tesztek sikertelenek voltak. Ellenőrizze a részleteket."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"A tesztek futtatása, hogy észlelje a konfigurációban felmerülő olyan problémákat, amelyek miatt az értesítések nem az elvárt módon viselkednek."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Kísérlet a javításra"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Minden teszt sikeresen lezajlott."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Értesítések hibaelhárítása"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Egyes tesztek a figyelmét igénylik. Ellenőrizze a részleteket."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Riwayat dorongan"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Jalankan tes"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Jalankan tes lagi"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Beberapa tes gagal. Silakan periksa detailnya."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Jalankan pengujian untuk mendeteksi masalah apa pun dalam konfigurasi Anda yang mungkin membuat notifikasi tidak berperilaku seperti yang diharapkan."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Mencoba untuk memperbaiki"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Semua tes berhasil dilalui."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Pecahkan masalah notifikasi"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Beberapa tes membutuhkan perhatian Anda. Silakan periksa detailnya."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Cronologia push"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Esegui i test"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Esegui nuovamente i test"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Alcuni test sono falliti. Si prega di controllare i dettagli."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Esegui i test per individuare eventuali problemi nella tua configurazione che potrebbero far sì che le notifiche non si comportino come previsto."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Prova a risolvere"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Tutti i test sono stati superati con successo."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Risoluzione di problemi delle notifiche"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Alcuni test richiedono la tua attenzione. Si prega di controllare i dettagli."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_screen_action">"ტესტების გაშვება"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"ტესტების ისევ გაშვება"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"ზოგიერთი ტესტი წარუმატებელია. გთხოვთ შეამოწმოთ დეტალები."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"ჩაატარეთ ტესტები თქვენს კონფიგურაციაში იმ პრობლემების აღმოსაჩენად, რომლებიც გამოიწვევენ შეტყობინებების არასწორ ქცევას."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"შეკეთების მცდელობა"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"ყველა ტესტი წარმატებით დასრულდა."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"პრობლემების გადაჭრის შეტყობინებები"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"ზოგიერთი ტესტი ითხოვს თქვენს ყურადღებას. გთხოვთ შეამოწმოთ დეტალები."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"푸시 기록"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"테스트 실행"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"테스트를 다시 실행하세요"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"일부 테스트가 실패했습니다. 자세한 내용을 확인해 주세요."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"구성에서 알림이 예상대로 작동하지 않게 하는 문제가 있는지 감지하기 위해 테스트를 실행하세요."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"수정을 시도하다"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"모든 테스트를 성공적으로 통과했습니다."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"문제 해결 알림"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"일부 테스트는 귀하의 주의가 필요합니다. 자세한 내용을 확인해 주시기 바랍니다."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Push-historikk"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Kjør tester"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Kjør tester igjen"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Noen tester mislyktes. Vennligst sjekk detaljene."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Kjør testene for å avdekke eventuelle problemer i konfigurasjonen som kan føre til at varslene ikke oppfører seg som forventet."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Forsøk å fikse"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Alle testene ble bestått."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Feilsøk varsler"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Noen tester krever din oppmerksomhet. Vennligst sjekk detaljene."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_screen_action">"Tests uitvoeren"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Tests opnieuw uitvoeren"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Sommige tests zijn mislukt. Controleer de details."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Voer de tests uit om problemen in je configuratie op te sporen waardoor meldingen mogelijk niet naar verwachting werken."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Probeer het op te lossen"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Alle tests zijn geslaagd."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Problemen met meldingen oplossen"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Sommige tests vereisen je aandacht. Controleer de details."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Historia powiadomień Push"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Uruchom testy"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Uruchom testy ponownie"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Niektóre testy się nie powiodły. Sprawdź szczegóły."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Uruchom testy, aby wykryć potencjalne problemy z konfiguracją, jeśli powiadomienia nie działają prawidłowo."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Spróbuj naprawić"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Wszystkie testy przebiegły pomyślnie."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Rozwiązywanie problemów powiadomień"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Niektóre testy wymagają Twojej uwagi. Sprawdź szczegóły."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Histórico de push"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Executar testes"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Executar os testes novamente"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Alguns testes falharam. Verifique os detalhes."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Execute os testes para detectar qualquer problema na sua configuração que possa fazer com que as notificações não se comportem como esperado."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Tentar consertar"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Todos os testes foram aprovados com sucesso."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Solucionar problemas de notificações"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Alguns testes exigem sua atenção. Verifique os detalhes."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Histórico de push"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Correr testes"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Correr testes novamente"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Alguns testes falharam. Por favor, verifica os detalhes."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Corre os testes para detetar problemas com a tua configuração que possam levar a comportamentos inesperados das notificações."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Tentar corrigir"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Todos os testes realizados sem problemas."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Corrigir notificações"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Alguns testes necessitam a tua atenção. Por favor, verifica os detalhes."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Istoricul notificărilor"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Rulați testele"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Rulați din nou testele"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Unele teste au eșuat. Vă rugăm să verificați detaliile."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Rulați testele pentru a detecta probleme în configurația dumneavoastră care poat face ca notificările să nu se funcționeze corect."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Încercați să remediați"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Toate testele au trecut cu succes."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Depanați notificările"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Unele teste necesită atenția dumneavoastră. Vă rugăm să verificați detaliile."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"История уведомлений"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Выполнение тестов"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Повторное выполнение тестов"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Некоторые тесты провалились. Пожалуйста, проверьте детали."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Выполните тесты, чтобы обнаружить любую проблему в конфигурации, из-за которой уведомления могут работать не так, как ожидалось."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Попытка исправить"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Все тесты прошли успешно."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Уведомления об устранении неполадок"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Некоторые тесты требуют вашего внимания. Пожалуйста, проверьте детали."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"História push oznámení"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Spustiť testy"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Spustiť testy znova"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Niektoré testy zlyhali. Skontrolujte prosím podrobnosti."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Spustite testy, aby ste zistili akýkoľvek problém vo vašej konfigurácii, ktorý môže spôsobiť, že sa upozornenia nebudú správať podľa očakávania."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Pokus o opravu"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Všetky testy prebehli úspešne."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Oznámenia riešení problémov"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Niektoré testy si vyžadujú vašu pozornosť. Prosím skontrolujte podrobnosti."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Push-historik"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Kör tester"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Kör tester igen"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Vissa tester misslyckades. Kontrollera detaljerna."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Kör testerna för att upptäcka eventuella problem i din konfiguration som kan göra att aviseringar inte fungerar som förväntat."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Försök att fixa"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Alla tester godkändes."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Felsök aviseringar"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Vissa tester kräver din uppmärksamhet. Kontrollera detaljerna."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_screen_action">"Testleri çalıştır"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Testleri yeniden çalıştır"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Bazı testler başarısız oldu. Lütfen ayrıntıları kontrol edin."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Yapılandırmanızda bildirimlerin beklendiği gibi davranmamasına neden olabilecek herhangi bir sorunu algılamak için testleri çalıştırın."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Düzeltmeye çalışın"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Tüm testler başarıyla geçti."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Sorun Giderme Bildirimleri"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Bazı testlere bakmanız gerekiyor. Lütfen ayrıntıları kontrol edin."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Історія push-сповіщень"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Запустити тести"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Запустити тести знову"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Деякі тести не пройдено. Перегляньте подробиці."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Запустіть тести, щоб виявити будь-яку проблему у вашій конфігурації, через яку сповіщення можуть не працювати належним чином."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Спробувати виправити"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Всі тести успішно пройдено."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Усунення неполадок сповіщень"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Деякі тести вимагають вашої уваги. Перегляньте подробиці."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_screen_action">"جانچہا چلائیں"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"جانچہا دوبارہ چلائیں"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"کچھ جانچیں ناکام۔ برائے مہربانی تفصیلات پڑتال کریں۔"</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"اپنی تکوین میں کسی بھی مسئلے کا کھوج لگانے کے لیے جانچ ہا چلائیں جس سے اطلاعات توقع کے مطابق برتاؤ نہ کریں۔"</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"ٹھیک کرنے کی کوشش کریں"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"تمام جانچیں کامیابی سے گزاریں ہیں۔"</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"اطلاعات کا ازالہ کریں"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"کچھ جانچوں کیلئے آپکی توجہ درکار ہے۔ برائے مہربانی تفصیلات پڑتال کریں۔"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Bildirishnoma tarixi"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Testlarni ishga tushirish"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Testlarni qayta ishga tushirish"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Ba’zi testlar muvaffaqiyatsiz tugadi. Iltimos, tafsilotlarni tekshirib chiqing."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Sozlamalaringizdagi bildirishnomalarning kutilganidek ishlamasligi mumkin bo‘lgan har qanday muammoni aniqlash uchun testlarni o‘tkazing."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Tuzatishga urinish"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Barcha testlar muvaffaqiyatli yakunlandi."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Bildirishnomalar bilan bog‘liq muammolarni bartaraf etish"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Baʼzi testlar sizning eʼtiboringizni talab etadi. Iltimos, tafsilotlarni tekshirib chiqing."</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"推播通知歷史紀錄"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"執行測試"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"再次執行測試"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"部份測試失敗。請檢查詳細資訊。"</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"執行測試以偵測組態中可能導致通知無法如預期般執行的任何問題。"</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"嘗試修復"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"所有測試皆成功通過。"</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"疑難排解通知"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"部份測試需要您的注意。請檢查詳細資訊。"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"推送历史记录"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"运行测试"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"再次运行测试"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"一些测试失败了。请查看详情。"</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"运行测试以检测您的配置中可能导致通知无法按预期运行的问题。"</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"尝试修复"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"所有测试均成功通过。"</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"排查通知问题"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"有些测试需要注意。请查看详情。"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"Push history"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Run tests"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Run tests again"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Some tests failed. Please check the details."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Run the tests to detect any issue in your configuration that may make notifications not behave as expected."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Attempt to fix"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"All tests passed successfully."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Troubleshoot notifications"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Some tests require your attention. Please check the details."</string>
|
||||
</resources>
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint
|
||||
import io.element.android.services.analytics.test.FakeScreenTracker
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.node.TestParentNode
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultNotificationTroubleShootEntryPointTest {
|
||||
@get:Rule
|
||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@Test
|
||||
fun `test node builder`() {
|
||||
val entryPoint = DefaultNotificationTroubleShootEntryPoint()
|
||||
val parentNode = TestParentNode.create { buildContext, plugins ->
|
||||
TroubleshootNotificationsNode(
|
||||
buildContext = buildContext,
|
||||
plugins = plugins,
|
||||
factory = { createTroubleshootNotificationsPresenter() },
|
||||
screenTracker = FakeScreenTracker(),
|
||||
)
|
||||
}
|
||||
val callback = object : NotificationTroubleShootEntryPoint.Callback {
|
||||
override fun onDone() = lambdaError()
|
||||
override fun navigateToBlockedUsers() = lambdaError()
|
||||
}
|
||||
val result = entryPoint.createNode(
|
||||
parentNode = parentNode,
|
||||
buildContext = BuildContext.root(null),
|
||||
callback = callback,
|
||||
)
|
||||
assertThat(result).isInstanceOf(TroubleshootNotificationsNode::class.java)
|
||||
assertThat(result.plugins).contains(callback)
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
class FakeNotificationTroubleshootTest(
|
||||
override val order: Int = 0,
|
||||
private val defaultName: String = "test name",
|
||||
private val defaultDescription: String = "test description",
|
||||
private val firstStatus: NotificationTroubleshootTestState.Status = NotificationTroubleshootTestState.Status.Idle(visible = true),
|
||||
private val runAction: () -> NotificationTroubleshootTestState? = { null },
|
||||
private val resetAction: () -> NotificationTroubleshootTestState? = { null },
|
||||
private val quickFixAction: () -> NotificationTroubleshootTestState? = { null },
|
||||
) : NotificationTroubleshootTest {
|
||||
private val _state = MutableStateFlow(
|
||||
NotificationTroubleshootTestState(
|
||||
name = defaultName,
|
||||
description = defaultDescription,
|
||||
status = firstStatus
|
||||
)
|
||||
)
|
||||
override val state: StateFlow<NotificationTroubleshootTestState> = _state.asStateFlow()
|
||||
|
||||
override suspend fun run(coroutineScope: CoroutineScope) {
|
||||
updateState(NotificationTroubleshootTestState.Status.InProgress)
|
||||
runAction()?.let {
|
||||
_state.tryEmit(it)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun reset() {
|
||||
updateState(
|
||||
name = defaultName,
|
||||
description = defaultDescription,
|
||||
status = firstStatus,
|
||||
)
|
||||
resetAction()?.let {
|
||||
_state.emit(it)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun quickFix(
|
||||
coroutineScope: CoroutineScope,
|
||||
navigator: NotificationTroubleshootNavigator,
|
||||
) {
|
||||
updateState(NotificationTroubleshootTestState.Status.InProgress)
|
||||
quickFixAction()?.let {
|
||||
_state.emit(it)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateState(
|
||||
status: NotificationTroubleshootTestState.Status,
|
||||
name: String = defaultName,
|
||||
description: String = defaultDescription,
|
||||
) {
|
||||
_state.emit(
|
||||
NotificationTroubleshootTestState(
|
||||
name = name,
|
||||
description = description,
|
||||
status = status,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.push.test.FakeGetCurrentPushProvider
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class TroubleshootNotificationsPresenterTest {
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createTroubleshootNotificationsPresenter()
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.testSuiteState.tests).isEmpty()
|
||||
assertThat(initialState.testSuiteState.mainState).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - start test`() = runTest {
|
||||
val troubleshootTestSuite = createTroubleshootTestSuite(
|
||||
tests = setOf(FakeNotificationTroubleshootTest())
|
||||
)
|
||||
val presenter = createTroubleshootNotificationsPresenter(
|
||||
troubleshootTestSuite = troubleshootTestSuite,
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(TroubleshootNotificationsEvents.StartTests)
|
||||
skipItems(1)
|
||||
val stateAfterStart = awaitItem()
|
||||
assertThat(stateAfterStart.testSuiteState.mainState).isEqualTo(AsyncAction.Loading)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - start failed test`() = runTest {
|
||||
val troubleshootTestSuite = createTroubleshootTestSuite(
|
||||
tests = setOf(
|
||||
FakeNotificationTroubleshootTest(
|
||||
firstStatus = NotificationTroubleshootTestState.Status.Failure(hasQuickFix = false)
|
||||
)
|
||||
)
|
||||
)
|
||||
val presenter = createTroubleshootNotificationsPresenter(
|
||||
troubleshootTestSuite = troubleshootTestSuite,
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(TroubleshootNotificationsEvents.RetryFailedTests)
|
||||
skipItems(1)
|
||||
val stateAfterStart = awaitItem()
|
||||
assertThat(stateAfterStart.testSuiteState.mainState).isEqualTo(AsyncAction.Loading)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - critical failed test`() {
|
||||
`present - check main state`(
|
||||
tests = setOf(
|
||||
FakeNotificationTroubleshootTest(
|
||||
firstStatus = NotificationTroubleshootTestState.Status.Failure(isCritical = true)
|
||||
)
|
||||
),
|
||||
expectedIsCritical = true,
|
||||
expectedMainState = AsyncAction.Failure::class.java,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - success and critical failed test`() {
|
||||
`present - check main state`(
|
||||
tests = setOf(
|
||||
FakeNotificationTroubleshootTest(
|
||||
firstStatus = NotificationTroubleshootTestState.Status.Success
|
||||
),
|
||||
FakeNotificationTroubleshootTest(
|
||||
firstStatus = NotificationTroubleshootTestState.Status.Failure(isCritical = true)
|
||||
),
|
||||
),
|
||||
expectedIsCritical = true,
|
||||
expectedMainState = AsyncAction.Failure::class.java,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - non critical failed test`() {
|
||||
`present - check main state`(
|
||||
tests = setOf(
|
||||
FakeNotificationTroubleshootTest(
|
||||
firstStatus = NotificationTroubleshootTestState.Status.Failure(isCritical = false)
|
||||
)
|
||||
),
|
||||
expectedIsCritical = false,
|
||||
expectedMainState = AsyncAction.Success::class.java,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - waiting for user`() {
|
||||
`present - check main state`(
|
||||
tests = setOf(
|
||||
FakeNotificationTroubleshootTest(
|
||||
firstStatus = NotificationTroubleshootTestState.Status.WaitingForUser
|
||||
)
|
||||
),
|
||||
expectedIsCritical = false,
|
||||
expectedMainState = AsyncAction.ConfirmingNoParams::class.java,
|
||||
)
|
||||
}
|
||||
|
||||
private fun `present - check main state`(
|
||||
tests: Set<NotificationTroubleshootTest>,
|
||||
expectedIsCritical: Boolean,
|
||||
expectedMainState: Class<out AsyncAction<*>>,
|
||||
) = runTest {
|
||||
val troubleshootTestSuite = createTroubleshootTestSuite(
|
||||
tests = tests
|
||||
)
|
||||
val presenter = createTroubleshootNotificationsPresenter(
|
||||
troubleshootTestSuite = troubleshootTestSuite,
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.hasFailedTests).isEqualTo(expectedIsCritical)
|
||||
assertThat(initialState.testSuiteState.mainState).isInstanceOf(expectedMainState)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - quick fix test`() = runTest {
|
||||
val troubleshootTestSuite = createTroubleshootTestSuite(
|
||||
tests = setOf(
|
||||
FakeNotificationTroubleshootTest(
|
||||
firstStatus = NotificationTroubleshootTestState.Status.Failure(hasQuickFix = false)
|
||||
)
|
||||
)
|
||||
)
|
||||
val presenter = createTroubleshootNotificationsPresenter(
|
||||
troubleshootTestSuite = troubleshootTestSuite,
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.testSuiteState.mainState).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
initialState.eventSink(TroubleshootNotificationsEvents.QuickFix(0))
|
||||
val stateAfterStart = awaitItem()
|
||||
assertThat(stateAfterStart.testSuiteState.mainState).isEqualTo(AsyncAction.Loading)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTroubleshootTestSuite(
|
||||
tests: Set<NotificationTroubleshootTest> = emptySet(),
|
||||
currentPushProvider: String? = null,
|
||||
): TroubleshootTestSuite {
|
||||
return TroubleshootTestSuite(
|
||||
sessionId = A_SESSION_ID,
|
||||
notificationTroubleshootTests = tests,
|
||||
getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider),
|
||||
analyticsService = FakeAnalyticsService(),
|
||||
)
|
||||
}
|
||||
|
||||
internal fun createTroubleshootNotificationsPresenter(
|
||||
navigator: NotificationTroubleshootNavigator = object : NotificationTroubleshootNavigator {
|
||||
override fun navigateToBlockedUsers() = lambdaError()
|
||||
},
|
||||
troubleshootTestSuite: TroubleshootTestSuite = createTroubleshootTestSuite(),
|
||||
): TroubleshootNotificationsPresenter {
|
||||
return TroubleshootNotificationsPresenter(
|
||||
navigator = navigator,
|
||||
troubleshootTestSuite = troubleshootTestSuite,
|
||||
)
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.ensureCalledOnce
|
||||
import io.element.android.tests.testutils.pressBack
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class TroubleshootNotificationsViewTest {
|
||||
@get:Rule
|
||||
val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun `press menu back invokes the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<TroubleshootNotificationsEvents>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
rule.setTroubleshootNotificationsView(
|
||||
state = aTroubleshootNotificationsState(
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
onBackClick = it,
|
||||
)
|
||||
rule.pressBack()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on run test emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<TroubleshootNotificationsEvents>()
|
||||
rule.setTroubleshootNotificationsView(
|
||||
aTroubleshootNotificationsState(
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.onNodeWithText("Run tests").performClick()
|
||||
eventsRecorder.assertSingle(TroubleshootNotificationsEvents.StartTests)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on run test again emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<TroubleshootNotificationsEvents>()
|
||||
rule.setTroubleshootNotificationsView(
|
||||
aTroubleshootNotificationsState(
|
||||
tests = listOf(
|
||||
aTroubleshootTestStateFailure(
|
||||
hasQuickFix = false
|
||||
)
|
||||
),
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.onNodeWithText("Run tests again").performClick()
|
||||
eventsRecorder.assertList(
|
||||
listOf(
|
||||
TroubleshootNotificationsEvents.RetryFailedTests,
|
||||
TroubleshootNotificationsEvents.StartTests,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on quick fix emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<TroubleshootNotificationsEvents>()
|
||||
rule.setTroubleshootNotificationsView(
|
||||
aTroubleshootNotificationsState(
|
||||
tests = listOf(
|
||||
aTroubleshootTestStateFailure(
|
||||
hasQuickFix = true
|
||||
)
|
||||
),
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.onNodeWithText("Attempt to fix").performClick()
|
||||
eventsRecorder.assertList(
|
||||
listOf(
|
||||
TroubleshootNotificationsEvents.RetryFailedTests,
|
||||
TroubleshootNotificationsEvents.QuickFix(0),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setTroubleshootNotificationsView(
|
||||
state: TroubleshootNotificationsState,
|
||||
onBackClick: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
setContent {
|
||||
TroubleshootNotificationsView(
|
||||
state = state,
|
||||
onBackClick = onBackClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl.history
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.push.test.FakePushService
|
||||
import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint
|
||||
import io.element.android.services.analytics.test.FakeScreenTracker
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.node.TestParentNode
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultPushHistoryEntryPointTest {
|
||||
@get:Rule
|
||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@Test
|
||||
fun `test node builder`() {
|
||||
val entryPoint = DefaultPushHistoryEntryPoint()
|
||||
val parentNode = TestParentNode.create { buildContext, plugins ->
|
||||
PushHistoryNode(
|
||||
buildContext = buildContext,
|
||||
plugins = plugins,
|
||||
presenterFactory = {
|
||||
PushHistoryPresenter(
|
||||
pushHistoryNavigator = object : PushHistoryNavigator {
|
||||
override fun navigateTo(roomId: RoomId, eventId: EventId) = lambdaError()
|
||||
},
|
||||
pushService = FakePushService(),
|
||||
matrixClient = FakeMatrixClient(),
|
||||
)
|
||||
},
|
||||
screenTracker = FakeScreenTracker(),
|
||||
)
|
||||
}
|
||||
val callback = object : PushHistoryEntryPoint.Callback {
|
||||
override fun onDone() = lambdaError()
|
||||
override fun navigateToEvent(roomId: RoomId, eventId: EventId) = lambdaError()
|
||||
}
|
||||
val result = entryPoint.createNode(
|
||||
parentNode = parentNode,
|
||||
buildContext = BuildContext.root(null),
|
||||
callback = callback,
|
||||
)
|
||||
assertThat(result).isInstanceOf(PushHistoryNode::class.java)
|
||||
assertThat(result.plugins).contains(callback)
|
||||
}
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl.history
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
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.FakeMatrixClient
|
||||
import io.element.android.libraries.push.api.PushService
|
||||
import io.element.android.libraries.push.test.FakePushService
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class PushHistoryPresenterTest {
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createPushHistoryPresenter()
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.pushCounter).isEqualTo(0)
|
||||
assertThat(initialState.pushHistoryItems).isEmpty()
|
||||
assertThat(initialState.showOnlyErrors).isFalse()
|
||||
assertThat(initialState.resetAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
assertThat(initialState.showNotSameAccountError).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - updating state`() = runTest {
|
||||
val pushService = FakePushService()
|
||||
val presenter = createPushHistoryPresenter(
|
||||
pushService = pushService,
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.pushCounter).isEqualTo(0)
|
||||
assertThat(initialState.pushHistoryItems).isEmpty()
|
||||
pushService.emitPushCounter(1)
|
||||
assertThat(awaitItem().pushCounter).isEqualTo(1)
|
||||
val item = aPushHistoryItem()
|
||||
pushService.emitPushHistoryItems(listOf(item))
|
||||
assertThat(awaitItem().pushHistoryItems).containsExactly(item)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - reset and cancel`() = runTest {
|
||||
val resetPushHistoryResult = lambdaRecorder<Unit> { }
|
||||
val pushService = FakePushService(
|
||||
resetPushHistoryResult = resetPushHistoryResult,
|
||||
)
|
||||
val presenter = createPushHistoryPresenter(
|
||||
pushService = pushService,
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(PushHistoryEvents.Reset(requiresConfirmation = true))
|
||||
assertThat(awaitItem().resetAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
initialState.eventSink(PushHistoryEvents.ClearDialog)
|
||||
assertThat(awaitItem().resetAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
resetPushHistoryResult.assertions().isNeverCalled()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - reset and confirm`() = runTest {
|
||||
val resetPushHistoryResult = lambdaRecorder<Unit> { }
|
||||
val pushService = FakePushService(
|
||||
resetPushHistoryResult = resetPushHistoryResult,
|
||||
)
|
||||
val presenter = createPushHistoryPresenter(
|
||||
pushService = pushService,
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(PushHistoryEvents.Reset(requiresConfirmation = true))
|
||||
assertThat(awaitItem().resetAction).isEqualTo(AsyncAction.ConfirmingNoParams)
|
||||
initialState.eventSink(PushHistoryEvents.Reset(requiresConfirmation = false))
|
||||
assertThat(awaitItem().resetAction).isEqualTo(AsyncAction.Loading)
|
||||
assertThat(awaitItem().resetAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
resetPushHistoryResult.assertions().isCalledOnce()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - set show only errors`() = runTest {
|
||||
val pushService = FakePushService()
|
||||
val presenter = createPushHistoryPresenter(
|
||||
pushService = pushService,
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.showOnlyErrors).isFalse()
|
||||
val item = aPushHistoryItem(hasBeenResolved = true)
|
||||
val itemError = aPushHistoryItem(hasBeenResolved = false)
|
||||
pushService.emitPushHistoryItems(listOf(item, itemError))
|
||||
awaitItem().let { state ->
|
||||
assertThat(state.pushHistoryItems).containsExactly(item, itemError)
|
||||
state.eventSink(PushHistoryEvents.SetShowOnlyErrors(showOnlyErrors = true))
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().let { state ->
|
||||
assertThat(state.showOnlyErrors).isTrue()
|
||||
assertThat(state.pushHistoryItems).containsExactly(itemError)
|
||||
state.eventSink(PushHistoryEvents.SetShowOnlyErrors(showOnlyErrors = false))
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().let { state ->
|
||||
assertThat(state.showOnlyErrors).isFalse()
|
||||
assertThat(state.pushHistoryItems).containsExactly(item, itemError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - item click current account`() = runTest {
|
||||
val pushHistoryNavigatorResult = lambdaRecorder<RoomId, EventId, Unit> { _, _ -> }
|
||||
val presenter = createPushHistoryPresenter(
|
||||
pushHistoryNavigator = { roomId, eventId ->
|
||||
pushHistoryNavigatorResult(roomId, eventId)
|
||||
}
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(
|
||||
PushHistoryEvents.NavigateTo(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
eventId = AN_EVENT_ID,
|
||||
)
|
||||
)
|
||||
pushHistoryNavigatorResult.assertions()
|
||||
.isCalledOnce()
|
||||
.with(value(A_ROOM_ID), value(AN_EVENT_ID))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - item click not current account`() = runTest {
|
||||
val presenter = createPushHistoryPresenter()
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(
|
||||
PushHistoryEvents.NavigateTo(
|
||||
sessionId = A_SESSION_ID_2,
|
||||
roomId = A_ROOM_ID,
|
||||
eventId = AN_EVENT_ID,
|
||||
)
|
||||
)
|
||||
assertThat(awaitItem().showNotSameAccountError).isTrue()
|
||||
// Reset error
|
||||
initialState.eventSink(PushHistoryEvents.ClearDialog)
|
||||
assertThat(awaitItem().showNotSameAccountError).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createPushHistoryPresenter(
|
||||
pushHistoryNavigator: PushHistoryNavigator = PushHistoryNavigator { _, _ -> lambdaError() },
|
||||
pushService: PushService = FakePushService(),
|
||||
matrixClient: MatrixClient = FakeMatrixClient(),
|
||||
): PushHistoryPresenter {
|
||||
return PushHistoryPresenter(
|
||||
pushHistoryNavigator = pushHistoryNavigator,
|
||||
pushService = pushService,
|
||||
matrixClient = matrixClient,
|
||||
)
|
||||
}
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.impl.history
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_FORMATTED_DATE
|
||||
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.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.clickOn
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PushHistoryViewTest {
|
||||
@get:Rule
|
||||
val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun `clicking on Reset sends a PushHistoryEvents`() {
|
||||
val eventsRecorder = EventsRecorder<PushHistoryEvents>()
|
||||
rule.setPushHistoryView(
|
||||
aPushHistoryState(
|
||||
pushCounter = 123,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
val menuContentDescription = rule.activity.getString(CommonStrings.a11y_user_menu)
|
||||
rule.onNodeWithContentDescription(menuContentDescription).performClick()
|
||||
rule.clickOn(CommonStrings.action_reset)
|
||||
eventsRecorder.assertSingle(PushHistoryEvents.Reset(requiresConfirmation = true))
|
||||
// Also check that the push counter is rendered
|
||||
rule.onNodeWithText("123").assertExists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on show only errors sends a PushHistoryEvents(true)`() {
|
||||
val eventsRecorder = EventsRecorder<PushHistoryEvents>()
|
||||
rule.setPushHistoryView(
|
||||
aPushHistoryState(
|
||||
showOnlyErrors = false,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
val menuContentDescription = rule.activity.getString(CommonStrings.a11y_user_menu)
|
||||
rule.onNodeWithContentDescription(menuContentDescription).performClick()
|
||||
rule.onNodeWithText("Show only errors").performClick()
|
||||
eventsRecorder.assertSingle(PushHistoryEvents.SetShowOnlyErrors(showOnlyErrors = true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on show only errors sends a PushHistoryEvents(false)`() {
|
||||
val eventsRecorder = EventsRecorder<PushHistoryEvents>()
|
||||
rule.setPushHistoryView(
|
||||
aPushHistoryState(
|
||||
showOnlyErrors = true,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
val menuContentDescription = rule.activity.getString(CommonStrings.a11y_user_menu)
|
||||
rule.onNodeWithContentDescription(menuContentDescription).performClick()
|
||||
rule.onNodeWithText("Show only errors").performClick()
|
||||
eventsRecorder.assertSingle(PushHistoryEvents.SetShowOnlyErrors(showOnlyErrors = false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on an invalid event has no effect`() {
|
||||
val eventsRecorder = EventsRecorder<PushHistoryEvents>(expectEvents = false)
|
||||
rule.setPushHistoryView(
|
||||
aPushHistoryState(
|
||||
pushHistoryItems = listOf(
|
||||
aPushHistoryItem(
|
||||
formattedDate = A_FORMATTED_DATE,
|
||||
)
|
||||
),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.onNodeWithText(A_FORMATTED_DATE).performClick()
|
||||
// No callback invoked
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on a valid event emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<PushHistoryEvents>()
|
||||
rule.setPushHistoryView(
|
||||
aPushHistoryState(
|
||||
pushHistoryItems = listOf(
|
||||
aPushHistoryItem(
|
||||
formattedDate = A_FORMATTED_DATE,
|
||||
eventId = AN_EVENT_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
sessionId = A_SESSION_ID,
|
||||
)
|
||||
),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.onNodeWithText(A_FORMATTED_DATE).performClick()
|
||||
eventsRecorder.assertSingle(
|
||||
PushHistoryEvents.NavigateTo(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
eventId = AN_EVENT_ID,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setPushHistoryView(
|
||||
state: PushHistoryState,
|
||||
onBackClick: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
setContent {
|
||||
PushHistoryView(
|
||||
state = state,
|
||||
onBackClick = onBackClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
plugins {
|
||||
id("io.element.android-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.libraries.troubleshoot.test"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.troubleshoot.api)
|
||||
implementation(projects.tests.testutils)
|
||||
implementation(libs.coroutines.test)
|
||||
implementation(libs.test.core)
|
||||
implementation(libs.test.turbine)
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.test
|
||||
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakeNotificationTroubleShootEntryPoint : NotificationTroubleShootEntryPoint {
|
||||
override fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
callback: NotificationTroubleShootEntryPoint.Callback,
|
||||
): Node = lambdaError()
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.test
|
||||
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakeNotificationTroubleshootNavigator(
|
||||
private val openIgnoredUsersResult: () -> Unit = { lambdaError() },
|
||||
) : NotificationTroubleshootNavigator {
|
||||
override fun navigateToBlockedUsers() = openIgnoredUsersResult()
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.troubleshoot.test
|
||||
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakePushHistoryEntryPoint : PushHistoryEntryPoint {
|
||||
override fun createNode(
|
||||
parentNode: Node,
|
||||
buildContext: BuildContext,
|
||||
callback: PushHistoryEntryPoint.Callback,
|
||||
): Node = lambdaError()
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
@file:Suppress("UnusedImports")
|
||||
|
||||
package io.element.android.libraries.troubleshoot.test
|
||||
|
||||
import app.cash.turbine.TurbineTestContext
|
||||
import app.cash.turbine.test
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
|
||||
context(testScope: TestScope)
|
||||
suspend fun NotificationTroubleshootTest.runAndTestState(
|
||||
validate: suspend TurbineTestContext<NotificationTroubleshootTestState>.() -> Unit,
|
||||
) {
|
||||
testScope.backgroundScope.launch {
|
||||
run(this)
|
||||
}
|
||||
state.test(validate = validate)
|
||||
}
|
||||
Reference in New Issue
Block a user