Skip to content

Commit 51ee5bf

Browse files
authored
Merge pull request #3618 from element-hq/feature/bma/injectPresenter
Ensure that `Presenter`s do not depend on other presenters.
2 parents dd60eaf + d390b4f commit 51ee5bf

File tree

73 files changed

+849
-1029
lines changed

Some content is hidden

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

73 files changed

+849
-1029
lines changed

appnav/src/main/kotlin/io/element/android/appnav/root/RootPresenter.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import androidx.compose.runtime.LaunchedEffect
1212
import androidx.compose.runtime.collectAsState
1313
import androidx.compose.runtime.getValue
1414
import im.vector.app.features.analytics.plan.SuperProperties
15-
import io.element.android.features.rageshake.api.crash.CrashDetectionPresenter
16-
import io.element.android.features.rageshake.api.detection.RageshakeDetectionPresenter
15+
import io.element.android.features.rageshake.api.crash.CrashDetectionState
16+
import io.element.android.features.rageshake.api.detection.RageshakeDetectionState
1717
import io.element.android.features.share.api.ShareService
1818
import io.element.android.libraries.architecture.Presenter
1919
import io.element.android.libraries.matrix.api.SdkMetadata
@@ -22,8 +22,8 @@ import io.element.android.services.apperror.api.AppErrorStateService
2222
import javax.inject.Inject
2323

2424
class RootPresenter @Inject constructor(
25-
private val crashDetectionPresenter: CrashDetectionPresenter,
26-
private val rageshakeDetectionPresenter: RageshakeDetectionPresenter,
25+
private val crashDetectionPresenter: Presenter<CrashDetectionState>,
26+
private val rageshakeDetectionPresenter: Presenter<RageshakeDetectionState>,
2727
private val appErrorStateService: AppErrorStateService,
2828
private val analyticsService: AnalyticsService,
2929
private val shareService: ShareService,

appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,11 @@ import app.cash.molecule.moleculeFlow
1212
import app.cash.turbine.test
1313
import com.google.common.truth.Truth.assertThat
1414
import io.element.android.appnav.root.RootPresenter
15-
import io.element.android.features.rageshake.impl.crash.DefaultCrashDetectionPresenter
16-
import io.element.android.features.rageshake.impl.detection.DefaultRageshakeDetectionPresenter
17-
import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePreferencesPresenter
18-
import io.element.android.features.rageshake.test.crash.FakeCrashDataStore
19-
import io.element.android.features.rageshake.test.rageshake.FakeRageShake
20-
import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore
21-
import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder
15+
import io.element.android.features.rageshake.api.crash.aCrashDetectionState
16+
import io.element.android.features.rageshake.api.detection.aRageshakeDetectionState
2217
import io.element.android.features.share.api.ShareService
2318
import io.element.android.features.share.test.FakeShareService
2419
import io.element.android.libraries.matrix.test.FakeSdkMetadata
25-
import io.element.android.libraries.matrix.test.core.aBuildMeta
2620
import io.element.android.services.analytics.test.FakeAnalyticsService
2721
import io.element.android.services.apperror.api.AppErrorState
2822
import io.element.android.services.apperror.api.AppErrorStateService
@@ -44,7 +38,6 @@ class RootPresenterTest {
4438
moleculeFlow(RecompositionMode.Immediate) {
4539
presenter.present()
4640
}.test {
47-
skipItems(1)
4841
val initialState = awaitItem()
4942
assertThat(initialState.crashDetectionState.crashDetected).isFalse()
5043
}
@@ -61,7 +54,7 @@ class RootPresenterTest {
6154
moleculeFlow(RecompositionMode.Immediate) {
6255
presenter.present()
6356
}.test {
64-
skipItems(2)
57+
skipItems(1)
6558
lambda.assertions().isCalledOnce()
6659
}
6760
}
@@ -76,8 +69,6 @@ class RootPresenterTest {
7669
moleculeFlow(RecompositionMode.Immediate) {
7770
presenter.present()
7871
}.test {
79-
skipItems(1)
80-
8172
val initialState = awaitItem()
8273
assertThat(initialState.errorState).isInstanceOf(AppErrorState.Error::class.java)
8374
val initialErrorState = initialState.errorState as AppErrorState.Error
@@ -93,25 +84,9 @@ class RootPresenterTest {
9384
appErrorService: AppErrorStateService = DefaultAppErrorStateService(),
9485
shareService: ShareService = FakeShareService {},
9586
): RootPresenter {
96-
val crashDataStore = FakeCrashDataStore()
97-
val rageshakeDataStore = FakeRageshakeDataStore()
98-
val rageshake = FakeRageShake()
99-
val screenshotHolder = FakeScreenshotHolder()
100-
val crashDetectionPresenter = DefaultCrashDetectionPresenter(
101-
buildMeta = aBuildMeta(),
102-
crashDataStore = crashDataStore
103-
)
104-
val rageshakeDetectionPresenter = DefaultRageshakeDetectionPresenter(
105-
screenshotHolder = screenshotHolder,
106-
rageShake = rageshake,
107-
preferencesPresenter = DefaultRageshakePreferencesPresenter(
108-
rageshake = rageshake,
109-
rageshakeDataStore = rageshakeDataStore,
110-
)
111-
)
11287
return RootPresenter(
113-
crashDetectionPresenter = crashDetectionPresenter,
114-
rageshakeDetectionPresenter = rageshakeDetectionPresenter,
88+
crashDetectionPresenter = { aCrashDetectionState() },
89+
rageshakeDetectionPresenter = { aRageshakeDetectionState() },
11590
appErrorStateService = appErrorService,
11691
analyticsService = FakeAnalyticsService(),
11792
shareService = shareService,

features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesPresenter.kt

Lines changed: 0 additions & 12 deletions
This file was deleted.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2024 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only
5+
* Please see LICENSE in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.analytics.impl.di
9+
10+
import com.squareup.anvil.annotations.ContributesTo
11+
import dagger.Binds
12+
import dagger.Module
13+
import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesState
14+
import io.element.android.features.analytics.impl.preferences.AnalyticsPreferencesPresenter
15+
import io.element.android.libraries.architecture.Presenter
16+
import io.element.android.libraries.di.AppScope
17+
18+
@ContributesTo(AppScope::class)
19+
@Module
20+
interface AnalyticsModule {
21+
@Binds
22+
fun bindAnalyticsPreferencesPresenter(presenter: AnalyticsPreferencesPresenter): Presenter<AnalyticsPreferencesState>
23+
}

features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/DefaultAnalyticsPreferencesPresenter.kt renamed to features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenter.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,20 @@ package io.element.android.features.analytics.impl.preferences
1010
import androidx.compose.runtime.Composable
1111
import androidx.compose.runtime.collectAsState
1212
import androidx.compose.runtime.rememberCoroutineScope
13-
import com.squareup.anvil.annotations.ContributesBinding
1413
import io.element.android.appconfig.AnalyticsConfig
1514
import io.element.android.features.analytics.api.AnalyticsOptInEvents
16-
import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesPresenter
1715
import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesState
16+
import io.element.android.libraries.architecture.Presenter
1817
import io.element.android.libraries.core.meta.BuildMeta
19-
import io.element.android.libraries.di.AppScope
2018
import io.element.android.services.analytics.api.AnalyticsService
2119
import kotlinx.coroutines.CoroutineScope
2220
import kotlinx.coroutines.launch
2321
import javax.inject.Inject
2422

25-
@ContributesBinding(AppScope::class)
26-
class DefaultAnalyticsPreferencesPresenter @Inject constructor(
23+
class AnalyticsPreferencesPresenter @Inject constructor(
2724
private val analyticsService: AnalyticsService,
2825
private val buildMeta: BuildMeta,
29-
) : AnalyticsPreferencesPresenter {
26+
) : Presenter<AnalyticsPreferencesState> {
3027
@Composable
3128
override fun present(): AnalyticsPreferencesState {
3229
val localCoroutineScope = rememberCoroutineScope()

features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class AnalyticsPreferencesPresenterTest {
2525

2626
@Test
2727
fun `present - initial state available`() = runTest {
28-
val presenter = DefaultAnalyticsPreferencesPresenter(
28+
val presenter = AnalyticsPreferencesPresenter(
2929
FakeAnalyticsService(isEnabled = true, didAskUserConsent = true),
3030
aBuildMeta()
3131
)
@@ -41,7 +41,7 @@ class AnalyticsPreferencesPresenterTest {
4141

4242
@Test
4343
fun `present - initial state not available`() = runTest {
44-
val presenter = DefaultAnalyticsPreferencesPresenter(
44+
val presenter = AnalyticsPreferencesPresenter(
4545
FakeAnalyticsService(isEnabled = false, didAskUserConsent = false),
4646
aBuildMeta()
4747
)
@@ -55,7 +55,7 @@ class AnalyticsPreferencesPresenterTest {
5555

5656
@Test
5757
fun `present - enable and disable`() = runTest {
58-
val presenter = DefaultAnalyticsPreferencesPresenter(
58+
val presenter = AnalyticsPreferencesPresenter(
5959
FakeAnalyticsService(isEnabled = true, didAskUserConsent = true),
6060
aBuildMeta()
6161
)

features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueServiceTest.kt

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,19 @@ import io.element.android.features.ftue.impl.state.DefaultFtueService
1515
import io.element.android.features.ftue.impl.state.FtueStep
1616
import io.element.android.features.lockscreen.api.LockScreenService
1717
import io.element.android.features.lockscreen.test.FakeLockScreenService
18+
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
1819
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
1920
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
21+
import io.element.android.libraries.permissions.api.PermissionStateProvider
2022
import io.element.android.libraries.permissions.impl.FakePermissionStateProvider
23+
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
2124
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
2225
import io.element.android.services.analytics.api.AnalyticsService
2326
import io.element.android.services.analytics.test.FakeAnalyticsService
2427
import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider
2528
import io.element.android.tests.testutils.lambda.lambdaRecorder
2629
import io.element.android.tests.testutils.lambda.value
27-
import kotlinx.coroutines.CoroutineScope
28-
import kotlinx.coroutines.SupervisorJob
29-
import kotlinx.coroutines.cancel
30+
import kotlinx.coroutines.test.TestScope
3031
import kotlinx.coroutines.test.runTest
3132
import org.junit.Test
3233

@@ -36,8 +37,9 @@ class DefaultFtueServiceTest {
3637
val sessionVerificationService = FakeSessionVerificationService().apply {
3738
givenVerifiedStatus(SessionVerifiedStatus.Unknown)
3839
}
39-
val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob())
40-
val service = createDefaultFtueService(coroutineScope, sessionVerificationService)
40+
val service = createDefaultFtueService(
41+
sessionVerificationService = sessionVerificationService,
42+
)
4143

4244
service.state.test {
4345
// Verification state is unknown, we don't display the flow yet
@@ -47,9 +49,6 @@ class DefaultFtueServiceTest {
4749
sessionVerificationService.givenVerifiedStatus(SessionVerifiedStatus.NotVerified)
4850
assertThat(awaitItem()).isEqualTo(FtueState.Incomplete)
4951
}
50-
51-
// Cleanup
52-
coroutineScope.cancel()
5352
}
5453

5554
@Test
@@ -58,10 +57,7 @@ class DefaultFtueServiceTest {
5857
val sessionVerificationService = FakeSessionVerificationService()
5958
val permissionStateProvider = FakePermissionStateProvider(permissionGranted = true)
6059
val lockScreenService = FakeLockScreenService()
61-
val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob())
62-
6360
val service = createDefaultFtueService(
64-
coroutineScope = coroutineScope,
6561
sessionVerificationService = sessionVerificationService,
6662
analyticsService = analyticsService,
6763
permissionStateProvider = permissionStateProvider,
@@ -75,9 +71,6 @@ class DefaultFtueServiceTest {
7571
service.updateState()
7672

7773
assertThat(service.state.value).isEqualTo(FtueState.Complete)
78-
79-
// Cleanup
80-
coroutineScope.cancel()
8174
}
8275

8376
@Test
@@ -88,10 +81,7 @@ class DefaultFtueServiceTest {
8881
val analyticsService = FakeAnalyticsService()
8982
val permissionStateProvider = FakePermissionStateProvider(permissionGranted = false)
9083
val lockScreenService = FakeLockScreenService()
91-
val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob())
92-
9384
val service = createDefaultFtueService(
94-
coroutineScope = coroutineScope,
9585
sessionVerificationService = sessionVerificationService,
9686
analyticsService = analyticsService,
9787
permissionStateProvider = permissionStateProvider,
@@ -126,20 +116,15 @@ class DefaultFtueServiceTest {
126116
// Final state
127117
null,
128118
)
129-
130-
// Cleanup
131-
coroutineScope.cancel()
132119
}
133120

134121
@Test
135122
fun `if a check for a step is true, start from the next one`() = runTest {
136-
val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob())
137123
val sessionVerificationService = FakeSessionVerificationService()
138124
val analyticsService = FakeAnalyticsService()
139125
val permissionStateProvider = FakePermissionStateProvider(permissionGranted = false)
140126
val lockScreenService = FakeLockScreenService()
141127
val service = createDefaultFtueService(
142-
coroutineScope = coroutineScope,
143128
sessionVerificationService = sessionVerificationService,
144129
analyticsService = analyticsService,
145130
permissionStateProvider = permissionStateProvider,
@@ -155,22 +140,17 @@ class DefaultFtueServiceTest {
155140

156141
analyticsService.setDidAskUserConsent()
157142
assertThat(service.getNextStep(null)).isNull()
158-
159-
// Cleanup
160-
coroutineScope.cancel()
161143
}
162144

163145
@Test
164146
fun `if version is older than 13 we don't display the notification opt in screen`() = runTest {
165-
val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob())
166147
val sessionVerificationService = FakeSessionVerificationService()
167148
val analyticsService = FakeAnalyticsService()
168149
val lockScreenService = FakeLockScreenService()
169150

170151
val service = createDefaultFtueService(
171152
sdkIntVersion = Build.VERSION_CODES.M,
172153
sessionVerificationService = sessionVerificationService,
173-
coroutineScope = coroutineScope,
174154
analyticsService = analyticsService,
175155
lockScreenService = lockScreenService,
176156
)
@@ -182,14 +162,10 @@ class DefaultFtueServiceTest {
182162

183163
analyticsService.setDidAskUserConsent()
184164
assertThat(service.getNextStep(null)).isNull()
185-
186-
// Cleanup
187-
coroutineScope.cancel()
188165
}
189166

190167
@Test
191168
fun `reset do the expected actions S`() = runTest {
192-
val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob())
193169
val resetAnalyticsLambda = lambdaRecorder<Unit> { }
194170
val analyticsService = FakeAnalyticsService(
195171
resetLambda = resetAnalyticsLambda
@@ -199,7 +175,6 @@ class DefaultFtueServiceTest {
199175
resetPermissionLambda = resetPermissionLambda
200176
)
201177
val service = createDefaultFtueService(
202-
coroutineScope = coroutineScope,
203178
sdkIntVersion = Build.VERSION_CODES.S,
204179
permissionStateProvider = permissionStateProvider,
205180
analyticsService = analyticsService,
@@ -211,7 +186,6 @@ class DefaultFtueServiceTest {
211186

212187
@Test
213188
fun `reset do the expected actions TIRAMISU`() = runTest {
214-
val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob())
215189
val resetLambda = lambdaRecorder<Unit> { }
216190
val analyticsService = FakeAnalyticsService(
217191
resetLambda = resetLambda
@@ -221,7 +195,6 @@ class DefaultFtueServiceTest {
221195
resetPermissionLambda = resetPermissionLambda
222196
)
223197
val service = createDefaultFtueService(
224-
coroutineScope = coroutineScope,
225198
sdkIntVersion = Build.VERSION_CODES.TIRAMISU,
226199
permissionStateProvider = permissionStateProvider,
227200
analyticsService = analyticsService,
@@ -232,17 +205,16 @@ class DefaultFtueServiceTest {
232205
.with(value("android.permission.POST_NOTIFICATIONS"))
233206
}
234207

235-
private fun createDefaultFtueService(
236-
coroutineScope: CoroutineScope,
237-
sessionVerificationService: FakeSessionVerificationService = FakeSessionVerificationService(),
208+
private fun TestScope.createDefaultFtueService(
209+
sessionVerificationService: SessionVerificationService = FakeSessionVerificationService(),
238210
analyticsService: AnalyticsService = FakeAnalyticsService(),
239-
permissionStateProvider: FakePermissionStateProvider = FakePermissionStateProvider(permissionGranted = false),
211+
permissionStateProvider: PermissionStateProvider = FakePermissionStateProvider(permissionGranted = false),
240212
lockScreenService: LockScreenService = FakeLockScreenService(),
241-
sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(),
213+
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),
242214
// First version where notification permission is required
243215
sdkIntVersion: Int = Build.VERSION_CODES.TIRAMISU,
244216
) = DefaultFtueService(
245-
sessionCoroutineScope = coroutineScope,
217+
sessionCoroutineScope = backgroundScope,
246218
sessionVerificationService = sessionVerificationService,
247219
sdkVersionProvider = FakeBuildVersionSdkIntProvider(sdkIntVersion),
248220
analyticsService = analyticsService,

features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomPresenter.kt

Lines changed: 0 additions & 16 deletions
This file was deleted.

features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomStateProvider.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,10 @@ fun aLeaveRoomState(
5757
confirmation: LeaveRoomState.Confirmation = LeaveRoomState.Confirmation.Hidden,
5858
progress: LeaveRoomState.Progress = LeaveRoomState.Progress.Hidden,
5959
error: LeaveRoomState.Error = LeaveRoomState.Error.Hidden,
60+
eventSink: (LeaveRoomEvent) -> Unit = {},
6061
) = LeaveRoomState(
6162
confirmation = confirmation,
6263
progress = progress,
6364
error = error,
64-
eventSink = {},
65+
eventSink = eventSink,
6566
)

0 commit comments

Comments
 (0)