Skip to content

Commit f6c6943

Browse files
authored
Merge branch 'develop' into feature/fga/pinned_messages_actions
2 parents 004cb74 + 7238af7 commit f6c6943

File tree

138 files changed

+1582
-1025
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

138 files changed

+1582
-1025
lines changed

.idea/dictionaries/shared.xml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CHANGES.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
Changes in Element X v0.6.0 (2024-09-12)
2+
========================================
3+
4+
### 🙌 Improvements
5+
* Enables pinned messages feature by default. by @ganfra in https://github.com/element-hq/element-x-android/pull/3439
6+
* Pinned messages list : hide reactions by @ganfra in https://github.com/element-hq/element-x-android/pull/3430
7+
8+
### 🐛 Bugfixes
9+
* Feature/fga/pinned messages fix timeline provider by @ganfra in https://github.com/element-hq/element-x-android/pull/3432
10+
11+
### Dependency upgrades
12+
* Update activity to v1.9.2 by @renovate in https://github.com/element-hq/element-x-android/pull/3397
13+
* Update peter-evans/create-pull-request action to v7 by @renovate in https://github.com/element-hq/element-x-android/pull/3383
14+
* Rust sdk upgrade to 0.2.43 by @bmarty in https://github.com/element-hq/element-x-android/pull/3446
15+
16+
### Others
17+
* DeviceId and cleanup. by @bmarty in https://github.com/element-hq/element-x-android/pull/3442
18+
* Update application store assets by @bmarty in https://github.com/element-hq/element-x-android/pull/3441
19+
120
Changes in Element X v0.5.3 (2024-09-10)
221
========================================
322

appnav/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ dependencies {
5959
testImplementation(libs.test.turbine)
6060
testImplementation(projects.libraries.matrix.test)
6161
testImplementation(projects.libraries.oidc.impl)
62+
testImplementation(projects.libraries.preferences.test)
6263
testImplementation(projects.libraries.push.test)
6364
testImplementation(projects.libraries.pushproviders.test)
6465
testImplementation(projects.features.networkmonitor.test)

appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInEvents.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ package io.element.android.appnav.loggedin
99

1010
sealed interface LoggedInEvents {
1111
data class CloseErrorDialog(val doNotShowAgain: Boolean) : LoggedInEvents
12+
data object CheckSlidingSyncProxyAvailability : LoggedInEvents
13+
data object LogoutAndMigrateToNativeSlidingSync : LoggedInEvents
1214
}

appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import androidx.compose.runtime.getValue
1616
import androidx.compose.runtime.mutableStateOf
1717
import androidx.compose.runtime.remember
1818
import androidx.compose.runtime.rememberCoroutineScope
19+
import androidx.compose.runtime.setValue
1920
import im.vector.app.features.analytics.plan.CryptoSessionStateChange
2021
import im.vector.app.features.analytics.plan.UserProperties
2122
import io.element.android.features.networkmonitor.api.NetworkMonitor
@@ -29,6 +30,7 @@ import io.element.android.libraries.matrix.api.encryption.RecoveryState
2930
import io.element.android.libraries.matrix.api.roomlist.RoomListService
3031
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
3132
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
33+
import io.element.android.libraries.preferences.api.store.EnableNativeSlidingSyncUseCase
3234
import io.element.android.libraries.push.api.PushService
3335
import io.element.android.libraries.pushproviders.api.RegistrationFailure
3436
import io.element.android.services.analytics.api.AnalyticsService
@@ -48,6 +50,7 @@ class LoggedInPresenter @Inject constructor(
4850
private val sessionVerificationService: SessionVerificationService,
4951
private val analyticsService: AnalyticsService,
5052
private val encryptionService: EncryptionService,
53+
private val enableNativeSlidingSyncUseCase: EnableNativeSlidingSyncUseCase,
5154
) : Presenter<LoggedInState> {
5255
@Composable
5356
override fun present(): LoggedInState {
@@ -78,6 +81,7 @@ class LoggedInPresenter @Inject constructor(
7881
networkStatus == NetworkStatus.Online && syncIndicator == RoomListService.SyncIndicator.Show
7982
}
8083
}
84+
var forceNativeSlidingSyncMigration by remember { mutableStateOf(false) }
8185
LaunchedEffect(Unit) {
8286
combine(
8387
sessionVerificationService.sessionVerifiedStatus,
@@ -97,13 +101,26 @@ class LoggedInPresenter @Inject constructor(
97101
}
98102
}
99103
}
104+
LoggedInEvents.CheckSlidingSyncProxyAvailability -> coroutineScope.launch {
105+
// Force the user to log out if they were using the proxy sliding sync and it's no longer available, but native sliding sync is.
106+
forceNativeSlidingSyncMigration = !matrixClient.isUsingNativeSlidingSync() &&
107+
matrixClient.isNativeSlidingSyncSupported() &&
108+
!matrixClient.isSlidingSyncProxySupported()
109+
}
110+
LoggedInEvents.LogoutAndMigrateToNativeSlidingSync -> coroutineScope.launch {
111+
// Enable native sliding sync if it wasn't already the case
112+
enableNativeSlidingSyncUseCase()
113+
// Then force the logout
114+
matrixClient.logout(userInitiated = true, ignoreSdkError = true)
115+
}
100116
}
101117
}
102118

103119
return LoggedInState(
104120
showSyncSpinner = showSyncSpinner,
105121
pusherRegistrationState = pusherRegistrationState.value,
106122
ignoreRegistrationError = ignoreRegistrationError,
123+
forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration,
107124
eventSink = ::handleEvent
108125
)
109126
}

appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ data class LoggedInState(
1313
val showSyncSpinner: Boolean,
1414
val pusherRegistrationState: AsyncData<Unit>,
1515
val ignoreRegistrationError: Boolean,
16+
val forceNativeSlidingSyncMigration: Boolean,
1617
val eventSink: (LoggedInEvents) -> Unit,
1718
)

appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@ open class LoggedInStateProvider : PreviewParameterProvider<LoggedInState> {
1616
aLoggedInState(),
1717
aLoggedInState(showSyncSpinner = true),
1818
aLoggedInState(pusherRegistrationState = AsyncData.Failure(PusherRegistrationFailure.NoDistributorsAvailable())),
19+
aLoggedInState(forceNativeSlidingSyncMigration = true),
1920
)
2021
}
2122

2223
fun aLoggedInState(
2324
showSyncSpinner: Boolean = false,
2425
pusherRegistrationState: AsyncData<Unit> = AsyncData.Uninitialized,
26+
forceNativeSlidingSyncMigration: Boolean = false,
2527
) = LoggedInState(
2628
showSyncSpinner = showSyncSpinner,
2729
pusherRegistrationState = pusherRegistrationState,
2830
ignoreRegistrationError = false,
31+
forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration,
2932
eventSink = {},
3033
)

appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@ import androidx.compose.ui.Alignment
1515
import androidx.compose.ui.Modifier
1616
import androidx.compose.ui.res.stringResource
1717
import androidx.compose.ui.tooling.preview.PreviewParameter
18+
import androidx.lifecycle.Lifecycle
19+
import io.element.android.appnav.R
1820
import io.element.android.libraries.architecture.AsyncData
21+
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
1922
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialogWithDoNotShowAgain
2023
import io.element.android.libraries.designsystem.preview.ElementPreview
2124
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
25+
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
2226
import io.element.android.libraries.matrix.api.exception.isNetworkError
2327
import io.element.android.libraries.ui.strings.CommonStrings
2428

@@ -28,6 +32,11 @@ fun LoggedInView(
2832
navigateToNotificationTroubleshoot: () -> Unit,
2933
modifier: Modifier = Modifier
3034
) {
35+
OnLifecycleEvent { _, event ->
36+
if (event == Lifecycle.Event.ON_RESUME) {
37+
state.eventSink(LoggedInEvents.CheckSlidingSyncProxyAvailability)
38+
}
39+
}
3140
Box(
3241
modifier = modifier
3342
.fillMaxSize()
@@ -61,6 +70,13 @@ fun LoggedInView(
6170
}
6271
}
6372
}
73+
74+
// Set the force migration dialog here so it's always displayed over every screen
75+
if (state.forceNativeSlidingSyncMigration) {
76+
ForceNativeSlidingSyncMigrationDialog(onSubmit = {
77+
state.eventSink(LoggedInEvents.LogoutAndMigrateToNativeSlidingSync)
78+
})
79+
}
6480
}
6581

6682
private fun Throwable.getReason(): String? {
@@ -80,6 +96,19 @@ private fun Throwable.getReason(): String? {
8096
}
8197
}
8298

99+
@Composable
100+
private fun ForceNativeSlidingSyncMigrationDialog(
101+
onSubmit: () -> Unit,
102+
) {
103+
ErrorDialog(
104+
title = null,
105+
content = stringResource(R.string.banner_migrate_to_native_sliding_sync_force_logout_title),
106+
submitText = stringResource(R.string.banner_migrate_to_native_sliding_sync_action),
107+
onSubmit = onSubmit,
108+
canDismiss = false,
109+
)
110+
}
111+
83112
@PreviewsDayNight
84113
@Composable
85114
internal fun LoggedInViewPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) = ElementPreview {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
3+
<string name="banner_migrate_to_native_sliding_sync_action">"Log Out &amp; Upgrade"</string>
4+
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."</string>
5+
</resources>

appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient
2929
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
3030
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
3131
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
32+
import io.element.android.libraries.preferences.api.store.EnableNativeSlidingSyncUseCase
33+
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
3234
import io.element.android.libraries.push.api.PushService
3335
import io.element.android.libraries.push.test.FakePushService
3436
import io.element.android.libraries.pushproviders.api.Distributor
@@ -42,6 +44,10 @@ import io.element.android.tests.testutils.lambda.any
4244
import io.element.android.tests.testutils.lambda.lambdaError
4345
import io.element.android.tests.testutils.lambda.lambdaRecorder
4446
import io.element.android.tests.testutils.lambda.value
47+
import kotlinx.coroutines.ExperimentalCoroutinesApi
48+
import kotlinx.coroutines.flow.first
49+
import kotlinx.coroutines.test.TestScope
50+
import kotlinx.coroutines.test.advanceUntilIdle
4551
import kotlinx.coroutines.test.runTest
4652
import org.junit.Rule
4753
import org.junit.Test
@@ -91,7 +97,8 @@ class LoggedInPresenterTest {
9197
pushService = FakePushService(),
9298
sessionVerificationService = verificationService,
9399
analyticsService = analyticsService,
94-
encryptionService = encryptionService
100+
encryptionService = encryptionService,
101+
enableNativeSlidingSyncUseCase = EnableNativeSlidingSyncUseCase(InMemoryAppPreferencesStore(), this),
95102
)
96103
moleculeFlow(RecompositionMode.Immediate) {
97104
presenter.present()
@@ -487,26 +494,103 @@ class LoggedInPresenterTest {
487494
)
488495
}
489496

497+
@Test
498+
fun `present - CheckSlidingSyncProxyAvailability forces the sliding sync migration under the right circumstances`() = runTest {
499+
// The migration will be forced if:
500+
// - The user is not using the native sliding sync
501+
// - The sliding sync proxy is no longer supported
502+
// - The native sliding sync is supported
503+
val matrixClient = FakeMatrixClient(
504+
isUsingNativeSlidingSyncLambda = { false },
505+
isSlidingSyncProxySupportedLambda = { false },
506+
isNativeSlidingSyncSupportedLambda = { true },
507+
)
508+
val presenter = createLoggedInPresenter(matrixClient = matrixClient)
509+
moleculeFlow(RecompositionMode.Immediate) {
510+
presenter.present()
511+
}.test {
512+
val initialState = awaitItem()
513+
assertThat(initialState.forceNativeSlidingSyncMigration).isFalse()
514+
515+
initialState.eventSink(LoggedInEvents.CheckSlidingSyncProxyAvailability)
516+
517+
assertThat(awaitItem().forceNativeSlidingSyncMigration).isTrue()
518+
}
519+
}
520+
521+
@Test
522+
fun `present - CheckSlidingSyncProxyAvailability will not force the migration if native sliding sync is not supported too`() = runTest {
523+
val matrixClient = FakeMatrixClient(
524+
isUsingNativeSlidingSyncLambda = { false },
525+
isSlidingSyncProxySupportedLambda = { false },
526+
isNativeSlidingSyncSupportedLambda = { false },
527+
)
528+
val presenter = createLoggedInPresenter(matrixClient = matrixClient)
529+
moleculeFlow(RecompositionMode.Immediate) {
530+
presenter.present()
531+
}.test {
532+
val initialState = awaitItem()
533+
assertThat(initialState.forceNativeSlidingSyncMigration).isFalse()
534+
535+
initialState.eventSink(LoggedInEvents.CheckSlidingSyncProxyAvailability)
536+
537+
expectNoEvents()
538+
}
539+
}
540+
541+
@OptIn(ExperimentalCoroutinesApi::class)
542+
@Test
543+
fun `present - LogoutAndMigrateToNativeSlidingSync enables native sliding sync and logs out the user`() = runTest {
544+
val logoutLambda = lambdaRecorder<Boolean, Boolean, String?> { userInitiated, ignoreSdkError ->
545+
assertThat(userInitiated).isTrue()
546+
assertThat(ignoreSdkError).isTrue()
547+
null
548+
}
549+
val matrixClient = FakeMatrixClient().apply {
550+
this.logoutLambda = logoutLambda
551+
}
552+
val appPreferencesStore = InMemoryAppPreferencesStore()
553+
val enableNativeSlidingSyncUseCase = EnableNativeSlidingSyncUseCase(appPreferencesStore, this)
554+
val presenter = createLoggedInPresenter(matrixClient = matrixClient, enableNativeSlidingSyncUseCase = enableNativeSlidingSyncUseCase)
555+
moleculeFlow(RecompositionMode.Immediate) {
556+
presenter.present()
557+
}.test {
558+
val initialState = awaitItem()
559+
560+
assertThat(appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first()).isFalse()
561+
562+
initialState.eventSink(LoggedInEvents.LogoutAndMigrateToNativeSlidingSync)
563+
564+
advanceUntilIdle()
565+
566+
assertThat(appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first()).isTrue()
567+
assertThat(logoutLambda.assertions().isCalledOnce())
568+
}
569+
}
570+
490571
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
491572
skipItems(1)
492573
return awaitItem()
493574
}
494575

495-
private fun createLoggedInPresenter(
576+
private fun TestScope.createLoggedInPresenter(
496577
roomListService: RoomListService = FakeRoomListService(),
497578
networkStatus: NetworkStatus = NetworkStatus.Offline,
498579
analyticsService: AnalyticsService = FakeAnalyticsService(),
499580
sessionVerificationService: SessionVerificationService = FakeSessionVerificationService(),
500581
encryptionService: EncryptionService = FakeEncryptionService(),
501582
pushService: PushService = FakePushService(),
583+
enableNativeSlidingSyncUseCase: EnableNativeSlidingSyncUseCase = EnableNativeSlidingSyncUseCase(InMemoryAppPreferencesStore(), this),
584+
matrixClient: MatrixClient = FakeMatrixClient(roomListService = roomListService),
502585
): LoggedInPresenter {
503586
return LoggedInPresenter(
504-
matrixClient = FakeMatrixClient(roomListService = roomListService),
587+
matrixClient = matrixClient,
505588
networkMonitor = FakeNetworkMonitor(networkStatus),
506589
pushService = pushService,
507590
sessionVerificationService = sessionVerificationService,
508591
analyticsService = analyticsService,
509-
encryptionService = encryptionService
592+
encryptionService = encryptionService,
593+
enableNativeSlidingSyncUseCase = enableNativeSlidingSyncUseCase,
510594
)
511595
}
512596
}

0 commit comments

Comments
 (0)