Merge pull request #5160 from element-hq/feature/bma/cleanupFeatureFlags
Remove old feature flags
This commit is contained in:
commit
0a4a4e20db
77 changed files with 310 additions and 1243 deletions
|
|
@ -147,14 +147,6 @@
|
||||||
<data android:host="user" />
|
<data android:host="user" />
|
||||||
<data android:host="room" />
|
<data android:host="room" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
|
||||||
|
|
||||||
<!-- Using an activity-alias for incoming share intent, in order
|
|
||||||
to be able to disable the feature programmatically -->
|
|
||||||
<activity-alias
|
|
||||||
android:name=".ShareActivity"
|
|
||||||
android:exported="true"
|
|
||||||
android:targetActivity=".MainActivity">
|
|
||||||
<!-- Incoming share simple -->
|
<!-- Incoming share simple -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
@ -171,7 +163,7 @@
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.OPENABLE" />
|
<category android:name="android.intent.category.OPENABLE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity-alias>
|
</activity>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,6 @@
|
||||||
package io.element.android.appconfig
|
package io.element.android.appconfig
|
||||||
|
|
||||||
object OnBoardingConfig {
|
object OnBoardingConfig {
|
||||||
/** Whether the user can use QR code login. */
|
|
||||||
const val CAN_LOGIN_WITH_QR_CODE = true
|
|
||||||
|
|
||||||
/** Whether the user can create an account using the app. */
|
/** Whether the user can create an account using the app. */
|
||||||
const val CAN_CREATE_ACCOUNT = true
|
const val CAN_CREATE_ACCOUNT = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ dependencies {
|
||||||
testImplementation(projects.features.networkmonitor.test)
|
testImplementation(projects.features.networkmonitor.test)
|
||||||
testImplementation(projects.tests.testutils)
|
testImplementation(projects.tests.testutils)
|
||||||
testImplementation(projects.features.rageshake.test)
|
testImplementation(projects.features.rageshake.test)
|
||||||
testImplementation(projects.features.share.test)
|
|
||||||
testImplementation(projects.services.appnavstate.test)
|
testImplementation(projects.services.appnavstate.test)
|
||||||
testImplementation(projects.services.analytics.test)
|
testImplementation(projects.services.analytics.test)
|
||||||
testImplementation(libs.test.appyx.junit)
|
testImplementation(libs.test.appyx.junit)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import androidx.compose.runtime.getValue
|
||||||
import im.vector.app.features.analytics.plan.SuperProperties
|
import im.vector.app.features.analytics.plan.SuperProperties
|
||||||
import io.element.android.features.rageshake.api.crash.CrashDetectionState
|
import io.element.android.features.rageshake.api.crash.CrashDetectionState
|
||||||
import io.element.android.features.rageshake.api.detection.RageshakeDetectionState
|
import io.element.android.features.rageshake.api.detection.RageshakeDetectionState
|
||||||
import io.element.android.features.share.api.ShareService
|
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.matrix.api.SdkMetadata
|
import io.element.android.libraries.matrix.api.SdkMetadata
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
|
|
@ -26,7 +25,6 @@ class RootPresenter @Inject constructor(
|
||||||
private val rageshakeDetectionPresenter: Presenter<RageshakeDetectionState>,
|
private val rageshakeDetectionPresenter: Presenter<RageshakeDetectionState>,
|
||||||
private val appErrorStateService: AppErrorStateService,
|
private val appErrorStateService: AppErrorStateService,
|
||||||
private val analyticsService: AnalyticsService,
|
private val analyticsService: AnalyticsService,
|
||||||
private val shareService: ShareService,
|
|
||||||
private val sdkMetadata: SdkMetadata,
|
private val sdkMetadata: SdkMetadata,
|
||||||
) : Presenter<RootState> {
|
) : Presenter<RootState> {
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -45,10 +43,6 @@ class RootPresenter @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
shareService.observeFeatureFlag(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
return RootState(
|
return RootState(
|
||||||
rageshakeDetectionState = rageshakeDetectionState,
|
rageshakeDetectionState = rageshakeDetectionState,
|
||||||
crashDetectionState = crashDetectionState,
|
crashDetectionState = crashDetectionState,
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,12 @@ import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.appnav.root.RootPresenter
|
import io.element.android.appnav.root.RootPresenter
|
||||||
import io.element.android.features.rageshake.api.crash.aCrashDetectionState
|
import io.element.android.features.rageshake.api.crash.aCrashDetectionState
|
||||||
import io.element.android.features.rageshake.api.detection.aRageshakeDetectionState
|
import io.element.android.features.rageshake.api.detection.aRageshakeDetectionState
|
||||||
import io.element.android.features.share.api.ShareService
|
|
||||||
import io.element.android.features.share.test.FakeShareService
|
|
||||||
import io.element.android.libraries.matrix.test.FakeSdkMetadata
|
import io.element.android.libraries.matrix.test.FakeSdkMetadata
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.services.apperror.api.AppErrorState
|
import io.element.android.services.apperror.api.AppErrorState
|
||||||
import io.element.android.services.apperror.api.AppErrorStateService
|
import io.element.android.services.apperror.api.AppErrorStateService
|
||||||
import io.element.android.services.apperror.impl.DefaultAppErrorStateService
|
import io.element.android.services.apperror.impl.DefaultAppErrorStateService
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
@ -43,22 +39,6 @@ class RootPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - check that share service is invoked`() = runTest {
|
|
||||||
val lambda = lambdaRecorder<CoroutineScope, Unit> { _ -> }
|
|
||||||
val presenter = createRootPresenter(
|
|
||||||
shareService = FakeShareService {
|
|
||||||
lambda(it)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
skipItems(1)
|
|
||||||
lambda.assertions().isCalledOnce()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - passes app error state`() = runTest {
|
fun `present - passes app error state`() = runTest {
|
||||||
val presenter = createRootPresenter(
|
val presenter = createRootPresenter(
|
||||||
|
|
@ -82,14 +62,12 @@ class RootPresenterTest {
|
||||||
|
|
||||||
private fun createRootPresenter(
|
private fun createRootPresenter(
|
||||||
appErrorService: AppErrorStateService = DefaultAppErrorStateService(),
|
appErrorService: AppErrorStateService = DefaultAppErrorStateService(),
|
||||||
shareService: ShareService = FakeShareService {},
|
|
||||||
): RootPresenter {
|
): RootPresenter {
|
||||||
return RootPresenter(
|
return RootPresenter(
|
||||||
crashDetectionPresenter = { aCrashDetectionState() },
|
crashDetectionPresenter = { aCrashDetectionState() },
|
||||||
rageshakeDetectionPresenter = { aRageshakeDetectionState() },
|
rageshakeDetectionPresenter = { aRageshakeDetectionState() },
|
||||||
appErrorStateService = appErrorService,
|
appErrorStateService = appErrorService,
|
||||||
analyticsService = FakeAnalyticsService(),
|
analyticsService = FakeAnalyticsService(),
|
||||||
shareService = shareService,
|
|
||||||
sdkMetadata = FakeSdkMetadata("sha")
|
sdkMetadata = FakeSdkMetadata("sha")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,36 +101,34 @@ private fun RoomListModalBottomSheetContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if (contextMenu.markAsUnreadFeatureFlagEnabled) {
|
if (contextMenu.hasNewContent) {
|
||||||
if (contextMenu.hasNewContent) {
|
ListItem(
|
||||||
ListItem(
|
headlineContent = {
|
||||||
headlineContent = {
|
Text(
|
||||||
Text(
|
text = stringResource(id = R.string.screen_roomlist_mark_as_read),
|
||||||
text = stringResource(id = R.string.screen_roomlist_mark_as_read),
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
)
|
||||||
)
|
},
|
||||||
},
|
onClick = onRoomMarkReadClick,
|
||||||
onClick = onRoomMarkReadClick,
|
leadingContent = ListItemContent.Icon(
|
||||||
leadingContent = ListItemContent.Icon(
|
iconSource = IconSource.Vector(CompoundIcons.MarkAsRead())
|
||||||
iconSource = IconSource.Vector(CompoundIcons.MarkAsRead())
|
),
|
||||||
),
|
style = ListItemStyle.Primary,
|
||||||
style = ListItemStyle.Primary,
|
)
|
||||||
)
|
} else {
|
||||||
} else {
|
ListItem(
|
||||||
ListItem(
|
headlineContent = {
|
||||||
headlineContent = {
|
Text(
|
||||||
Text(
|
text = stringResource(id = R.string.screen_roomlist_mark_as_unread),
|
||||||
text = stringResource(id = R.string.screen_roomlist_mark_as_unread),
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
)
|
||||||
)
|
},
|
||||||
},
|
onClick = onRoomMarkUnreadClick,
|
||||||
onClick = onRoomMarkUnreadClick,
|
leadingContent = ListItemContent.Icon(
|
||||||
leadingContent = ListItemContent.Icon(
|
iconSource = IconSource.Vector(CompoundIcons.MarkAsUnread())
|
||||||
iconSource = IconSource.Vector(CompoundIcons.MarkAsUnread())
|
),
|
||||||
),
|
style = ListItemStyle.Primary,
|
||||||
style = ListItemStyle.Primary,
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,6 @@ import io.element.android.features.leaveroom.api.LeaveRoomState
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.coroutine.mapState
|
import io.element.android.libraries.core.coroutine.mapState
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
|
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -74,7 +72,6 @@ class RoomListPresenter @Inject constructor(
|
||||||
private val client: MatrixClient,
|
private val client: MatrixClient,
|
||||||
private val leaveRoomPresenter: Presenter<LeaveRoomState>,
|
private val leaveRoomPresenter: Presenter<LeaveRoomState>,
|
||||||
private val roomListDataSource: RoomListDataSource,
|
private val roomListDataSource: RoomListDataSource,
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
private val filtersPresenter: Presenter<RoomListFiltersState>,
|
private val filtersPresenter: Presenter<RoomListFiltersState>,
|
||||||
private val searchPresenter: Presenter<RoomListSearchState>,
|
private val searchPresenter: Presenter<RoomListSearchState>,
|
||||||
private val sessionPreferencesStore: SessionPreferencesStore,
|
private val sessionPreferencesStore: SessionPreferencesStore,
|
||||||
|
|
@ -244,7 +241,6 @@ class RoomListPresenter @Inject constructor(
|
||||||
roomName = event.roomSummary.name,
|
roomName = event.roomSummary.name,
|
||||||
isDm = event.roomSummary.isDm,
|
isDm = event.roomSummary.isDm,
|
||||||
isFavorite = event.roomSummary.isFavorite,
|
isFavorite = event.roomSummary.isFavorite,
|
||||||
markAsUnreadFeatureFlagEnabled = featureFlagService.isFeatureEnabled(FeatureFlags.MarkAsUnread),
|
|
||||||
hasNewContent = event.roomSummary.hasNewContent,
|
hasNewContent = event.roomSummary.hasNewContent,
|
||||||
displayClearRoomCacheAction = appPreferencesStore.isDeveloperModeEnabledFlow().first(),
|
displayClearRoomCacheAction = appPreferencesStore.isDeveloperModeEnabledFlow().first(),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,6 @@ data class RoomListState(
|
||||||
val roomName: String?,
|
val roomName: String?,
|
||||||
val isDm: Boolean,
|
val isDm: Boolean,
|
||||||
val isFavorite: Boolean,
|
val isFavorite: Boolean,
|
||||||
val markAsUnreadFeatureFlagEnabled: Boolean,
|
|
||||||
val hasNewContent: Boolean,
|
val hasNewContent: Boolean,
|
||||||
val displayClearRoomCacheAction: Boolean,
|
val displayClearRoomCacheAction: Boolean,
|
||||||
) : ContextMenu
|
) : ContextMenu
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ internal fun aContextMenuShown(
|
||||||
roomId = RoomId("!aRoom:aDomain"),
|
roomId = RoomId("!aRoom:aDomain"),
|
||||||
roomName = roomName,
|
roomName = roomName,
|
||||||
isDm = isDm,
|
isDm = isDm,
|
||||||
markAsUnreadFeatureFlagEnabled = true,
|
|
||||||
hasNewContent = hasNewContent,
|
hasNewContent = hasNewContent,
|
||||||
isFavorite = isFavorite,
|
isFavorite = isFavorite,
|
||||||
displayClearRoomCacheAction = false,
|
displayClearRoomCacheAction = false,
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,6 @@ import io.element.android.libraries.dateformatter.api.DateFormatter
|
||||||
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
||||||
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
|
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
|
||||||
import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter
|
import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
|
||||||
import io.element.android.libraries.fullscreenintent.api.aFullScreenIntentPermissionsState
|
import io.element.android.libraries.fullscreenintent.api.aFullScreenIntentPermissionsState
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -217,7 +215,6 @@ class RoomListPresenterTest {
|
||||||
roomName = summary.name,
|
roomName = summary.name,
|
||||||
isDm = false,
|
isDm = false,
|
||||||
isFavorite = false,
|
isFavorite = false,
|
||||||
markAsUnreadFeatureFlagEnabled = true,
|
|
||||||
hasNewContent = false,
|
hasNewContent = false,
|
||||||
displayClearRoomCacheAction = false,
|
displayClearRoomCacheAction = false,
|
||||||
)
|
)
|
||||||
|
|
@ -235,7 +232,6 @@ class RoomListPresenterTest {
|
||||||
roomName = summary.name,
|
roomName = summary.name,
|
||||||
isDm = false,
|
isDm = false,
|
||||||
isFavorite = true,
|
isFavorite = true,
|
||||||
markAsUnreadFeatureFlagEnabled = true,
|
|
||||||
hasNewContent = false,
|
hasNewContent = false,
|
||||||
displayClearRoomCacheAction = false,
|
displayClearRoomCacheAction = false,
|
||||||
)
|
)
|
||||||
|
|
@ -263,7 +259,6 @@ class RoomListPresenterTest {
|
||||||
roomName = summary.name,
|
roomName = summary.name,
|
||||||
isDm = false,
|
isDm = false,
|
||||||
isFavorite = false,
|
isFavorite = false,
|
||||||
markAsUnreadFeatureFlagEnabled = true,
|
|
||||||
// true here.
|
// true here.
|
||||||
hasNewContent = false,
|
hasNewContent = false,
|
||||||
displayClearRoomCacheAction = true,
|
displayClearRoomCacheAction = true,
|
||||||
|
|
@ -295,7 +290,6 @@ class RoomListPresenterTest {
|
||||||
roomName = summary.name,
|
roomName = summary.name,
|
||||||
isDm = false,
|
isDm = false,
|
||||||
isFavorite = false,
|
isFavorite = false,
|
||||||
markAsUnreadFeatureFlagEnabled = true,
|
|
||||||
hasNewContent = false,
|
hasNewContent = false,
|
||||||
displayClearRoomCacheAction = false,
|
displayClearRoomCacheAction = false,
|
||||||
)
|
)
|
||||||
|
|
@ -604,7 +598,6 @@ class RoomListPresenterTest {
|
||||||
dateFormatter: DateFormatter = FakeDateFormatter(),
|
dateFormatter: DateFormatter = FakeDateFormatter(),
|
||||||
roomLastMessageFormatter: RoomLastMessageFormatter = FakeRoomLastMessageFormatter(),
|
roomLastMessageFormatter: RoomLastMessageFormatter = FakeRoomLastMessageFormatter(),
|
||||||
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),
|
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),
|
||||||
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
|
|
||||||
analyticsService: AnalyticsService = FakeAnalyticsService(),
|
analyticsService: AnalyticsService = FakeAnalyticsService(),
|
||||||
filtersPresenter: Presenter<RoomListFiltersState> = Presenter { aRoomListFiltersState() },
|
filtersPresenter: Presenter<RoomListFiltersState> = Presenter { aRoomListFiltersState() },
|
||||||
searchPresenter: Presenter<RoomListSearchState> = Presenter { aRoomListSearchState() },
|
searchPresenter: Presenter<RoomListSearchState> = Presenter { aRoomListSearchState() },
|
||||||
|
|
@ -626,7 +619,6 @@ class RoomListPresenterTest {
|
||||||
sessionCoroutineScope = backgroundScope,
|
sessionCoroutineScope = backgroundScope,
|
||||||
dateTimeObserver = FakeDateTimeObserver(),
|
dateTimeObserver = FakeDateTimeObserver(),
|
||||||
),
|
),
|
||||||
featureFlagService = featureFlagService,
|
|
||||||
searchPresenter = searchPresenter,
|
searchPresenter = searchPresenter,
|
||||||
sessionPreferencesStore = sessionPreferencesStore,
|
sessionPreferencesStore = sessionPreferencesStore,
|
||||||
filtersPresenter = filtersPresenter,
|
filtersPresenter = filtersPresenter,
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ internal class AndroidLocationActionsTest {
|
||||||
accuracy = 0f
|
accuracy = 0f
|
||||||
)
|
)
|
||||||
// Set a locale with comma as decimal separator
|
// Set a locale with comma as decimal separator
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
Locale.setDefault(Locale.Category.FORMAT, Locale("pt", "BR"))
|
Locale.setDefault(Locale.Category.FORMAT, Locale("pt", "BR"))
|
||||||
|
|
||||||
val actual = buildUrl(location, "(weird/stuff here)", ::urlEncoder)
|
val actual = buildUrl(location, "(weird/stuff here)", ::urlEncoder)
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,6 @@ import io.element.android.features.lockscreen.impl.storage.LockScreenStore
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.di.SingleIn
|
import io.element.android.libraries.di.SingleIn
|
||||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.sessionstorage.api.observer.SessionListener
|
import io.element.android.libraries.sessionstorage.api.observer.SessionListener
|
||||||
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
|
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
|
||||||
import io.element.android.services.appnavstate.api.AppForegroundStateService
|
import io.element.android.services.appnavstate.api.AppForegroundStateService
|
||||||
|
|
@ -29,7 +27,6 @@ import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -40,7 +37,6 @@ import kotlin.time.Duration
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class DefaultLockScreenService @Inject constructor(
|
class DefaultLockScreenService @Inject constructor(
|
||||||
private val lockScreenConfig: LockScreenConfig,
|
private val lockScreenConfig: LockScreenConfig,
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
private val lockScreenStore: LockScreenStore,
|
private val lockScreenStore: LockScreenStore,
|
||||||
private val pinCodeManager: PinCodeManager,
|
private val pinCodeManager: PinCodeManager,
|
||||||
@AppCoroutineScope
|
@AppCoroutineScope
|
||||||
|
|
@ -108,12 +104,7 @@ class DefaultLockScreenService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isPinSetup(): Flow<Boolean> {
|
override fun isPinSetup(): Flow<Boolean> {
|
||||||
return combine(
|
return pinCodeManager.hasPinCode()
|
||||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.PinUnlock),
|
|
||||||
pinCodeManager.hasPinCode()
|
|
||||||
) { isEnabled, hasPinCode ->
|
|
||||||
isEnabled && hasPinCode
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isSetupRequired(): Flow<Boolean> {
|
override fun isSetupRequired(): Flow<Boolean> {
|
||||||
|
|
|
||||||
|
|
@ -27,14 +27,11 @@ import io.element.android.features.login.impl.login.LoginHelper
|
||||||
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.ui.utils.MultipleTapToUnlock
|
import io.element.android.libraries.ui.utils.MultipleTapToUnlock
|
||||||
|
|
||||||
class OnBoardingPresenter @AssistedInject constructor(
|
class OnBoardingPresenter @AssistedInject constructor(
|
||||||
@Assisted private val params: OnBoardingNode.Params,
|
@Assisted private val params: OnBoardingNode.Params,
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
private val enterpriseService: EnterpriseService,
|
private val enterpriseService: EnterpriseService,
|
||||||
private val defaultAccountProviderAccessControl: DefaultAccountProviderAccessControl,
|
private val defaultAccountProviderAccessControl: DefaultAccountProviderAccessControl,
|
||||||
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
|
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
|
||||||
|
|
@ -80,8 +77,7 @@ class OnBoardingPresenter @AssistedInject constructor(
|
||||||
forcedAccountProvider ?: linkAccountProvider
|
forcedAccountProvider ?: linkAccountProvider
|
||||||
}
|
}
|
||||||
val canLoginWithQrCode by produceState(initialValue = false, linkAccountProvider) {
|
val canLoginWithQrCode by produceState(initialValue = false, linkAccountProvider) {
|
||||||
value = linkAccountProvider == null &&
|
value = linkAccountProvider == null
|
||||||
featureFlagService.isFeatureEnabled(FeatureFlags.QrCodeLogin)
|
|
||||||
}
|
}
|
||||||
val canReportBug by remember { rageshakeFeatureAvailability.isAvailable() }.collectAsState(false)
|
val canReportBug by remember { rageshakeFeatureAvailability.isAvailable() }.collectAsState(false)
|
||||||
var showReportBug by rememberSaveable { mutableStateOf(false) }
|
var showReportBug by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,6 @@ import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationR
|
||||||
import io.element.android.features.wellknown.test.FakeWellknownRetriever
|
import io.element.android.features.wellknown.test.FakeWellknownRetriever
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||||
import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER
|
import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER
|
||||||
import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_2
|
import io.element.android.libraries.matrix.test.AN_ACCOUNT_PROVIDER_2
|
||||||
|
|
@ -70,13 +67,8 @@ class OnBoardingPresenterTest {
|
||||||
productionApplicationName = "B",
|
productionApplicationName = "B",
|
||||||
desktopApplicationName = "C",
|
desktopApplicationName = "C",
|
||||||
)
|
)
|
||||||
val featureFlagService = FakeFeatureFlagService(
|
|
||||||
initialState = mapOf(FeatureFlags.QrCodeLogin.key to true),
|
|
||||||
buildMeta = buildMeta,
|
|
||||||
)
|
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
buildMeta = buildMeta,
|
buildMeta = buildMeta,
|
||||||
featureFlagService = featureFlagService,
|
|
||||||
enterpriseService = FakeEnterpriseService(
|
enterpriseService = FakeEnterpriseService(
|
||||||
defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG, EnterpriseService.ANY_ACCOUNT_PROVIDER) },
|
defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG, EnterpriseService.ANY_ACCOUNT_PROVIDER) },
|
||||||
),
|
),
|
||||||
|
|
@ -131,9 +123,6 @@ class OnBoardingPresenterTest {
|
||||||
accountProvider = ACCOUNT_PROVIDER_FROM_LINK,
|
accountProvider = ACCOUNT_PROVIDER_FROM_LINK,
|
||||||
loginHint = null,
|
loginHint = null,
|
||||||
),
|
),
|
||||||
featureFlagService = FakeFeatureFlagService(
|
|
||||||
initialState = mapOf(FeatureFlags.QrCodeLogin.key to true),
|
|
||||||
),
|
|
||||||
enterpriseService = FakeEnterpriseService(
|
enterpriseService = FakeEnterpriseService(
|
||||||
defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG, EnterpriseService.ANY_ACCOUNT_PROVIDER) },
|
defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG, EnterpriseService.ANY_ACCOUNT_PROVIDER) },
|
||||||
isAllowedToConnectToHomeserverResult = { true },
|
isAllowedToConnectToHomeserverResult = { true },
|
||||||
|
|
@ -156,9 +145,6 @@ class OnBoardingPresenterTest {
|
||||||
accountProvider = ACCOUNT_PROVIDER_FROM_LINK,
|
accountProvider = ACCOUNT_PROVIDER_FROM_LINK,
|
||||||
loginHint = null,
|
loginHint = null,
|
||||||
),
|
),
|
||||||
featureFlagService = FakeFeatureFlagService(
|
|
||||||
initialState = mapOf(FeatureFlags.QrCodeLogin.key to true),
|
|
||||||
),
|
|
||||||
enterpriseService = FakeEnterpriseService(
|
enterpriseService = FakeEnterpriseService(
|
||||||
defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG, ACCOUNT_PROVIDER_FROM_CONFIG_2) },
|
defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG, ACCOUNT_PROVIDER_FROM_CONFIG_2) },
|
||||||
isAllowedToConnectToHomeserverResult = { false },
|
isAllowedToConnectToHomeserverResult = { false },
|
||||||
|
|
@ -181,9 +167,6 @@ class OnBoardingPresenterTest {
|
||||||
accountProvider = ACCOUNT_PROVIDER_FROM_LINK,
|
accountProvider = ACCOUNT_PROVIDER_FROM_LINK,
|
||||||
loginHint = null,
|
loginHint = null,
|
||||||
),
|
),
|
||||||
featureFlagService = FakeFeatureFlagService(
|
|
||||||
initialState = mapOf(FeatureFlags.QrCodeLogin.key to true),
|
|
||||||
),
|
|
||||||
enterpriseService = FakeEnterpriseService(
|
enterpriseService = FakeEnterpriseService(
|
||||||
defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG) },
|
defaultHomeserverListResult = { listOf(ACCOUNT_PROVIDER_FROM_CONFIG) },
|
||||||
)
|
)
|
||||||
|
|
@ -237,7 +220,6 @@ class OnBoardingPresenterTest {
|
||||||
private fun createPresenter(
|
private fun createPresenter(
|
||||||
params: OnBoardingNode.Params = OnBoardingNode.Params(null, null),
|
params: OnBoardingNode.Params = OnBoardingNode.Params(null, null),
|
||||||
buildMeta: BuildMeta = aBuildMeta(),
|
buildMeta: BuildMeta = aBuildMeta(),
|
||||||
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
|
|
||||||
enterpriseService: EnterpriseService = FakeEnterpriseService(),
|
enterpriseService: EnterpriseService = FakeEnterpriseService(),
|
||||||
wellknownRetriever: WellknownRetriever = FakeWellknownRetriever(),
|
wellknownRetriever: WellknownRetriever = FakeWellknownRetriever(),
|
||||||
rageshakeFeatureAvailability: () -> Flow<Boolean> = { flowOf(true) },
|
rageshakeFeatureAvailability: () -> Flow<Boolean> = { flowOf(true) },
|
||||||
|
|
@ -245,7 +227,6 @@ private fun createPresenter(
|
||||||
) = OnBoardingPresenter(
|
) = OnBoardingPresenter(
|
||||||
params = params,
|
params = params,
|
||||||
buildMeta = buildMeta,
|
buildMeta = buildMeta,
|
||||||
featureFlagService = featureFlagService,
|
|
||||||
enterpriseService = enterpriseService,
|
enterpriseService = enterpriseService,
|
||||||
defaultAccountProviderAccessControl = DefaultAccountProviderAccessControl(
|
defaultAccountProviderAccessControl = DefaultAccountProviderAccessControl(
|
||||||
enterpriseService = enterpriseService,
|
enterpriseService = enterpriseService,
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.messages.api.pinned
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
|
|
||||||
fun interface IsPinnedMessagesFeatureEnabled {
|
|
||||||
@Composable
|
|
||||||
operator fun invoke(): Boolean
|
|
||||||
}
|
|
||||||
|
|
@ -63,8 +63,6 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
|
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
||||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||||
|
|
@ -111,7 +109,6 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
private val snackbarDispatcher: SnackbarDispatcher,
|
private val snackbarDispatcher: SnackbarDispatcher,
|
||||||
private val dispatchers: CoroutineDispatchers,
|
private val dispatchers: CoroutineDispatchers,
|
||||||
private val clipboardHelper: ClipboardHelper,
|
private val clipboardHelper: ClipboardHelper,
|
||||||
private val featureFlagsService: FeatureFlagService,
|
|
||||||
private val htmlConverterProvider: HtmlConverterProvider,
|
private val htmlConverterProvider: HtmlConverterProvider,
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
private val timelineController: TimelineController,
|
private val timelineController: TimelineController,
|
||||||
|
|
@ -187,11 +184,6 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
||||||
|
|
||||||
var enableVoiceMessages by remember { mutableStateOf(false) }
|
|
||||||
LaunchedEffect(featureFlagsService) {
|
|
||||||
enableVoiceMessages = featureFlagsService.isFeatureEnabled(FeatureFlags.VoiceMessages)
|
|
||||||
}
|
|
||||||
|
|
||||||
var dmUserVerificationState by remember { mutableStateOf<IdentityState?>(null) }
|
var dmUserVerificationState by remember { mutableStateOf<IdentityState?>(null) }
|
||||||
|
|
||||||
val membersState by room.membersStateFlow.collectAsState()
|
val membersState by room.membersStateFlow.collectAsState()
|
||||||
|
|
@ -261,7 +253,6 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
showReinvitePrompt = showReinvitePrompt,
|
showReinvitePrompt = showReinvitePrompt,
|
||||||
inviteProgress = inviteProgress.value,
|
inviteProgress = inviteProgress.value,
|
||||||
enableTextFormatting = MessageComposerConfig.ENABLE_RICH_TEXT_EDITING,
|
enableTextFormatting = MessageComposerConfig.ENABLE_RICH_TEXT_EDITING,
|
||||||
enableVoiceMessages = enableVoiceMessages,
|
|
||||||
appName = buildMeta.applicationName,
|
appName = buildMeta.applicationName,
|
||||||
roomCallState = roomCallState,
|
roomCallState = roomCallState,
|
||||||
pinnedMessagesBannerState = pinnedMessagesBannerState,
|
pinnedMessagesBannerState = pinnedMessagesBannerState,
|
||||||
|
|
@ -449,7 +440,6 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
val composerMode = MessageComposerMode.EditCaption(
|
val composerMode = MessageComposerMode.EditCaption(
|
||||||
eventOrTransactionId = targetEvent.eventOrTransactionId,
|
eventOrTransactionId = targetEvent.eventOrTransactionId,
|
||||||
content = "",
|
content = "",
|
||||||
showCaptionCompatibilityWarning = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaCaptionWarning),
|
|
||||||
)
|
)
|
||||||
composerState.eventSink(
|
composerState.eventSink(
|
||||||
MessageComposerEvents.SetMode(composerMode)
|
MessageComposerEvents.SetMode(composerMode)
|
||||||
|
|
@ -463,7 +453,6 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
val composerMode = MessageComposerMode.EditCaption(
|
val composerMode = MessageComposerMode.EditCaption(
|
||||||
eventOrTransactionId = targetEvent.eventOrTransactionId,
|
eventOrTransactionId = targetEvent.eventOrTransactionId,
|
||||||
content = (targetEvent.content as? TimelineItemEventContentWithAttachment)?.caption.orEmpty(),
|
content = (targetEvent.content as? TimelineItemEventContentWithAttachment)?.caption.orEmpty(),
|
||||||
showCaptionCompatibilityWarning = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaCaptionWarning),
|
|
||||||
)
|
)
|
||||||
composerState.eventSink(
|
composerState.eventSink(
|
||||||
MessageComposerEvents.SetMode(composerMode)
|
MessageComposerEvents.SetMode(composerMode)
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ data class MessagesState(
|
||||||
val inviteProgress: AsyncData<Unit>,
|
val inviteProgress: AsyncData<Unit>,
|
||||||
val showReinvitePrompt: Boolean,
|
val showReinvitePrompt: Boolean,
|
||||||
val enableTextFormatting: Boolean,
|
val enableTextFormatting: Boolean,
|
||||||
val enableVoiceMessages: Boolean,
|
|
||||||
val roomCallState: RoomCallState,
|
val roomCallState: RoomCallState,
|
||||||
val appName: String,
|
val appName: String,
|
||||||
val pinnedMessagesBannerState: PinnedMessagesBannerState,
|
val pinnedMessagesBannerState: PinnedMessagesBannerState,
|
||||||
|
|
|
||||||
|
|
@ -61,14 +61,12 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
|
||||||
aMessagesState(roomName = null),
|
aMessagesState(roomName = null),
|
||||||
aMessagesState(composerState = aMessageComposerState(showTextFormatting = true)),
|
aMessagesState(composerState = aMessageComposerState(showTextFormatting = true)),
|
||||||
aMessagesState(
|
aMessagesState(
|
||||||
enableVoiceMessages = true,
|
|
||||||
voiceMessageComposerState = aVoiceMessageComposerState(showPermissionRationaleDialog = true),
|
voiceMessageComposerState = aVoiceMessageComposerState(showPermissionRationaleDialog = true),
|
||||||
),
|
),
|
||||||
aMessagesState(
|
aMessagesState(
|
||||||
roomCallState = anOngoingCallState(),
|
roomCallState = anOngoingCallState(),
|
||||||
),
|
),
|
||||||
aMessagesState(
|
aMessagesState(
|
||||||
enableVoiceMessages = true,
|
|
||||||
voiceMessageComposerState = aVoiceMessageComposerState(
|
voiceMessageComposerState = aVoiceMessageComposerState(
|
||||||
voiceMessageState = aVoiceMessagePreviewState(),
|
voiceMessageState = aVoiceMessagePreviewState(),
|
||||||
showSendFailureDialog = true
|
showSendFailureDialog = true
|
||||||
|
|
@ -113,7 +111,6 @@ fun aMessagesState(
|
||||||
reactionSummaryState: ReactionSummaryState = aReactionSummaryState(),
|
reactionSummaryState: ReactionSummaryState = aReactionSummaryState(),
|
||||||
hasNetworkConnection: Boolean = true,
|
hasNetworkConnection: Boolean = true,
|
||||||
showReinvitePrompt: Boolean = false,
|
showReinvitePrompt: Boolean = false,
|
||||||
enableVoiceMessages: Boolean = true,
|
|
||||||
roomCallState: RoomCallState = aStandByCallState(),
|
roomCallState: RoomCallState = aStandByCallState(),
|
||||||
pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(),
|
pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(),
|
||||||
dmUserVerificationState: IdentityState? = null,
|
dmUserVerificationState: IdentityState? = null,
|
||||||
|
|
@ -141,7 +138,6 @@ fun aMessagesState(
|
||||||
inviteProgress = AsyncData.Uninitialized,
|
inviteProgress = AsyncData.Uninitialized,
|
||||||
showReinvitePrompt = showReinvitePrompt,
|
showReinvitePrompt = showReinvitePrompt,
|
||||||
enableTextFormatting = true,
|
enableTextFormatting = true,
|
||||||
enableVoiceMessages = enableVoiceMessages,
|
|
||||||
roomCallState = roomCallState,
|
roomCallState = roomCallState,
|
||||||
appName = "Element",
|
appName = "Element",
|
||||||
pinnedMessagesBannerState = pinnedMessagesBannerState,
|
pinnedMessagesBannerState = pinnedMessagesBannerState,
|
||||||
|
|
|
||||||
|
|
@ -377,7 +377,7 @@ private fun MessagesViewContent(
|
||||||
enableTextFormatting = state.enableTextFormatting,
|
enableTextFormatting = state.enableTextFormatting,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (state.enableVoiceMessages && state.voiceMessageComposerState.showPermissionRationaleDialog) {
|
if (state.voiceMessageComposerState.showPermissionRationaleDialog) {
|
||||||
VoiceMessagePermissionRationaleDialog(
|
VoiceMessagePermissionRationaleDialog(
|
||||||
onContinue = {
|
onContinue = {
|
||||||
state.voiceMessageComposerState.eventSink(VoiceMessageComposerEvents.AcceptPermissionRationale)
|
state.voiceMessageComposerState.eventSink(VoiceMessageComposerEvents.AcceptPermissionRationale)
|
||||||
|
|
@ -388,7 +388,7 @@ private fun MessagesViewContent(
|
||||||
appName = state.appName
|
appName = state.appName
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (state.enableVoiceMessages && state.voiceMessageComposerState.showSendFailureDialog) {
|
if (state.voiceMessageComposerState.showSendFailureDialog) {
|
||||||
VoiceMessageSendingFailedDialog(
|
VoiceMessageSendingFailedDialog(
|
||||||
onDismiss = { state.voiceMessageComposerState.eventSink(VoiceMessageComposerEvents.DismissSendFailureDialog) },
|
onDismiss = { state.voiceMessageComposerState.eventSink(VoiceMessageComposerEvents.DismissSendFailureDialog) },
|
||||||
)
|
)
|
||||||
|
|
@ -464,7 +464,6 @@ private fun MessagesViewComposerBottomSheetContents(
|
||||||
MessageComposerView(
|
MessageComposerView(
|
||||||
state = state.composerState,
|
state = state.composerState,
|
||||||
voiceMessageState = state.voiceMessageComposerState,
|
voiceMessageState = state.voiceMessageComposerState,
|
||||||
enableVoiceMessages = state.enableVoiceMessages,
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.features.messages.api.pinned.IsPinnedMessagesFeatureEnabled
|
|
||||||
import io.element.android.features.messages.impl.UserEventPermissions
|
import io.element.android.features.messages.impl.UserEventPermissions
|
||||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemActionComparator
|
import io.element.android.features.messages.impl.actionlist.model.TimelineItemActionComparator
|
||||||
|
|
@ -40,8 +39,6 @@ import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.dateformatter.api.DateFormatter
|
import io.element.android.libraries.dateformatter.api.DateFormatter
|
||||||
import io.element.android.libraries.dateformatter.api.DateFormatterMode
|
import io.element.android.libraries.dateformatter.api.DateFormatterMode
|
||||||
import io.element.android.libraries.di.RoomScope
|
import io.element.android.libraries.di.RoomScope
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||||
|
|
@ -62,10 +59,8 @@ class DefaultActionListPresenter @AssistedInject constructor(
|
||||||
@Assisted
|
@Assisted
|
||||||
private val postProcessor: TimelineItemActionPostProcessor,
|
private val postProcessor: TimelineItemActionPostProcessor,
|
||||||
private val appPreferencesStore: AppPreferencesStore,
|
private val appPreferencesStore: AppPreferencesStore,
|
||||||
private val isPinnedMessagesFeatureEnabled: IsPinnedMessagesFeatureEnabled,
|
|
||||||
private val room: BaseRoom,
|
private val room: BaseRoom,
|
||||||
private val userSendFailureFactory: VerifiedUserSendFailureFactory,
|
private val userSendFailureFactory: VerifiedUserSendFailureFactory,
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
private val dateFormatter: DateFormatter,
|
private val dateFormatter: DateFormatter,
|
||||||
) : ActionListPresenter {
|
) : ActionListPresenter {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
|
|
@ -87,7 +82,6 @@ class DefaultActionListPresenter @AssistedInject constructor(
|
||||||
val isDeveloperModeEnabled by remember {
|
val isDeveloperModeEnabled by remember {
|
||||||
appPreferencesStore.isDeveloperModeEnabledFlow()
|
appPreferencesStore.isDeveloperModeEnabledFlow()
|
||||||
}.collectAsState(initial = false)
|
}.collectAsState(initial = false)
|
||||||
val isPinnedEventsEnabled = isPinnedMessagesFeatureEnabled()
|
|
||||||
val pinnedEventIds by remember {
|
val pinnedEventIds by remember {
|
||||||
room.roomInfoFlow.map { it.pinnedEventIds }
|
room.roomInfoFlow.map { it.pinnedEventIds }
|
||||||
}.collectAsState(initial = persistentListOf())
|
}.collectAsState(initial = persistentListOf())
|
||||||
|
|
@ -99,7 +93,6 @@ class DefaultActionListPresenter @AssistedInject constructor(
|
||||||
timelineItem = event.event,
|
timelineItem = event.event,
|
||||||
usersEventPermissions = event.userEventPermissions,
|
usersEventPermissions = event.userEventPermissions,
|
||||||
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
||||||
isPinnedEventsEnabled = isPinnedEventsEnabled,
|
|
||||||
pinnedEventIds = pinnedEventIds,
|
pinnedEventIds = pinnedEventIds,
|
||||||
target = target,
|
target = target,
|
||||||
)
|
)
|
||||||
|
|
@ -116,7 +109,6 @@ class DefaultActionListPresenter @AssistedInject constructor(
|
||||||
timelineItem: TimelineItem.Event,
|
timelineItem: TimelineItem.Event,
|
||||||
usersEventPermissions: UserEventPermissions,
|
usersEventPermissions: UserEventPermissions,
|
||||||
isDeveloperModeEnabled: Boolean,
|
isDeveloperModeEnabled: Boolean,
|
||||||
isPinnedEventsEnabled: Boolean,
|
|
||||||
pinnedEventIds: ImmutableList<EventId>,
|
pinnedEventIds: ImmutableList<EventId>,
|
||||||
target: MutableState<ActionListState.Target>
|
target: MutableState<ActionListState.Target>
|
||||||
) = launch {
|
) = launch {
|
||||||
|
|
@ -126,7 +118,6 @@ class DefaultActionListPresenter @AssistedInject constructor(
|
||||||
timelineItem = timelineItem,
|
timelineItem = timelineItem,
|
||||||
usersEventPermissions = usersEventPermissions,
|
usersEventPermissions = usersEventPermissions,
|
||||||
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
||||||
isPinnedEventsEnabled = isPinnedEventsEnabled,
|
|
||||||
isEventPinned = pinnedEventIds.contains(timelineItem.eventId),
|
isEventPinned = pinnedEventIds.contains(timelineItem.eventId),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -154,7 +145,6 @@ class DefaultActionListPresenter @AssistedInject constructor(
|
||||||
timelineItem: TimelineItem.Event,
|
timelineItem: TimelineItem.Event,
|
||||||
usersEventPermissions: UserEventPermissions,
|
usersEventPermissions: UserEventPermissions,
|
||||||
isDeveloperModeEnabled: Boolean,
|
isDeveloperModeEnabled: Boolean,
|
||||||
isPinnedEventsEnabled: Boolean,
|
|
||||||
isEventPinned: Boolean,
|
isEventPinned: Boolean,
|
||||||
): List<TimelineItemAction> {
|
): List<TimelineItemAction> {
|
||||||
val canRedact = timelineItem.isMine && usersEventPermissions.canRedactOwn || !timelineItem.isMine && usersEventPermissions.canRedactOther
|
val canRedact = timelineItem.isMine && usersEventPermissions.canRedactOwn || !timelineItem.isMine && usersEventPermissions.canRedactOther
|
||||||
|
|
@ -173,9 +163,7 @@ class DefaultActionListPresenter @AssistedInject constructor(
|
||||||
if (timelineItem.content is TimelineItemEventContentWithAttachment) {
|
if (timelineItem.content is TimelineItemEventContentWithAttachment) {
|
||||||
// Caption
|
// Caption
|
||||||
if (timelineItem.content.caption == null) {
|
if (timelineItem.content.caption == null) {
|
||||||
if (featureFlagService.isFeatureEnabled(FeatureFlags.MediaCaptionCreation)) {
|
add(TimelineItemAction.AddCaption)
|
||||||
add(TimelineItemAction.AddCaption)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
add(TimelineItemAction.EditCaption)
|
add(TimelineItemAction.EditCaption)
|
||||||
add(TimelineItemAction.RemoveCaption)
|
add(TimelineItemAction.RemoveCaption)
|
||||||
|
|
@ -189,7 +177,7 @@ class DefaultActionListPresenter @AssistedInject constructor(
|
||||||
if (canRedact && timelineItem.content is TimelineItemPollContent && !timelineItem.content.isEnded) {
|
if (canRedact && timelineItem.content is TimelineItemPollContent && !timelineItem.content.isEnded) {
|
||||||
add(TimelineItemAction.EndPoll)
|
add(TimelineItemAction.EndPoll)
|
||||||
}
|
}
|
||||||
val canPinUnpin = isPinnedEventsEnabled && usersEventPermissions.canPinUnpin && timelineItem.isRemote
|
val canPinUnpin = usersEventPermissions.canPinUnpin && timelineItem.isRemote
|
||||||
if (canPinUnpin) {
|
if (canPinUnpin) {
|
||||||
if (isEventPinned) {
|
if (isEventPinned) {
|
||||||
add(TimelineItemAction.Unpin)
|
add(TimelineItemAction.Unpin)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ package io.element.android.features.messages.impl.attachments.preview
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
|
@ -32,8 +31,6 @@ import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage
|
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo
|
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo
|
||||||
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||||
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
|
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
|
||||||
|
|
@ -58,7 +55,6 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
|
||||||
private val mediaSender: MediaSender,
|
private val mediaSender: MediaSender,
|
||||||
private val permalinkBuilder: PermalinkBuilder,
|
private val permalinkBuilder: PermalinkBuilder,
|
||||||
private val temporaryUriDeleter: TemporaryUriDeleter,
|
private val temporaryUriDeleter: TemporaryUriDeleter,
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
private val mediaOptimizationSelectorPresenterFactory: MediaOptimizationSelectorPresenter.Factory,
|
private val mediaOptimizationSelectorPresenterFactory: MediaOptimizationSelectorPresenter.Factory,
|
||||||
@SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope,
|
@SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope,
|
||||||
private val dispatchers: CoroutineDispatchers,
|
private val dispatchers: CoroutineDispatchers,
|
||||||
|
|
@ -86,14 +82,6 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
val ongoingSendAttachmentJob = remember { mutableStateOf<Job?>(null) }
|
val ongoingSendAttachmentJob = remember { mutableStateOf<Job?>(null) }
|
||||||
|
|
||||||
val allowCaption by remember {
|
|
||||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.MediaCaptionCreation)
|
|
||||||
}.collectAsState(initial = false)
|
|
||||||
val showCaptionCompatibilityWarning by remember {
|
|
||||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.MediaCaptionWarning)
|
|
||||||
}.collectAsState(initial = false)
|
|
||||||
|
|
||||||
var useSendQueue by remember { mutableStateOf(false) }
|
|
||||||
var preprocessMediaJob by remember { mutableStateOf<Job?>(null) }
|
var preprocessMediaJob by remember { mutableStateOf<Job?>(null) }
|
||||||
|
|
||||||
val mediaAttachment = attachment as Attachment.Media
|
val mediaAttachment = attachment as Attachment.Media
|
||||||
|
|
@ -106,10 +94,6 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
var displayFileTooLargeError by remember { mutableStateOf(false) }
|
var displayFileTooLargeError by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
useSendQueue = featureFlagService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(mediaOptimizationSelectorState.displayMediaSelectorViews) {
|
LaunchedEffect(mediaOptimizationSelectorState.displayMediaSelectorViews) {
|
||||||
// If the media optimization selector is not displayed, we can pre-process the media
|
// If the media optimization selector is not displayed, we can pre-process the media
|
||||||
// to prepare it for sending. This is done to avoid blocking the UI thread when the
|
// to prepare it for sending. This is done to avoid blocking the UI thread when the
|
||||||
|
|
@ -182,18 +166,17 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
|
||||||
.takeIf { it.isNotEmpty() }
|
.takeIf { it.isNotEmpty() }
|
||||||
|
|
||||||
// If we're supposed to send the media as a background job, we can dismiss this screen already
|
// If we're supposed to send the media as a background job, we can dismiss this screen already
|
||||||
if (useSendQueue && coroutineContext.isActive) {
|
if (coroutineContext.isActive) {
|
||||||
onDoneListener()
|
onDoneListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
// If using the send queue, send it using the session coroutine scope so it doesn't matter if this screen or the chat one are closed
|
// Send the media using the session coroutine scope so it doesn't matter if this screen or the chat one are closed
|
||||||
val sendMediaCoroutineScope = if (useSendQueue) sessionCoroutineScope else coroutineScope
|
sessionCoroutineScope.launch(dispatchers.io) {
|
||||||
sendMediaCoroutineScope.launch(dispatchers.io) {
|
|
||||||
sendPreProcessedMedia(
|
sendPreProcessedMedia(
|
||||||
mediaUploadInfo = mediaUploadInfo,
|
mediaUploadInfo = mediaUploadInfo,
|
||||||
caption = caption,
|
caption = caption,
|
||||||
sendActionState = sendActionState,
|
sendActionState = sendActionState,
|
||||||
dismissAfterSend = !useSendQueue,
|
dismissAfterSend = false,
|
||||||
inReplyToEventId = null,
|
inReplyToEventId = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -238,8 +221,6 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
|
||||||
attachment = attachment,
|
attachment = attachment,
|
||||||
sendActionState = sendActionState.value,
|
sendActionState = sendActionState.value,
|
||||||
textEditorState = textEditorState,
|
textEditorState = textEditorState,
|
||||||
allowCaption = allowCaption,
|
|
||||||
showCaptionCompatibilityWarning = showCaptionCompatibilityWarning,
|
|
||||||
mediaOptimizationSelectorState = mediaOptimizationSelectorState,
|
mediaOptimizationSelectorState = mediaOptimizationSelectorState,
|
||||||
displayFileTooLargeError = displayFileTooLargeError,
|
displayFileTooLargeError = displayFileTooLargeError,
|
||||||
eventSink = ::handleEvents
|
eventSink = ::handleEvents
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@ data class AttachmentsPreviewState(
|
||||||
val attachment: Attachment,
|
val attachment: Attachment,
|
||||||
val sendActionState: SendActionState,
|
val sendActionState: SendActionState,
|
||||||
val textEditorState: TextEditorState,
|
val textEditorState: TextEditorState,
|
||||||
val allowCaption: Boolean,
|
|
||||||
val showCaptionCompatibilityWarning: Boolean,
|
|
||||||
val mediaOptimizationSelectorState: MediaOptimizationSelectorState,
|
val mediaOptimizationSelectorState: MediaOptimizationSelectorState,
|
||||||
val displayFileTooLargeError: Boolean,
|
val displayFileTooLargeError: Boolean,
|
||||||
val eventSink: (AttachmentsPreviewEvents) -> Unit
|
val eventSink: (AttachmentsPreviewEvents) -> Unit
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,6 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider<Attachment
|
||||||
anAttachmentsPreviewState(sendActionState = SendActionState.Sending.ReadyToUpload(aMediaUploadInfo())),
|
anAttachmentsPreviewState(sendActionState = SendActionState.Sending.ReadyToUpload(aMediaUploadInfo())),
|
||||||
anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Uploading(0.5f, aMediaUploadInfo())),
|
anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Uploading(0.5f, aMediaUploadInfo())),
|
||||||
anAttachmentsPreviewState(sendActionState = SendActionState.Failure(RuntimeException("error"), aMediaUploadInfo())),
|
anAttachmentsPreviewState(sendActionState = SendActionState.Failure(RuntimeException("error"), aMediaUploadInfo())),
|
||||||
anAttachmentsPreviewState(allowCaption = false),
|
|
||||||
anAttachmentsPreviewState(showCaptionCompatibilityWarning = true),
|
|
||||||
anAttachmentsPreviewState(displayFileTooLargeError = true),
|
anAttachmentsPreviewState(displayFileTooLargeError = true),
|
||||||
anAttachmentsPreviewState(
|
anAttachmentsPreviewState(
|
||||||
mediaInfo = aVideoMediaInfo(),
|
mediaInfo = aVideoMediaInfo(),
|
||||||
|
|
@ -65,8 +63,6 @@ fun anAttachmentsPreviewState(
|
||||||
mediaInfo: MediaInfo = anImageMediaInfo(),
|
mediaInfo: MediaInfo = anImageMediaInfo(),
|
||||||
textEditorState: TextEditorState = aTextEditorStateMarkdown(),
|
textEditorState: TextEditorState = aTextEditorStateMarkdown(),
|
||||||
sendActionState: SendActionState = SendActionState.Idle,
|
sendActionState: SendActionState = SendActionState.Idle,
|
||||||
allowCaption: Boolean = true,
|
|
||||||
showCaptionCompatibilityWarning: Boolean = true,
|
|
||||||
mediaOptimizationSelectorState: MediaOptimizationSelectorState = aMediaOptimisationSelectorState(),
|
mediaOptimizationSelectorState: MediaOptimizationSelectorState = aMediaOptimisationSelectorState(),
|
||||||
displayFileTooLargeError: Boolean = false,
|
displayFileTooLargeError: Boolean = false,
|
||||||
) = AttachmentsPreviewState(
|
) = AttachmentsPreviewState(
|
||||||
|
|
@ -75,8 +71,6 @@ fun anAttachmentsPreviewState(
|
||||||
),
|
),
|
||||||
sendActionState = sendActionState,
|
sendActionState = sendActionState,
|
||||||
textEditorState = textEditorState,
|
textEditorState = textEditorState,
|
||||||
allowCaption = allowCaption,
|
|
||||||
showCaptionCompatibilityWarning = showCaptionCompatibilityWarning,
|
|
||||||
mediaOptimizationSelectorState = mediaOptimizationSelectorState,
|
mediaOptimizationSelectorState = mediaOptimizationSelectorState,
|
||||||
displayFileTooLargeError = displayFileTooLargeError,
|
displayFileTooLargeError = displayFileTooLargeError,
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
|
|
|
||||||
|
|
@ -363,17 +363,13 @@ private fun AttachmentsPreviewBottomActions(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
state = state.textEditorState,
|
state = state.textEditorState,
|
||||||
voiceMessageState = VoiceMessageState.Idle,
|
voiceMessageState = VoiceMessageState.Idle,
|
||||||
composerMode = MessageComposerMode.Attachment(
|
composerMode = MessageComposerMode.Attachment,
|
||||||
allowCaption = state.allowCaption,
|
|
||||||
showCaptionCompatibilityWarning = state.showCaptionCompatibilityWarning,
|
|
||||||
),
|
|
||||||
onRequestFocus = {},
|
onRequestFocus = {},
|
||||||
onSendMessage = onSendClick,
|
onSendMessage = onSendClick,
|
||||||
showTextFormatting = false,
|
showTextFormatting = false,
|
||||||
onResetComposerMode = {},
|
onResetComposerMode = {},
|
||||||
onAddAttachment = {},
|
onAddAttachment = {},
|
||||||
onDismissTextFormatting = {},
|
onDismissTextFormatting = {},
|
||||||
enableVoiceMessages = false,
|
|
||||||
onVoiceRecorderEvent = {},
|
onVoiceRecorderEvent = {},
|
||||||
onVoicePlayerEvent = {},
|
onVoicePlayerEvent = {},
|
||||||
onSendVoiceMessage = {},
|
onSendVoiceMessage = {},
|
||||||
|
|
|
||||||
|
|
@ -132,17 +132,15 @@ private fun AttachmentSourcePickerMenu(
|
||||||
style = ListItemStyle.Primary,
|
style = ListItemStyle.Primary,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (state.canCreatePoll) {
|
ListItem(
|
||||||
ListItem(
|
modifier = Modifier.clickable {
|
||||||
modifier = Modifier.clickable {
|
state.eventSink(MessageComposerEvents.PickAttachmentSource.Poll)
|
||||||
state.eventSink(MessageComposerEvents.PickAttachmentSource.Poll)
|
onCreatePollClick()
|
||||||
onCreatePollClick()
|
},
|
||||||
},
|
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Polls())),
|
||||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Polls())),
|
headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_poll)) },
|
||||||
headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_poll)) },
|
style = ListItemStyle.Primary,
|
||||||
style = ListItemStyle.Primary,
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
if (enableTextFormatting) {
|
if (enableTextFormatting) {
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.ToggleTextFormatting(enabled = true)) },
|
modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.ToggleTextFormatting(enabled = true)) },
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,6 @@ import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||||
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
|
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
|
||||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||||
|
|
@ -103,7 +101,6 @@ class MessageComposerPresenter @AssistedInject constructor(
|
||||||
private val sessionCoroutineScope: CoroutineScope,
|
private val sessionCoroutineScope: CoroutineScope,
|
||||||
private val room: JoinedRoom,
|
private val room: JoinedRoom,
|
||||||
private val mediaPickerProvider: PickerProvider,
|
private val mediaPickerProvider: PickerProvider,
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
private val sessionPreferencesStore: SessionPreferencesStore,
|
private val sessionPreferencesStore: SessionPreferencesStore,
|
||||||
private val localMediaFactory: LocalMediaFactory,
|
private val localMediaFactory: LocalMediaFactory,
|
||||||
private val mediaSender: MediaSender,
|
private val mediaSender: MediaSender,
|
||||||
|
|
@ -156,13 +153,7 @@ class MessageComposerPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
val canShareLocation = remember { mutableStateOf(false) }
|
val canShareLocation = remember { mutableStateOf(false) }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
canShareLocation.value = featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing) &&
|
canShareLocation.value = locationService.isServiceAvailable()
|
||||||
locationService.isServiceAvailable()
|
|
||||||
}
|
|
||||||
|
|
||||||
val canCreatePoll = remember { mutableStateOf(false) }
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
canCreatePoll.value = featureFlagService.isFeatureEnabled(FeatureFlags.Polls)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val galleryMediaPicker = mediaPickerProvider.registerGalleryPicker { uri, mimeType ->
|
val galleryMediaPicker = mediaPickerProvider.registerGalleryPicker { uri, mimeType ->
|
||||||
|
|
@ -376,7 +367,6 @@ class MessageComposerPresenter @AssistedInject constructor(
|
||||||
showAttachmentSourcePicker = showAttachmentSourcePicker,
|
showAttachmentSourcePicker = showAttachmentSourcePicker,
|
||||||
showTextFormatting = showTextFormatting,
|
showTextFormatting = showTextFormatting,
|
||||||
canShareLocation = canShareLocation.value,
|
canShareLocation = canShareLocation.value,
|
||||||
canCreatePoll = canCreatePoll.value,
|
|
||||||
suggestions = suggestions.toPersistentList(),
|
suggestions = suggestions.toPersistentList(),
|
||||||
resolveMentionDisplay = resolveMentionDisplay,
|
resolveMentionDisplay = resolveMentionDisplay,
|
||||||
resolveAtRoomMentionDisplay = resolveAtRoomMentionDisplay,
|
resolveAtRoomMentionDisplay = resolveAtRoomMentionDisplay,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ data class MessageComposerState(
|
||||||
val showAttachmentSourcePicker: Boolean,
|
val showAttachmentSourcePicker: Boolean,
|
||||||
val showTextFormatting: Boolean,
|
val showTextFormatting: Boolean,
|
||||||
val canShareLocation: Boolean,
|
val canShareLocation: Boolean,
|
||||||
val canCreatePoll: Boolean,
|
|
||||||
val suggestions: ImmutableList<ResolvedSuggestion>,
|
val suggestions: ImmutableList<ResolvedSuggestion>,
|
||||||
val resolveMentionDisplay: (String, String) -> TextDisplay,
|
val resolveMentionDisplay: (String, String) -> TextDisplay,
|
||||||
val resolveAtRoomMentionDisplay: () -> TextDisplay,
|
val resolveAtRoomMentionDisplay: () -> TextDisplay,
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ fun aMessageComposerState(
|
||||||
showTextFormatting: Boolean = false,
|
showTextFormatting: Boolean = false,
|
||||||
showAttachmentSourcePicker: Boolean = false,
|
showAttachmentSourcePicker: Boolean = false,
|
||||||
canShareLocation: Boolean = true,
|
canShareLocation: Boolean = true,
|
||||||
canCreatePoll: Boolean = true,
|
|
||||||
suggestions: ImmutableList<ResolvedSuggestion> = persistentListOf(),
|
suggestions: ImmutableList<ResolvedSuggestion> = persistentListOf(),
|
||||||
eventSink: (MessageComposerEvents) -> Unit = {},
|
eventSink: (MessageComposerEvents) -> Unit = {},
|
||||||
) = MessageComposerState(
|
) = MessageComposerState(
|
||||||
|
|
@ -40,7 +39,6 @@ fun aMessageComposerState(
|
||||||
showTextFormatting = showTextFormatting,
|
showTextFormatting = showTextFormatting,
|
||||||
showAttachmentSourcePicker = showAttachmentSourcePicker,
|
showAttachmentSourcePicker = showAttachmentSourcePicker,
|
||||||
canShareLocation = canShareLocation,
|
canShareLocation = canShareLocation,
|
||||||
canCreatePoll = canCreatePoll,
|
|
||||||
suggestions = suggestions,
|
suggestions = suggestions,
|
||||||
resolveMentionDisplay = { _, _ -> TextDisplay.Plain },
|
resolveMentionDisplay = { _, _ -> TextDisplay.Plain },
|
||||||
resolveAtRoomMentionDisplay = { TextDisplay.Plain },
|
resolveAtRoomMentionDisplay = { TextDisplay.Plain },
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ import kotlinx.coroutines.launch
|
||||||
internal fun MessageComposerView(
|
internal fun MessageComposerView(
|
||||||
state: MessageComposerState,
|
state: MessageComposerState,
|
||||||
voiceMessageState: VoiceMessageComposerState,
|
voiceMessageState: VoiceMessageComposerState,
|
||||||
enableVoiceMessages: Boolean,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val view = LocalView.current
|
val view = LocalView.current
|
||||||
|
|
@ -104,7 +103,6 @@ internal fun MessageComposerView(
|
||||||
onResetComposerMode = ::onCloseSpecialMode,
|
onResetComposerMode = ::onCloseSpecialMode,
|
||||||
onAddAttachment = ::onAddAttachment,
|
onAddAttachment = ::onAddAttachment,
|
||||||
onDismissTextFormatting = ::onDismissTextFormatting,
|
onDismissTextFormatting = ::onDismissTextFormatting,
|
||||||
enableVoiceMessages = enableVoiceMessages,
|
|
||||||
onVoiceRecorderEvent = onVoiceRecorderEvent,
|
onVoiceRecorderEvent = onVoiceRecorderEvent,
|
||||||
onVoicePlayerEvent = onVoicePlayerEvent,
|
onVoicePlayerEvent = onVoicePlayerEvent,
|
||||||
onSendVoiceMessage = onSendVoiceMessage,
|
onSendVoiceMessage = onSendVoiceMessage,
|
||||||
|
|
@ -128,13 +126,11 @@ internal fun MessageComposerViewPreview(
|
||||||
modifier = Modifier.height(IntrinsicSize.Min),
|
modifier = Modifier.height(IntrinsicSize.Min),
|
||||||
state = state,
|
state = state,
|
||||||
voiceMessageState = aVoiceMessageComposerState(),
|
voiceMessageState = aVoiceMessageComposerState(),
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
MessageComposerView(
|
MessageComposerView(
|
||||||
modifier = Modifier.height(200.dp),
|
modifier = Modifier.height(200.dp),
|
||||||
state = state,
|
state = state,
|
||||||
voiceMessageState = aVoiceMessageComposerState(),
|
voiceMessageState = aVoiceMessageComposerState(),
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
DisabledComposerView()
|
DisabledComposerView()
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +146,6 @@ internal fun MessageComposerViewVoicePreview(
|
||||||
modifier = Modifier.height(IntrinsicSize.Min),
|
modifier = Modifier.height(IntrinsicSize.Min),
|
||||||
state = aMessageComposerState(),
|
state = aMessageComposerState(),
|
||||||
voiceMessageState = state,
|
voiceMessageState = state,
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.pinned
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
|
||||||
import io.element.android.features.messages.api.pinned.IsPinnedMessagesFeatureEnabled
|
|
||||||
import io.element.android.libraries.di.AppScope
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
|
||||||
class DefaultIsPinnedMessagesFeatureEnabled @Inject constructor(
|
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
) : IsPinnedMessagesFeatureEnabled {
|
|
||||||
@Composable
|
|
||||||
override operator fun invoke(): Boolean {
|
|
||||||
var isFeatureEnabled by rememberSaveable {
|
|
||||||
mutableStateOf(false)
|
|
||||||
}
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.PinnedEvents)
|
|
||||||
.onEach { isFeatureEnabled = it }
|
|
||||||
.launchIn(this)
|
|
||||||
}
|
|
||||||
return isFeatureEnabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -12,8 +12,6 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.core.coroutine.mapState
|
import io.element.android.libraries.core.coroutine.mapState
|
||||||
import io.element.android.libraries.di.RoomScope
|
import io.element.android.libraries.di.RoomScope
|
||||||
import io.element.android.libraries.di.SingleIn
|
import io.element.android.libraries.di.SingleIn
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
|
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||||
|
|
@ -23,7 +21,6 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
@ -35,7 +32,6 @@ import javax.inject.Inject
|
||||||
class PinnedEventsTimelineProvider @Inject constructor(
|
class PinnedEventsTimelineProvider @Inject constructor(
|
||||||
private val room: JoinedRoom,
|
private val room: JoinedRoom,
|
||||||
private val syncService: SyncService,
|
private val syncService: SyncService,
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
private val dispatchers: CoroutineDispatchers,
|
private val dispatchers: CoroutineDispatchers,
|
||||||
) : TimelineProvider {
|
) : TimelineProvider {
|
||||||
private val _timelineStateFlow: MutableStateFlow<AsyncData<Timeline>> =
|
private val _timelineStateFlow: MutableStateFlow<AsyncData<Timeline>> =
|
||||||
|
|
@ -66,20 +62,10 @@ class PinnedEventsTimelineProvider @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun onActive() = coroutineScope {
|
private suspend fun onActive() = coroutineScope {
|
||||||
combine(
|
syncService.syncState.onEach {
|
||||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.PinnedEvents),
|
|
||||||
syncService.syncState,
|
|
||||||
) { isEnabled, _ ->
|
|
||||||
// do not use syncState here as data can be loaded from cache, it's just to trigger retry if needed
|
// do not use syncState here as data can be loaded from cache, it's just to trigger retry if needed
|
||||||
isEnabled
|
loadTimelineIfNeeded()
|
||||||
}
|
}
|
||||||
.onEach { isFeatureEnabled ->
|
|
||||||
if (isFeatureEnabled) {
|
|
||||||
loadTimelineIfNeeded()
|
|
||||||
} else {
|
|
||||||
resetTimeline()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.launchIn(this)
|
.launchIn(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,6 @@ import io.element.android.features.messages.impl.utils.TextPillificationHelper
|
||||||
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
|
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
|
||||||
import io.element.android.libraries.androidutils.text.safeLinkify
|
import io.element.android.libraries.androidutils.text.safeLinkify
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
|
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
|
||||||
|
|
@ -56,7 +54,6 @@ import kotlin.time.Duration
|
||||||
class TimelineItemContentMessageFactory @Inject constructor(
|
class TimelineItemContentMessageFactory @Inject constructor(
|
||||||
private val fileSizeFormatter: FileSizeFormatter,
|
private val fileSizeFormatter: FileSizeFormatter,
|
||||||
private val fileExtensionExtractor: FileExtensionExtractor,
|
private val fileExtensionExtractor: FileExtensionExtractor,
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
private val htmlConverterProvider: HtmlConverterProvider,
|
private val htmlConverterProvider: HtmlConverterProvider,
|
||||||
private val permalinkParser: PermalinkParser,
|
private val permalinkParser: PermalinkParser,
|
||||||
private val textPillificationHelper: TextPillificationHelper,
|
private val textPillificationHelper: TextPillificationHelper,
|
||||||
|
|
@ -177,38 +174,20 @@ class TimelineItemContentMessageFactory @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is VoiceMessageType -> {
|
is VoiceMessageType -> {
|
||||||
when (featureFlagService.isFeatureEnabled(FeatureFlags.VoiceMessages)) {
|
TimelineItemVoiceContent(
|
||||||
true -> {
|
eventId = eventId,
|
||||||
TimelineItemVoiceContent(
|
filename = messageType.filename,
|
||||||
eventId = eventId,
|
fileSize = messageType.info?.size ?: 0,
|
||||||
filename = messageType.filename,
|
caption = messageType.caption?.trimEnd(),
|
||||||
fileSize = messageType.info?.size ?: 0,
|
formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(),
|
||||||
caption = messageType.caption?.trimEnd(),
|
isEdited = content.isEdited,
|
||||||
formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(),
|
mediaSource = messageType.source,
|
||||||
isEdited = content.isEdited,
|
duration = messageType.info?.duration ?: Duration.ZERO,
|
||||||
mediaSource = messageType.source,
|
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
|
||||||
duration = messageType.info?.duration ?: Duration.ZERO,
|
waveform = messageType.details?.waveform?.toImmutableList() ?: persistentListOf(),
|
||||||
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
|
formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),
|
||||||
waveform = messageType.details?.waveform?.toImmutableList() ?: persistentListOf(),
|
fileExtension = fileExtensionExtractor.extractFromName(messageType.filename)
|
||||||
formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),
|
)
|
||||||
fileExtension = fileExtensionExtractor.extractFromName(messageType.filename)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
false -> {
|
|
||||||
TimelineItemAudioContent(
|
|
||||||
filename = messageType.filename,
|
|
||||||
fileSize = messageType.info?.size ?: 0,
|
|
||||||
caption = messageType.caption?.trimEnd(),
|
|
||||||
formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(),
|
|
||||||
isEdited = content.isEdited,
|
|
||||||
mediaSource = messageType.source,
|
|
||||||
duration = messageType.info?.duration ?: Duration.ZERO,
|
|
||||||
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
|
|
||||||
formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),
|
|
||||||
fileExtension = fileExtensionExtractor.extractFromName(messageType.filename),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
is FileMessageType -> {
|
is FileMessageType -> {
|
||||||
val fileExtension = fileExtensionExtractor.extractFromName(messageType.filename)
|
val fileExtension = fileExtensionExtractor.extractFromName(messageType.filename)
|
||||||
|
|
|
||||||
|
|
@ -9,23 +9,18 @@ package io.element.android.features.messages.impl.timeline.factories.event
|
||||||
|
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
|
||||||
import io.element.android.features.poll.api.pollcontent.PollContentStateFactory
|
import io.element.android.features.poll.api.pollcontent.PollContentStateFactory
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TimelineItemContentPollFactory @Inject constructor(
|
class TimelineItemContentPollFactory @Inject constructor(
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
private val pollContentStateFactory: PollContentStateFactory,
|
private val pollContentStateFactory: PollContentStateFactory,
|
||||||
) {
|
) {
|
||||||
suspend fun create(
|
suspend fun create(
|
||||||
event: EventTimelineItem,
|
event: EventTimelineItem,
|
||||||
content: PollContent,
|
content: PollContent,
|
||||||
): TimelineItemEventContent {
|
): TimelineItemEventContent {
|
||||||
if (!featureFlagService.isFeatureEnabled(FeatureFlags.Polls)) return TimelineItemUnknownContent
|
|
||||||
val pollContentState = pollContentStateFactory.create(event, content)
|
val pollContentState = pollContentStateFactory.create(event, content)
|
||||||
return TimelineItemPollContent(
|
return TimelineItemPollContent(
|
||||||
isMine = pollContentState.isMine,
|
isMine = pollContentState.isMine,
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,6 @@ import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
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.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
|
@ -994,37 +991,6 @@ class MessagesPresenterTest {
|
||||||
composerMode = MessageComposerMode.EditCaption(
|
composerMode = MessageComposerMode.EditCaption(
|
||||||
eventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(),
|
eventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(),
|
||||||
content = A_CAPTION,
|
content = A_CAPTION,
|
||||||
showCaptionCompatibilityWarning = true,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - handle action edit caption without warning`() = runTest {
|
|
||||||
val messageEvent = aMessageEvent(
|
|
||||||
content = aTimelineItemImageContent(
|
|
||||||
caption = A_CAPTION,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val composerRecorder = EventsRecorder<MessageComposerEvents>()
|
|
||||||
val presenter = createMessagesPresenter(
|
|
||||||
messageComposerPresenter = { aMessageComposerState(eventSink = composerRecorder) },
|
|
||||||
featureFlagService = FakeFeatureFlagService(
|
|
||||||
initialState = mapOf(FeatureFlags.MediaCaptionWarning.key to false)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
presenter.testWithLifecycleOwner {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.EditCaption, messageEvent))
|
|
||||||
awaitItem()
|
|
||||||
composerRecorder.assertSingle(
|
|
||||||
MessageComposerEvents.SetMode(
|
|
||||||
composerMode = MessageComposerMode.EditCaption(
|
|
||||||
eventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(),
|
|
||||||
content = A_CAPTION,
|
|
||||||
showCaptionCompatibilityWarning = false,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -1051,37 +1017,6 @@ class MessagesPresenterTest {
|
||||||
composerMode = MessageComposerMode.EditCaption(
|
composerMode = MessageComposerMode.EditCaption(
|
||||||
eventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(),
|
eventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(),
|
||||||
content = "",
|
content = "",
|
||||||
showCaptionCompatibilityWarning = true,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - handle action add caption without warning`() = runTest {
|
|
||||||
val composerRecorder = EventsRecorder<MessageComposerEvents>()
|
|
||||||
val presenter = createMessagesPresenter(
|
|
||||||
messageComposerPresenter = { aMessageComposerState(eventSink = composerRecorder) },
|
|
||||||
featureFlagService = FakeFeatureFlagService(
|
|
||||||
initialState = mapOf(FeatureFlags.MediaCaptionWarning.key to false)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val messageEvent = aMessageEvent(
|
|
||||||
content = aTimelineItemImageContent(
|
|
||||||
caption = null,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
presenter.testWithLifecycleOwner {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.AddCaption, messageEvent))
|
|
||||||
awaitItem()
|
|
||||||
composerRecorder.assertSingle(
|
|
||||||
MessageComposerEvents.SetMode(
|
|
||||||
composerMode = MessageComposerMode.EditCaption(
|
|
||||||
eventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(),
|
|
||||||
content = "",
|
|
||||||
showCaptionCompatibilityWarning = false,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -1234,7 +1169,6 @@ class MessagesPresenterTest {
|
||||||
typingNoticeResult = { Result.success(Unit) },
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
),
|
),
|
||||||
navigator: FakeMessagesNavigator = FakeMessagesNavigator(),
|
navigator: FakeMessagesNavigator = FakeMessagesNavigator(),
|
||||||
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
|
|
||||||
clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(),
|
clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(),
|
||||||
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
||||||
timelineEventSink: (TimelineEvents) -> Unit = {},
|
timelineEventSink: (TimelineEvents) -> Unit = {},
|
||||||
|
|
@ -1270,7 +1204,6 @@ class MessagesPresenterTest {
|
||||||
snackbarDispatcher = SnackbarDispatcher(),
|
snackbarDispatcher = SnackbarDispatcher(),
|
||||||
navigator = navigator,
|
navigator = navigator,
|
||||||
clipboardHelper = clipboardHelper,
|
clipboardHelper = clipboardHelper,
|
||||||
featureFlagsService = featureFlagService,
|
|
||||||
buildMeta = aBuildMeta(),
|
buildMeta = aBuildMeta(),
|
||||||
dispatchers = coroutineDispatchers,
|
dispatchers = coroutineDispatchers,
|
||||||
htmlConverterProvider = FakeHtmlConverterProvider(),
|
htmlConverterProvider = FakeHtmlConverterProvider(),
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,6 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent
|
||||||
import io.element.android.features.poll.api.pollcontent.aPollAnswerItemList
|
import io.element.android.features.poll.api.pollcontent.aPollAnswerItemList
|
||||||
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
|
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
|
|
@ -52,7 +50,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state`() = runTest {
|
fun `present - initial state`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -63,7 +61,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for message from me redacted`() = runTest {
|
fun `present - compute for message from me redacted`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -100,7 +98,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for message from others redacted`() = runTest {
|
fun `present - compute for message from others redacted`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -141,7 +139,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for others message`() = runTest {
|
fun `present - compute for others message`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -188,7 +186,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for others message in a thread`() = runTest {
|
fun `present - compute for others message in a thread`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
val messageEvent = aMessageEvent(
|
val messageEvent = aMessageEvent(
|
||||||
|
|
@ -234,7 +232,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for others message cannot sent message`() = runTest {
|
fun `present - compute for others message cannot sent message`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -280,7 +278,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for others message and can redact`() = runTest {
|
fun `present - compute for others message and can redact`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -328,7 +326,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for others message and cannot send reaction`() = runTest {
|
fun `present - compute for others message and cannot send reaction`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -376,7 +374,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for my message`() = runTest {
|
fun `present - compute for my message`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -423,7 +421,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for my message in a thread`() = runTest {
|
fun `present - compute for my message in a thread`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
val messageEvent = aMessageEvent(
|
val messageEvent = aMessageEvent(
|
||||||
|
|
@ -469,7 +467,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for my message cannot redact`() = runTest {
|
fun `present - compute for my message cannot redact`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -515,7 +513,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for my message no permission`() = runTest {
|
fun `present - compute for my message no permission`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -558,7 +556,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for a media item`() = runTest {
|
fun `present - compute for a media item`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -603,61 +601,9 @@ class ActionListPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - compute for a media item - caption disabled`() = runTest {
|
|
||||||
val presenter = createActionListPresenter(
|
|
||||||
isDeveloperModeEnabled = true,
|
|
||||||
isPinFeatureEnabled = true,
|
|
||||||
allowCaption = false,
|
|
||||||
)
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
val messageEvent = aMessageEvent(
|
|
||||||
isMine = true,
|
|
||||||
isEditable = true,
|
|
||||||
content = aTimelineItemImageContent(),
|
|
||||||
)
|
|
||||||
initialState.eventSink.invoke(
|
|
||||||
ActionListEvents.ComputeForMessage(
|
|
||||||
event = messageEvent,
|
|
||||||
userEventPermissions = aUserEventPermissions(
|
|
||||||
canRedactOwn = true,
|
|
||||||
canRedactOther = false,
|
|
||||||
canSendMessage = true,
|
|
||||||
canSendReaction = true,
|
|
||||||
canPinUnpin = true,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val successState = awaitItem()
|
|
||||||
assertThat(successState.target).isEqualTo(
|
|
||||||
ActionListState.Target.Success(
|
|
||||||
event = messageEvent,
|
|
||||||
sentTimeFull = "0 Full true",
|
|
||||||
displayEmojiReactions = true,
|
|
||||||
verifiedUserSendFailure = VerifiedUserSendFailure.None,
|
|
||||||
actions = persistentListOf(
|
|
||||||
TimelineItemAction.Reply,
|
|
||||||
TimelineItemAction.Forward,
|
|
||||||
// Not here
|
|
||||||
// TimelineItemAction.AddCaption,
|
|
||||||
TimelineItemAction.CopyLink,
|
|
||||||
TimelineItemAction.Pin,
|
|
||||||
TimelineItemAction.ViewSource,
|
|
||||||
TimelineItemAction.Redact,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
initialState.eventSink.invoke(ActionListEvents.Clear)
|
|
||||||
assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for a media with caption item`() = runTest {
|
fun `present - compute for a media with caption item`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -708,7 +654,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for a media with caption item - other user event`() = runTest {
|
fun `present - compute for a media with caption item - other user event`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -757,7 +703,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for a state item in debug build`() = runTest {
|
fun `present - compute for a state item in debug build`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -797,7 +743,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for a state item in non-debuggable build`() = runTest {
|
fun `present - compute for a state item in non-debuggable build`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -824,7 +770,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute message in non-debuggable build`() = runTest {
|
fun `present - compute message in non-debuggable build`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -870,7 +816,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute message when user can't pin`() = runTest {
|
fun `present - compute message when user can't pin`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -921,7 +867,6 @@ class ActionListPresenterTest {
|
||||||
}
|
}
|
||||||
val presenter = createActionListPresenter(
|
val presenter = createActionListPresenter(
|
||||||
isDeveloperModeEnabled = true,
|
isDeveloperModeEnabled = true,
|
||||||
isPinFeatureEnabled = true,
|
|
||||||
room = room
|
room = room
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -970,7 +915,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute message with no actions`() = runTest {
|
fun `present - compute message with no actions`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -1017,7 +962,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute not sent message`() = runTest {
|
fun `present - compute not sent message`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -1061,7 +1006,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for editable poll message`() = runTest {
|
fun `present - compute for editable poll message`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -1105,7 +1050,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for non-editable poll message`() = runTest {
|
fun `present - compute for non-editable poll message`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -1148,7 +1093,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for ended poll message`() = runTest {
|
fun `present - compute for ended poll message`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -1190,7 +1135,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for voice message`() = runTest {
|
fun `present - compute for voice message`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -1235,7 +1180,7 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - compute for call notify`() = runTest {
|
fun `present - compute for call notify`() = runTest {
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = true, isPinFeatureEnabled = true)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -1275,7 +1220,7 @@ class ActionListPresenterTest {
|
||||||
val room = FakeBaseRoom(
|
val room = FakeBaseRoom(
|
||||||
userDisplayNameResult = { Result.success("Alice") }
|
userDisplayNameResult = { Result.success("Alice") }
|
||||||
)
|
)
|
||||||
val presenter = createActionListPresenter(isDeveloperModeEnabled = false, isPinFeatureEnabled = false, room = room)
|
val presenter = createActionListPresenter(isDeveloperModeEnabled = false, room = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -1299,22 +1244,14 @@ class ActionListPresenterTest {
|
||||||
|
|
||||||
private fun createActionListPresenter(
|
private fun createActionListPresenter(
|
||||||
isDeveloperModeEnabled: Boolean,
|
isDeveloperModeEnabled: Boolean,
|
||||||
isPinFeatureEnabled: Boolean,
|
|
||||||
room: BaseRoom = FakeBaseRoom(),
|
room: BaseRoom = FakeBaseRoom(),
|
||||||
allowCaption: Boolean = true,
|
|
||||||
): ActionListPresenter {
|
): ActionListPresenter {
|
||||||
val preferencesStore = InMemoryAppPreferencesStore(isDeveloperModeEnabled = isDeveloperModeEnabled)
|
val preferencesStore = InMemoryAppPreferencesStore(isDeveloperModeEnabled = isDeveloperModeEnabled)
|
||||||
return DefaultActionListPresenter(
|
return DefaultActionListPresenter(
|
||||||
postProcessor = TimelineItemActionPostProcessor.Default,
|
postProcessor = TimelineItemActionPostProcessor.Default,
|
||||||
appPreferencesStore = preferencesStore,
|
appPreferencesStore = preferencesStore,
|
||||||
isPinnedMessagesFeatureEnabled = { isPinFeatureEnabled },
|
|
||||||
room = room,
|
room = room,
|
||||||
userSendFailureFactory = VerifiedUserSendFailureFactory(room),
|
userSendFailureFactory = VerifiedUserSendFailureFactory(room),
|
||||||
featureFlagService = FakeFeatureFlagService(
|
|
||||||
initialState = mapOf(
|
|
||||||
FeatureFlags.MediaCaptionCreation.key to allowCaption,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
dateFormatter = FakeDateFormatter(),
|
dateFormatter = FakeDateFormatter(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,6 @@ import io.element.android.features.messages.test.attachments.video.FakeMediaOpti
|
||||||
import io.element.android.libraries.androidutils.file.TemporaryUriDeleter
|
import io.element.android.libraries.androidutils.file.TemporaryUriDeleter
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||||
import io.element.android.libraries.matrix.api.media.AudioInfo
|
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||||
|
|
@ -84,34 +82,8 @@ class AttachmentsPreviewPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state`() = runTest {
|
fun `present - initial state`() = runTest {
|
||||||
createAttachmentsPreviewPresenter().test {
|
createAttachmentsPreviewPresenter().test {
|
||||||
skipItems(1)
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
assertThat(initialState.allowCaption).isTrue()
|
|
||||||
assertThat(initialState.showCaptionCompatibilityWarning).isTrue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - initial state no caption warning`() = runTest {
|
|
||||||
createAttachmentsPreviewPresenter(
|
|
||||||
showCaptionCompatibilityWarning = false,
|
|
||||||
).test {
|
|
||||||
skipItems(1)
|
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState.showCaptionCompatibilityWarning).isFalse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - initial state - caption not allowed`() = runTest {
|
|
||||||
createAttachmentsPreviewPresenter(
|
|
||||||
allowCaption = false,
|
|
||||||
).test {
|
|
||||||
skipItems(1)
|
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(initialState.allowCaption).isFalse()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,7 +116,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = true))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = true))
|
||||||
|
|
@ -186,7 +157,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
// Pre-processing finishes
|
// Pre-processing finishes
|
||||||
processLatch.complete(Unit)
|
processLatch.complete(Unit)
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
||||||
|
|
@ -221,7 +191,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
// Pre-processing finishes
|
// Pre-processing finishes
|
||||||
|
|
@ -253,7 +222,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||||
// Pre-processing finishes
|
// Pre-processing finishes
|
||||||
processLatch.complete(Unit)
|
processLatch.complete(Unit)
|
||||||
|
|
@ -282,7 +250,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
processLatch.complete(Unit)
|
processLatch.complete(Unit)
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||||
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Failure::class.java)
|
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Failure::class.java)
|
||||||
}
|
}
|
||||||
|
|
@ -304,7 +271,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.CancelAndDismiss)
|
initialState.eventSink(AttachmentsPreviewEvents.CancelAndDismiss)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
||||||
deleteCallback.assertions().isCalledOnce()
|
deleteCallback.assertions().isCalledOnce()
|
||||||
onDoneListener.assertions().isCalledOnce()
|
onDoneListener.assertions().isCalledOnce()
|
||||||
|
|
@ -339,7 +305,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.textEditorState.setMarkdown(A_CAPTION)
|
initialState.textEditorState.setMarkdown(A_CAPTION)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||||
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java)
|
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
||||||
|
|
@ -383,7 +348,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.textEditorState.setMarkdown(A_CAPTION)
|
initialState.textEditorState.setMarkdown(A_CAPTION)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||||
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java)
|
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
||||||
|
|
@ -425,7 +389,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.textEditorState.setMarkdown(A_CAPTION)
|
initialState.textEditorState.setMarkdown(A_CAPTION)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||||
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java)
|
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
||||||
|
|
@ -442,38 +405,7 @@ class AttachmentsPreviewPresenterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - send media failure scenario without media queue`() = runTest {
|
fun `present - send media failure scenario`() = runTest {
|
||||||
val failure = MediaPreProcessor.Failure(null)
|
|
||||||
val sendFileResult =
|
|
||||||
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
|
||||||
Result.failure(failure)
|
|
||||||
}
|
|
||||||
val room = FakeJoinedRoom(
|
|
||||||
liveTimeline = FakeTimeline().apply {
|
|
||||||
sendFileLambda = sendFileResult
|
|
||||||
},
|
|
||||||
)
|
|
||||||
val presenter = createAttachmentsPreviewPresenter(room = room, mediaUploadOnSendQueueEnabled = false)
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
|
||||||
val failureState = awaitItem()
|
|
||||||
assertThat(failureState.sendActionState).isEqualTo(SendActionState.Failure(failure, mediaUploadInfo))
|
|
||||||
sendFileResult.assertions().isCalledOnce()
|
|
||||||
failureState.eventSink(AttachmentsPreviewEvents.CancelAndClearSendState)
|
|
||||||
val clearedState = awaitLastSequentialItem()
|
|
||||||
assertThat(clearedState.sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - send media failure scenario with media queue`() = runTest {
|
|
||||||
val failure = MediaPreProcessor.Failure(null)
|
val failure = MediaPreProcessor.Failure(null)
|
||||||
val sendFileResult =
|
val sendFileResult =
|
||||||
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||||
|
|
@ -485,14 +417,13 @@ class AttachmentsPreviewPresenterTest {
|
||||||
sendFileLambda = sendFileResult
|
sendFileLambda = sendFileResult
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
val presenter = createAttachmentsPreviewPresenter(room = room, mediaUploadOnSendQueueEnabled = true, onDoneListener = onDoneListenerResult)
|
val presenter = createAttachmentsPreviewPresenter(room = room, onDoneListener = onDoneListenerResult)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
||||||
|
|
||||||
|
|
@ -509,25 +440,7 @@ class AttachmentsPreviewPresenterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - dismissing the progress dialog stops media upload without media queue`() = runTest {
|
fun `present - dismissing the progress dialog stops media upload`() = runTest {
|
||||||
val presenter = createAttachmentsPreviewPresenter(mediaUploadOnSendQueueEnabled = false)
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.CancelAndClearSendState)
|
|
||||||
// The sending is cancelled and the state is kept at ReadyToUpload
|
|
||||||
ensureAllEventsConsumed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - dismissing the progress dialog stops media upload with media queue`() = runTest {
|
|
||||||
val onDoneListenerResult = lambdaRecorder<Unit> {}
|
val onDoneListenerResult = lambdaRecorder<Unit> {}
|
||||||
val presenter = createAttachmentsPreviewPresenter(
|
val presenter = createAttachmentsPreviewPresenter(
|
||||||
room = FakeJoinedRoom(
|
room = FakeJoinedRoom(
|
||||||
|
|
@ -537,7 +450,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
mediaUploadOnSendQueueEnabled = true,
|
|
||||||
onDoneListener = onDoneListenerResult,
|
onDoneListener = onDoneListenerResult,
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -546,7 +458,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.CancelAndClearSendState)
|
initialState.eventSink(AttachmentsPreviewEvents.CancelAndClearSendState)
|
||||||
|
|
@ -574,7 +485,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
mediaUploadOnSendQueueEnabled = true,
|
|
||||||
onDoneListener = onDoneListenerResult,
|
onDoneListener = onDoneListenerResult,
|
||||||
mediaOptimizationSelectorPresenterFactory = FakeMediaOptimizationSelectorPresenterFactory {
|
mediaOptimizationSelectorPresenterFactory = FakeMediaOptimizationSelectorPresenterFactory {
|
||||||
MediaOptimizationSelectorState(
|
MediaOptimizationSelectorState(
|
||||||
|
|
@ -616,7 +526,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
mediaUploadOnSendQueueEnabled = true,
|
|
||||||
onDoneListener = onDoneListenerResult,
|
onDoneListener = onDoneListenerResult,
|
||||||
mediaOptimizationSelectorPresenterFactory = FakeMediaOptimizationSelectorPresenterFactory {
|
mediaOptimizationSelectorPresenterFactory = FakeMediaOptimizationSelectorPresenterFactory {
|
||||||
MediaOptimizationSelectorState(
|
MediaOptimizationSelectorState(
|
||||||
|
|
@ -672,9 +581,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
mediaPreProcessor: MediaPreProcessor = FakeMediaPreProcessor(),
|
mediaPreProcessor: MediaPreProcessor = FakeMediaPreProcessor(),
|
||||||
temporaryUriDeleter: TemporaryUriDeleter = FakeTemporaryUriDeleter(),
|
temporaryUriDeleter: TemporaryUriDeleter = FakeTemporaryUriDeleter(),
|
||||||
onDoneListener: OnDoneListener = OnDoneListener { lambdaError() },
|
onDoneListener: OnDoneListener = OnDoneListener { lambdaError() },
|
||||||
mediaUploadOnSendQueueEnabled: Boolean = true,
|
|
||||||
allowCaption: Boolean = true,
|
|
||||||
showCaptionCompatibilityWarning: Boolean = true,
|
|
||||||
displayMediaQualitySelectorViews: Boolean = false,
|
displayMediaQualitySelectorViews: Boolean = false,
|
||||||
mediaOptimizationSelectorPresenterFactory: FakeMediaOptimizationSelectorPresenterFactory = FakeMediaOptimizationSelectorPresenterFactory(
|
mediaOptimizationSelectorPresenterFactory: FakeMediaOptimizationSelectorPresenterFactory = FakeMediaOptimizationSelectorPresenterFactory(
|
||||||
fakePresenter = {
|
fakePresenter = {
|
||||||
|
|
@ -698,13 +604,6 @@ class AttachmentsPreviewPresenterTest {
|
||||||
}),
|
}),
|
||||||
permalinkBuilder = permalinkBuilder,
|
permalinkBuilder = permalinkBuilder,
|
||||||
temporaryUriDeleter = temporaryUriDeleter,
|
temporaryUriDeleter = temporaryUriDeleter,
|
||||||
featureFlagService = FakeFeatureFlagService(
|
|
||||||
initialState = mapOf(
|
|
||||||
FeatureFlags.MediaUploadOnSendQueue.key to mediaUploadOnSendQueueEnabled,
|
|
||||||
FeatureFlags.MediaCaptionCreation.key to allowCaption,
|
|
||||||
FeatureFlags.MediaCaptionWarning.key to showCaptionCompatibilityWarning,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
sessionCoroutineScope = this,
|
sessionCoroutineScope = this,
|
||||||
dispatchers = testCoroutineDispatchers(),
|
dispatchers = testCoroutineDispatchers(),
|
||||||
mediaOptimizationSelectorPresenterFactory = mediaOptimizationSelectorPresenterFactory,
|
mediaOptimizationSelectorPresenterFactory = mediaOptimizationSelectorPresenterFactory,
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ import io.element.android.features.poll.test.pollcontent.FakePollContentStateFac
|
||||||
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
||||||
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
||||||
import io.element.android.libraries.eventformatter.api.TimelineEventFormatter
|
import io.element.android.libraries.eventformatter.api.TimelineEventFormatter
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
|
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
|
||||||
|
|
@ -60,7 +59,6 @@ internal fun TestScope.aTimelineItemsFactory(
|
||||||
messageFactory = TimelineItemContentMessageFactory(
|
messageFactory = TimelineItemContentMessageFactory(
|
||||||
fileSizeFormatter = FakeFileSizeFormatter(),
|
fileSizeFormatter = FakeFileSizeFormatter(),
|
||||||
fileExtensionExtractor = FileExtensionExtractorWithoutValidation(),
|
fileExtensionExtractor = FileExtensionExtractorWithoutValidation(),
|
||||||
featureFlagService = FakeFeatureFlagService(),
|
|
||||||
htmlConverterProvider = FakeHtmlConverterProvider(),
|
htmlConverterProvider = FakeHtmlConverterProvider(),
|
||||||
permalinkParser = FakePermalinkParser(),
|
permalinkParser = FakePermalinkParser(),
|
||||||
textPillificationHelper = FakeTextPillificationHelper(),
|
textPillificationHelper = FakeTextPillificationHelper(),
|
||||||
|
|
@ -70,7 +68,7 @@ internal fun TestScope.aTimelineItemsFactory(
|
||||||
fileSizeFormatter = FakeFileSizeFormatter(),
|
fileSizeFormatter = FakeFileSizeFormatter(),
|
||||||
fileExtensionExtractor = FileExtensionExtractorWithoutValidation()
|
fileExtensionExtractor = FileExtensionExtractorWithoutValidation()
|
||||||
),
|
),
|
||||||
pollFactory = TimelineItemContentPollFactory(FakeFeatureFlagService(), FakePollContentStateFactory()),
|
pollFactory = TimelineItemContentPollFactory(FakePollContentStateFactory()),
|
||||||
utdFactory = TimelineItemContentUTDFactory(),
|
utdFactory = TimelineItemContentUTDFactory(),
|
||||||
roomMembershipFactory = TimelineItemContentRoomMembershipFactory(timelineEventFormatter),
|
roomMembershipFactory = TimelineItemContentRoomMembershipFactory(timelineEventFormatter),
|
||||||
profileChangeFactory = TimelineItemContentProfileChangeFactory(timelineEventFormatter),
|
profileChangeFactory = TimelineItemContentProfileChangeFactory(timelineEventFormatter),
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,6 @@ import io.element.android.features.messages.impl.utils.FakeTextPillificationHelp
|
||||||
import io.element.android.features.messages.impl.utils.TextPillificationHelper
|
import io.element.android.features.messages.impl.utils.TextPillificationHelper
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
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.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||||
|
|
@ -125,9 +122,6 @@ class MessageComposerPresenterTest {
|
||||||
private val pickerProvider = FakePickerProvider().apply {
|
private val pickerProvider = FakePickerProvider().apply {
|
||||||
givenResult(mockk()) // Uri is not available in JVM, so the only way to have a non-null Uri is using Mockk
|
givenResult(mockk()) // Uri is not available in JVM, so the only way to have a non-null Uri is using Mockk
|
||||||
}
|
}
|
||||||
private val featureFlagService = FakeFeatureFlagService(
|
|
||||||
mapOf(FeatureFlags.LocationSharing.key to true)
|
|
||||||
)
|
|
||||||
private val mediaPreProcessor = FakeMediaPreProcessor()
|
private val mediaPreProcessor = FakeMediaPreProcessor()
|
||||||
private val snackbarDispatcher = SnackbarDispatcher()
|
private val snackbarDispatcher = SnackbarDispatcher()
|
||||||
private val mockMediaUrl: Uri = mockk("localMediaUri")
|
private val mockMediaUrl: Uri = mockk("localMediaUri")
|
||||||
|
|
@ -1529,7 +1523,6 @@ class MessageComposerPresenterTest {
|
||||||
),
|
),
|
||||||
navigator: MessagesNavigator = FakeMessagesNavigator(),
|
navigator: MessagesNavigator = FakeMessagesNavigator(),
|
||||||
pickerProvider: PickerProvider = this@MessageComposerPresenterTest.pickerProvider,
|
pickerProvider: PickerProvider = this@MessageComposerPresenterTest.pickerProvider,
|
||||||
featureFlagService: FeatureFlagService = this@MessageComposerPresenterTest.featureFlagService,
|
|
||||||
locationService: LocationService = FakeLocationService(true),
|
locationService: LocationService = FakeLocationService(true),
|
||||||
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),
|
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),
|
||||||
mediaPreProcessor: MediaPreProcessor = this@MessageComposerPresenterTest.mediaPreProcessor,
|
mediaPreProcessor: MediaPreProcessor = this@MessageComposerPresenterTest.mediaPreProcessor,
|
||||||
|
|
@ -1551,7 +1544,6 @@ class MessageComposerPresenterTest {
|
||||||
sessionCoroutineScope = this,
|
sessionCoroutineScope = this,
|
||||||
room = room,
|
room = room,
|
||||||
mediaPickerProvider = pickerProvider,
|
mediaPickerProvider = pickerProvider,
|
||||||
featureFlagService = featureFlagService,
|
|
||||||
sessionPreferencesStore = sessionPreferencesStore,
|
sessionPreferencesStore = sessionPreferencesStore,
|
||||||
localMediaFactory = localMediaFactory,
|
localMediaFactory = localMediaFactory,
|
||||||
mediaSender = MediaSender(
|
mediaSender = MediaSender(
|
||||||
|
|
@ -1593,11 +1585,9 @@ fun anEditMode(
|
||||||
fun anEditCaptionMode(
|
fun anEditCaptionMode(
|
||||||
eventOrTransactionId: EventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(),
|
eventOrTransactionId: EventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(),
|
||||||
caption: String = A_CAPTION,
|
caption: String = A_CAPTION,
|
||||||
showCaptionCompatibilityWarning: Boolean = false,
|
|
||||||
) = MessageComposerMode.EditCaption(
|
) = MessageComposerMode.EditCaption(
|
||||||
eventOrTransactionId = eventOrTransactionId,
|
eventOrTransactionId = eventOrTransactionId,
|
||||||
content = caption,
|
content = caption,
|
||||||
showCaptionCompatibilityWarning = showCaptionCompatibilityWarning,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun aReplyMode() = MessageComposerMode.Reply(
|
fun aReplyMode() = MessageComposerMode.Reply(
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ package io.element.android.features.messages.impl.pinned.banner
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProvider
|
import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProvider
|
||||||
import io.element.android.libraries.eventformatter.test.FakePinnedMessagesBannerFormatter
|
import io.element.android.libraries.eventformatter.test.FakePinnedMessagesBannerFormatter
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||||
|
|
@ -35,7 +33,7 @@ import org.junit.Test
|
||||||
class PinnedMessagesBannerPresenterTest {
|
class PinnedMessagesBannerPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state`() = runTest {
|
fun `present - initial state`() = runTest {
|
||||||
val presenter = createPinnedMessagesBannerPresenter(isFeatureEnabled = true)
|
val presenter = createPinnedMessagesBannerPresenter()
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState).isEqualTo(PinnedMessagesBannerState.Hidden)
|
assertThat(initialState).isEqualTo(PinnedMessagesBannerState.Hidden)
|
||||||
|
|
@ -43,15 +41,6 @@ class PinnedMessagesBannerPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - feature disabled`() = runTest {
|
|
||||||
val presenter = createPinnedMessagesBannerPresenter(isFeatureEnabled = false)
|
|
||||||
presenter.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState).isEqualTo(PinnedMessagesBannerState.Hidden)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - loading state`() = runTest {
|
fun `present - loading state`() = runTest {
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
|
|
@ -188,14 +177,10 @@ class PinnedMessagesBannerPresenterTest {
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
syncService: SyncService = FakeSyncService(),
|
syncService: SyncService = FakeSyncService(),
|
||||||
isFeatureEnabled: Boolean = true,
|
|
||||||
): PinnedMessagesBannerPresenter {
|
): PinnedMessagesBannerPresenter {
|
||||||
val timelineProvider = PinnedEventsTimelineProvider(
|
val timelineProvider = PinnedEventsTimelineProvider(
|
||||||
room = room,
|
room = room,
|
||||||
syncService = syncService,
|
syncService = syncService,
|
||||||
featureFlagService = FakeFeatureFlagService(
|
|
||||||
initialState = mapOf(FeatureFlags.PinnedEvents.key to isFeatureEnabled)
|
|
||||||
),
|
|
||||||
dispatchers = testCoroutineDispatchers(),
|
dispatchers = testCoroutineDispatchers(),
|
||||||
)
|
)
|
||||||
timelineProvider.launchIn(backgroundScope)
|
timelineProvider.launchIn(backgroundScope)
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@ import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProv
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState
|
import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||||
|
|
@ -51,34 +49,17 @@ import org.junit.Test
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
class PinnedMessagesListPresenterTest {
|
class PinnedMessagesListPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state feature disabled`() = runTest {
|
fun `present - initial state`() = runTest {
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
canRedactOwnResult = { Result.success(true) },
|
canRedactOwnResult = { Result.success(true) },
|
||||||
canRedactOtherResult = { Result.success(true) },
|
canRedactOtherResult = { Result.success(true) },
|
||||||
canUserPinUnpinResult = { Result.success(true) },
|
canUserPinUnpinResult = { Result.success(true) },
|
||||||
|
).apply {
|
||||||
|
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
||||||
|
}
|
||||||
)
|
)
|
||||||
)
|
val presenter = createPinnedMessagesListPresenter(room = room)
|
||||||
val presenter = createPinnedMessagesListPresenter(room = room, isFeatureEnabled = false)
|
|
||||||
presenter.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState).isEqualTo(PinnedMessagesListState.Loading)
|
|
||||||
cancelAndIgnoreRemainingEvents()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - initial state feature enabled`() = runTest {
|
|
||||||
val room = FakeJoinedRoom(
|
|
||||||
baseRoom = FakeBaseRoom(
|
|
||||||
canRedactOwnResult = { Result.success(true) },
|
|
||||||
canRedactOtherResult = { Result.success(true) },
|
|
||||||
canUserPinUnpinResult = { Result.success(true) },
|
|
||||||
).apply {
|
|
||||||
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
val presenter = createPinnedMessagesListPresenter(room = room, isFeatureEnabled = true)
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState).isEqualTo(PinnedMessagesListState.Loading)
|
assertThat(initialState).isEqualTo(PinnedMessagesListState.Loading)
|
||||||
|
|
@ -90,15 +71,15 @@ class PinnedMessagesListPresenterTest {
|
||||||
fun `present - timeline failure state`() = runTest {
|
fun `present - timeline failure state`() = runTest {
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
canRedactOwnResult = { Result.success(true) },
|
canRedactOwnResult = { Result.success(true) },
|
||||||
canRedactOtherResult = { Result.success(true) },
|
canRedactOtherResult = { Result.success(true) },
|
||||||
canUserPinUnpinResult = { Result.success(true) },
|
canUserPinUnpinResult = { Result.success(true) },
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
||||||
},
|
},
|
||||||
createTimelineResult = { Result.failure(RuntimeException()) },
|
createTimelineResult = { Result.failure(RuntimeException()) },
|
||||||
)
|
)
|
||||||
val presenter = createPinnedMessagesListPresenter(room = room, isFeatureEnabled = true)
|
val presenter = createPinnedMessagesListPresenter(room = room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(3)
|
skipItems(3)
|
||||||
val failureState = awaitItem()
|
val failureState = awaitItem()
|
||||||
|
|
@ -111,15 +92,15 @@ class PinnedMessagesListPresenterTest {
|
||||||
fun `present - empty state`() = runTest {
|
fun `present - empty state`() = runTest {
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
canRedactOwnResult = { Result.success(true) },
|
canRedactOwnResult = { Result.success(true) },
|
||||||
canRedactOtherResult = { Result.success(true) },
|
canRedactOtherResult = { Result.success(true) },
|
||||||
canUserPinUnpinResult = { Result.success(true) },
|
canUserPinUnpinResult = { Result.success(true) },
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf()))
|
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf()))
|
||||||
},
|
},
|
||||||
createTimelineResult = { Result.success(FakeTimeline()) },
|
createTimelineResult = { Result.success(FakeTimeline()) },
|
||||||
)
|
)
|
||||||
val presenter = createPinnedMessagesListPresenter(room = room, isFeatureEnabled = true)
|
val presenter = createPinnedMessagesListPresenter(room = room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(3)
|
skipItems(3)
|
||||||
val emptyState = awaitItem()
|
val emptyState = awaitItem()
|
||||||
|
|
@ -133,15 +114,15 @@ class PinnedMessagesListPresenterTest {
|
||||||
val pinnedEventsTimeline = createPinnedMessagesTimeline()
|
val pinnedEventsTimeline = createPinnedMessagesTimeline()
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
canRedactOwnResult = { Result.success(true) },
|
canRedactOwnResult = { Result.success(true) },
|
||||||
canRedactOtherResult = { Result.success(true) },
|
canRedactOtherResult = { Result.success(true) },
|
||||||
canUserPinUnpinResult = { Result.success(true) },
|
canUserPinUnpinResult = { Result.success(true) },
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
||||||
},
|
},
|
||||||
createTimelineResult = { Result.success(pinnedEventsTimeline) },
|
createTimelineResult = { Result.success(pinnedEventsTimeline) },
|
||||||
)
|
)
|
||||||
val presenter = createPinnedMessagesListPresenter(room = room, isFeatureEnabled = true)
|
val presenter = createPinnedMessagesListPresenter(room = room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(3)
|
skipItems(3)
|
||||||
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
||||||
|
|
@ -162,15 +143,15 @@ class PinnedMessagesListPresenterTest {
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
canRedactOwnResult = { Result.success(true) },
|
canRedactOwnResult = { Result.success(true) },
|
||||||
canRedactOtherResult = { Result.success(true) },
|
canRedactOtherResult = { Result.success(true) },
|
||||||
canUserPinUnpinResult = { Result.success(true) },
|
canUserPinUnpinResult = { Result.success(true) },
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
||||||
},
|
},
|
||||||
createTimelineResult = { Result.success(pinnedEventsTimeline) },
|
createTimelineResult = { Result.success(pinnedEventsTimeline) },
|
||||||
)
|
)
|
||||||
val presenter = createPinnedMessagesListPresenter(room = room, isFeatureEnabled = true, analyticsService = analyticsService)
|
val presenter = createPinnedMessagesListPresenter(room = room, analyticsService = analyticsService)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(3)
|
skipItems(3)
|
||||||
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
||||||
|
|
@ -210,15 +191,15 @@ class PinnedMessagesListPresenterTest {
|
||||||
val pinnedEventsTimeline = createPinnedMessagesTimeline()
|
val pinnedEventsTimeline = createPinnedMessagesTimeline()
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
canRedactOwnResult = { Result.success(true) },
|
canRedactOwnResult = { Result.success(true) },
|
||||||
canRedactOtherResult = { Result.success(true) },
|
canRedactOtherResult = { Result.success(true) },
|
||||||
canUserPinUnpinResult = { Result.success(true) },
|
canUserPinUnpinResult = { Result.success(true) },
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
||||||
},
|
},
|
||||||
createTimelineResult = { Result.success(pinnedEventsTimeline) },
|
createTimelineResult = { Result.success(pinnedEventsTimeline) },
|
||||||
)
|
)
|
||||||
val presenter = createPinnedMessagesListPresenter(room = room, navigator = navigator, isFeatureEnabled = true)
|
val presenter = createPinnedMessagesListPresenter(room = room, navigator = navigator)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(3)
|
skipItems(3)
|
||||||
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
||||||
|
|
@ -241,15 +222,15 @@ class PinnedMessagesListPresenterTest {
|
||||||
val pinnedEventsTimeline = createPinnedMessagesTimeline()
|
val pinnedEventsTimeline = createPinnedMessagesTimeline()
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
canRedactOwnResult = { Result.success(true) },
|
canRedactOwnResult = { Result.success(true) },
|
||||||
canRedactOtherResult = { Result.success(true) },
|
canRedactOtherResult = { Result.success(true) },
|
||||||
canUserPinUnpinResult = { Result.success(true) },
|
canUserPinUnpinResult = { Result.success(true) },
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
||||||
},
|
},
|
||||||
createTimelineResult = { Result.success(pinnedEventsTimeline) },
|
createTimelineResult = { Result.success(pinnedEventsTimeline) },
|
||||||
)
|
)
|
||||||
val presenter = createPinnedMessagesListPresenter(room = room, navigator = navigator, isFeatureEnabled = true)
|
val presenter = createPinnedMessagesListPresenter(room = room, navigator = navigator)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(3)
|
skipItems(3)
|
||||||
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
||||||
|
|
@ -272,15 +253,15 @@ class PinnedMessagesListPresenterTest {
|
||||||
val pinnedEventsTimeline = createPinnedMessagesTimeline()
|
val pinnedEventsTimeline = createPinnedMessagesTimeline()
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
canRedactOwnResult = { Result.success(true) },
|
canRedactOwnResult = { Result.success(true) },
|
||||||
canRedactOtherResult = { Result.success(true) },
|
canRedactOtherResult = { Result.success(true) },
|
||||||
canUserPinUnpinResult = { Result.success(true) },
|
canUserPinUnpinResult = { Result.success(true) },
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
|
||||||
},
|
},
|
||||||
createTimelineResult = { Result.success(pinnedEventsTimeline) },
|
createTimelineResult = { Result.success(pinnedEventsTimeline) },
|
||||||
)
|
)
|
||||||
val presenter = createPinnedMessagesListPresenter(room = room, navigator = navigator, isFeatureEnabled = true)
|
val presenter = createPinnedMessagesListPresenter(room = room, navigator = navigator)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(3)
|
skipItems(3)
|
||||||
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
||||||
|
|
@ -315,15 +296,11 @@ class PinnedMessagesListPresenterTest {
|
||||||
navigator: PinnedMessagesListNavigator = FakePinnedMessagesListNavigator(),
|
navigator: PinnedMessagesListNavigator = FakePinnedMessagesListNavigator(),
|
||||||
room: JoinedRoom = FakeJoinedRoom(),
|
room: JoinedRoom = FakeJoinedRoom(),
|
||||||
syncService: SyncService = FakeSyncService(),
|
syncService: SyncService = FakeSyncService(),
|
||||||
isFeatureEnabled: Boolean = true,
|
|
||||||
analyticsService: AnalyticsService = FakeAnalyticsService(),
|
analyticsService: AnalyticsService = FakeAnalyticsService(),
|
||||||
): PinnedMessagesListPresenter {
|
): PinnedMessagesListPresenter {
|
||||||
val timelineProvider = PinnedEventsTimelineProvider(
|
val timelineProvider = PinnedEventsTimelineProvider(
|
||||||
room = room,
|
room = room,
|
||||||
syncService = syncService,
|
syncService = syncService,
|
||||||
featureFlagService = FakeFeatureFlagService(
|
|
||||||
initialState = mapOf(FeatureFlags.PinnedEvents.key to isFeatureEnabled)
|
|
||||||
),
|
|
||||||
dispatchers = testCoroutineDispatchers(),
|
dispatchers = testCoroutineDispatchers(),
|
||||||
)
|
)
|
||||||
timelineProvider.launchIn(backgroundScope)
|
timelineProvider.launchIn(backgroundScope)
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,6 @@ import io.element.android.features.messages.impl.utils.FakeTextPillificationHelp
|
||||||
import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider
|
import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider
|
||||||
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.api.media.AudioDetails
|
import io.element.android.libraries.matrix.api.media.AudioDetails
|
||||||
import io.element.android.libraries.matrix.api.media.AudioInfo
|
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||||
import io.element.android.libraries.matrix.api.media.FileInfo
|
import io.element.android.libraries.matrix.api.media.FileInfo
|
||||||
|
|
@ -430,35 +427,6 @@ class TimelineItemContentMessageFactoryTest {
|
||||||
assertThat(result).isEqualTo(expected)
|
assertThat(result).isEqualTo(expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test create VoiceMessageType feature disabled`() = runTest {
|
|
||||||
val sut = createTimelineItemContentMessageFactory(
|
|
||||||
featureFlagService = FakeFeatureFlagService(
|
|
||||||
initialState = mapOf(
|
|
||||||
FeatureFlags.VoiceMessages.key to false,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val result = sut.create(
|
|
||||||
content = createMessageContent(type = VoiceMessageType("filename", null, null, MediaSource("url"), null, null)),
|
|
||||||
senderDisambiguatedDisplayName = "Bob",
|
|
||||||
eventId = AN_EVENT_ID,
|
|
||||||
)
|
|
||||||
val expected = TimelineItemAudioContent(
|
|
||||||
filename = "filename",
|
|
||||||
fileSize = 0L,
|
|
||||||
caption = null,
|
|
||||||
formattedCaption = null,
|
|
||||||
isEdited = false,
|
|
||||||
duration = Duration.ZERO,
|
|
||||||
mediaSource = MediaSource(url = "url", json = null),
|
|
||||||
mimeType = MimeTypes.OctetStream,
|
|
||||||
formattedFileSize = "0 Bytes",
|
|
||||||
fileExtension = ""
|
|
||||||
)
|
|
||||||
assertThat(result).isEqualTo(expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test create ImageMessageType`() = runTest {
|
fun `test create ImageMessageType`() = runTest {
|
||||||
val sut = createTimelineItemContentMessageFactory()
|
val sut = createTimelineItemContentMessageFactory()
|
||||||
|
|
@ -794,13 +762,11 @@ class TimelineItemContentMessageFactoryTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createTimelineItemContentMessageFactory(
|
private fun createTimelineItemContentMessageFactory(
|
||||||
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
|
|
||||||
htmlConverterTransform: (String) -> CharSequence = { it },
|
htmlConverterTransform: (String) -> CharSequence = { it },
|
||||||
permalinkParser: FakePermalinkParser = FakePermalinkParser(),
|
permalinkParser: FakePermalinkParser = FakePermalinkParser(),
|
||||||
) = TimelineItemContentMessageFactory(
|
) = TimelineItemContentMessageFactory(
|
||||||
fileSizeFormatter = FakeFileSizeFormatter(),
|
fileSizeFormatter = FakeFileSizeFormatter(),
|
||||||
fileExtensionExtractor = FileExtensionExtractorWithoutValidation(),
|
fileExtensionExtractor = FileExtensionExtractorWithoutValidation(),
|
||||||
featureFlagService = featureFlagService,
|
|
||||||
htmlConverterProvider = FakeHtmlConverterProvider(htmlConverterTransform),
|
htmlConverterProvider = FakeHtmlConverterProvider(htmlConverterTransform),
|
||||||
permalinkParser = permalinkParser,
|
permalinkParser = permalinkParser,
|
||||||
textPillificationHelper = FakeTextPillificationHelper(),
|
textPillificationHelper = FakeTextPillificationHelper(),
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,6 @@ import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
|
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.indicator.api.IndicatorService
|
import io.element.android.libraries.indicator.api.IndicatorService
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
||||||
|
|
@ -42,7 +40,6 @@ class PreferencesRootPresenter @Inject constructor(
|
||||||
private val analyticsService: AnalyticsService,
|
private val analyticsService: AnalyticsService,
|
||||||
private val versionFormatter: VersionFormatter,
|
private val versionFormatter: VersionFormatter,
|
||||||
private val snackbarDispatcher: SnackbarDispatcher,
|
private val snackbarDispatcher: SnackbarDispatcher,
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
private val indicatorService: IndicatorService,
|
private val indicatorService: IndicatorService,
|
||||||
private val directLogoutPresenter: Presenter<DirectLogoutState>,
|
private val directLogoutPresenter: Presenter<DirectLogoutState>,
|
||||||
private val showDeveloperSettingsProvider: ShowDeveloperSettingsProvider,
|
private val showDeveloperSettingsProvider: ShowDeveloperSettingsProvider,
|
||||||
|
|
@ -60,15 +57,6 @@ class PreferencesRootPresenter @Inject constructor(
|
||||||
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
||||||
val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() }
|
val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() }
|
||||||
|
|
||||||
val showNotificationSettings = remember { mutableStateOf(false) }
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
showNotificationSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.NotificationSettings)
|
|
||||||
}
|
|
||||||
val showLockScreenSettings = remember { mutableStateOf(false) }
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
showLockScreenSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.PinUnlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We should display the 'complete verification' option if the current session can be verified
|
// We should display the 'complete verification' option if the current session can be verified
|
||||||
val canVerifyUserSession by sessionVerificationService.needsSessionVerification.collectAsState(false)
|
val canVerifyUserSession by sessionVerificationService.needsSessionVerification.collectAsState(false)
|
||||||
|
|
||||||
|
|
@ -122,8 +110,6 @@ class PreferencesRootPresenter @Inject constructor(
|
||||||
canReportBug = canReportBug,
|
canReportBug = canReportBug,
|
||||||
showDeveloperSettings = showDeveloperSettings,
|
showDeveloperSettings = showDeveloperSettings,
|
||||||
canDeactivateAccount = canDeactivateAccount,
|
canDeactivateAccount = canDeactivateAccount,
|
||||||
showNotificationSettings = showNotificationSettings.value,
|
|
||||||
showLockScreenSettings = showLockScreenSettings.value,
|
|
||||||
showBlockedUsersItem = showBlockedUsersItem,
|
showBlockedUsersItem = showBlockedUsersItem,
|
||||||
directLogoutState = directLogoutState,
|
directLogoutState = directLogoutState,
|
||||||
snackbarMessage = snackbarMessage,
|
snackbarMessage = snackbarMessage,
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,6 @@ data class PreferencesRootState(
|
||||||
val showAnalyticsSettings: Boolean,
|
val showAnalyticsSettings: Boolean,
|
||||||
val showDeveloperSettings: Boolean,
|
val showDeveloperSettings: Boolean,
|
||||||
val canDeactivateAccount: Boolean,
|
val canDeactivateAccount: Boolean,
|
||||||
val showLockScreenSettings: Boolean,
|
|
||||||
val showNotificationSettings: Boolean,
|
|
||||||
val showBlockedUsersItem: Boolean,
|
val showBlockedUsersItem: Boolean,
|
||||||
val directLogoutState: DirectLogoutState,
|
val directLogoutState: DirectLogoutState,
|
||||||
val snackbarMessage: SnackbarMessage?,
|
val snackbarMessage: SnackbarMessage?,
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,6 @@ fun aPreferencesRootState(
|
||||||
showAnalyticsSettings = true,
|
showAnalyticsSettings = true,
|
||||||
canReportBug = true,
|
canReportBug = true,
|
||||||
showDeveloperSettings = true,
|
showDeveloperSettings = true,
|
||||||
showNotificationSettings = true,
|
|
||||||
showLockScreenSettings = true,
|
|
||||||
showBlockedUsersItem = true,
|
showBlockedUsersItem = true,
|
||||||
canDeactivateAccount = true,
|
canDeactivateAccount = true,
|
||||||
snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete),
|
snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete),
|
||||||
|
|
|
||||||
|
|
@ -121,20 +121,16 @@ private fun ColumnScope.ManageAppSection(
|
||||||
onOpenLockScreenSettings: () -> Unit,
|
onOpenLockScreenSettings: () -> Unit,
|
||||||
onSecureBackupClick: () -> Unit,
|
onSecureBackupClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
if (state.showNotificationSettings) {
|
ListItem(
|
||||||
ListItem(
|
headlineContent = { Text(stringResource(id = R.string.screen_notification_settings_title)) },
|
||||||
headlineContent = { Text(stringResource(id = R.string.screen_notification_settings_title)) },
|
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Notifications())),
|
||||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Notifications())),
|
onClick = onOpenNotificationSettings,
|
||||||
onClick = onOpenNotificationSettings,
|
)
|
||||||
)
|
ListItem(
|
||||||
}
|
headlineContent = { Text(stringResource(id = CommonStrings.common_screen_lock)) },
|
||||||
if (state.showLockScreenSettings) {
|
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())),
|
||||||
ListItem(
|
onClick = onOpenLockScreenSettings,
|
||||||
headlineContent = { Text(stringResource(id = CommonStrings.common_screen_lock)) },
|
)
|
||||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())),
|
|
||||||
onClick = onOpenLockScreenSettings,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (state.showSecureBackup) {
|
if (state.showSecureBackup) {
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = { Text(stringResource(id = CommonStrings.common_encryption)) },
|
headlineContent = { Text(stringResource(id = CommonStrings.common_encryption)) },
|
||||||
|
|
@ -143,9 +139,7 @@ private fun ColumnScope.ManageAppSection(
|
||||||
onClick = onSecureBackupClick,
|
onClick = onSecureBackupClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (state.showNotificationSettings || state.showLockScreenSettings || state.showSecureBackup) {
|
HorizontalDivider()
|
||||||
HorizontalDivider()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import io.element.android.features.preferences.impl.utils.ShowDeveloperSettingsP
|
||||||
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||||
import io.element.android.libraries.core.meta.BuildType
|
import io.element.android.libraries.core.meta.BuildType
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
|
||||||
import io.element.android.libraries.indicator.api.IndicatorService
|
import io.element.android.libraries.indicator.api.IndicatorService
|
||||||
import io.element.android.libraries.indicator.test.FakeIndicatorService
|
import io.element.android.libraries.indicator.test.FakeIndicatorService
|
||||||
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
||||||
|
|
@ -77,8 +76,6 @@ class PreferencesRootPresenterTest {
|
||||||
assertThat(loadedState.devicesManagementUrl).isNull()
|
assertThat(loadedState.devicesManagementUrl).isNull()
|
||||||
assertThat(loadedState.showAnalyticsSettings).isFalse()
|
assertThat(loadedState.showAnalyticsSettings).isFalse()
|
||||||
assertThat(loadedState.showDeveloperSettings).isTrue()
|
assertThat(loadedState.showDeveloperSettings).isTrue()
|
||||||
assertThat(loadedState.showLockScreenSettings).isTrue()
|
|
||||||
assertThat(loadedState.showNotificationSettings).isTrue()
|
|
||||||
assertThat(loadedState.canDeactivateAccount).isTrue()
|
assertThat(loadedState.canDeactivateAccount).isTrue()
|
||||||
assertThat(loadedState.canReportBug).isTrue()
|
assertThat(loadedState.canReportBug).isTrue()
|
||||||
assertThat(loadedState.directLogoutState).isEqualTo(aDirectLogoutState())
|
assertThat(loadedState.directLogoutState).isEqualTo(aDirectLogoutState())
|
||||||
|
|
@ -194,7 +191,6 @@ class PreferencesRootPresenterTest {
|
||||||
analyticsService = FakeAnalyticsService(),
|
analyticsService = FakeAnalyticsService(),
|
||||||
versionFormatter = FakeVersionFormatter(),
|
versionFormatter = FakeVersionFormatter(),
|
||||||
snackbarDispatcher = SnackbarDispatcher(),
|
snackbarDispatcher = SnackbarDispatcher(),
|
||||||
featureFlagService = FakeFeatureFlagService(),
|
|
||||||
indicatorService = indicatorService,
|
indicatorService = indicatorService,
|
||||||
directLogoutPresenter = { aDirectLogoutState() },
|
directLogoutPresenter = { aDirectLogoutState() },
|
||||||
showDeveloperSettingsProvider = showDeveloperSettingsProvider,
|
showDeveloperSettingsProvider = showDeveloperSettingsProvider,
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,12 @@ import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.produceState
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import im.vector.app.features.analytics.plan.Interaction
|
import im.vector.app.features.analytics.plan.Interaction
|
||||||
import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
||||||
import io.element.android.features.leaveroom.api.LeaveRoomState
|
import io.element.android.features.leaveroom.api.LeaveRoomState
|
||||||
import io.element.android.features.messages.api.pinned.IsPinnedMessagesFeatureEnabled
|
|
||||||
import io.element.android.features.roomcall.api.RoomCallState
|
import io.element.android.features.roomcall.api.RoomCallState
|
||||||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter
|
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter
|
||||||
import io.element.android.features.roomdetails.impl.securityandprivacy.permissions.securityAndPrivacyPermissionsAsState
|
import io.element.android.features.roomdetails.impl.securityandprivacy.permissions.securityAndPrivacyPermissionsAsState
|
||||||
|
|
@ -69,7 +67,6 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
private val roomCallStatePresenter: Presenter<RoomCallState>,
|
private val roomCallStatePresenter: Presenter<RoomCallState>,
|
||||||
private val dispatchers: CoroutineDispatchers,
|
private val dispatchers: CoroutineDispatchers,
|
||||||
private val analyticsService: AnalyticsService,
|
private val analyticsService: AnalyticsService,
|
||||||
private val isPinnedMessagesFeatureEnabled: IsPinnedMessagesFeatureEnabled,
|
|
||||||
private val clipboardHelper: ClipboardHelper,
|
private val clipboardHelper: ClipboardHelper,
|
||||||
private val appPreferencesStore: AppPreferencesStore,
|
private val appPreferencesStore: AppPreferencesStore,
|
||||||
) : Presenter<RoomDetailsState> {
|
) : Presenter<RoomDetailsState> {
|
||||||
|
|
@ -77,7 +74,6 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
override fun present(): RoomDetailsState {
|
override fun present(): RoomDetailsState {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val leaveRoomState = leaveRoomPresenter.present()
|
val leaveRoomState = leaveRoomPresenter.present()
|
||||||
val canShowNotificationSettings = remember { mutableStateOf(false) }
|
|
||||||
val roomInfo by room.roomInfoFlow.collectAsState()
|
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||||
val isUserAdmin = room.isOwnUserAdmin()
|
val isUserAdmin = room.isOwnUserAdmin()
|
||||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||||
|
|
@ -88,19 +84,11 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
val isFavorite by remember { derivedStateOf { roomInfo.isFavorite } }
|
val isFavorite by remember { derivedStateOf { roomInfo.isFavorite } }
|
||||||
val joinRule by remember { derivedStateOf { roomInfo.joinRule } }
|
val joinRule by remember { derivedStateOf { roomInfo.joinRule } }
|
||||||
|
|
||||||
val canShowPinnedMessages = isPinnedMessagesFeatureEnabled()
|
|
||||||
val pinnedMessagesCount by remember { derivedStateOf { roomInfo.pinnedEventIds.size } }
|
val pinnedMessagesCount by remember { derivedStateOf { roomInfo.pinnedEventIds.size } }
|
||||||
|
|
||||||
val canShowMediaGallery by remember {
|
|
||||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.MediaGallery)
|
|
||||||
}.collectAsState(false)
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
canShowNotificationSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.NotificationSettings)
|
room.updateRoomNotificationSettings()
|
||||||
if (canShowNotificationSettings.value) {
|
observeNotificationSettings()
|
||||||
room.updateRoomNotificationSettings()
|
|
||||||
observeNotificationSettings()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val membersState by room.membersStateFlow.collectAsState()
|
val membersState by room.membersStateFlow.collectAsState()
|
||||||
|
|
@ -197,7 +185,6 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
isEncrypted = isEncrypted,
|
isEncrypted = isEncrypted,
|
||||||
canInvite = canInvite,
|
canInvite = canInvite,
|
||||||
canEdit = (canEditAvatar || canEditName || canEditTopic) && roomType == RoomDetailsType.Room,
|
canEdit = (canEditAvatar || canEditName || canEditTopic) && roomType == RoomDetailsType.Room,
|
||||||
canShowNotificationSettings = canShowNotificationSettings.value,
|
|
||||||
roomCallState = roomCallState,
|
roomCallState = roomCallState,
|
||||||
roomType = roomType,
|
roomType = roomType,
|
||||||
roomMemberDetailsState = roomMemberDetailsState,
|
roomMemberDetailsState = roomMemberDetailsState,
|
||||||
|
|
@ -207,8 +194,6 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
displayRolesAndPermissionsSettings = !isDm && isUserAdmin,
|
displayRolesAndPermissionsSettings = !isDm && isUserAdmin,
|
||||||
isPublic = joinRule == JoinRule.Public,
|
isPublic = joinRule == JoinRule.Public,
|
||||||
heroes = roomInfo.heroes.toPersistentList(),
|
heroes = roomInfo.heroes.toPersistentList(),
|
||||||
canShowPinnedMessages = canShowPinnedMessages,
|
|
||||||
canShowMediaGallery = canShowMediaGallery,
|
|
||||||
pinnedMessagesCount = pinnedMessagesCount,
|
pinnedMessagesCount = pinnedMessagesCount,
|
||||||
snackbarMessage = snackbarMessage,
|
snackbarMessage = snackbarMessage,
|
||||||
canShowKnockRequests = canShowKnockRequests,
|
canShowKnockRequests = canShowKnockRequests,
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ data class RoomDetailsState(
|
||||||
val roomMemberDetailsState: UserProfileState?,
|
val roomMemberDetailsState: UserProfileState?,
|
||||||
val canEdit: Boolean,
|
val canEdit: Boolean,
|
||||||
val canInvite: Boolean,
|
val canInvite: Boolean,
|
||||||
val canShowNotificationSettings: Boolean,
|
|
||||||
val roomCallState: RoomCallState,
|
val roomCallState: RoomCallState,
|
||||||
val leaveRoomState: LeaveRoomState,
|
val leaveRoomState: LeaveRoomState,
|
||||||
val roomNotificationSettings: RoomNotificationSettings?,
|
val roomNotificationSettings: RoomNotificationSettings?,
|
||||||
|
|
@ -40,8 +39,6 @@ data class RoomDetailsState(
|
||||||
val displayRolesAndPermissionsSettings: Boolean,
|
val displayRolesAndPermissionsSettings: Boolean,
|
||||||
val isPublic: Boolean,
|
val isPublic: Boolean,
|
||||||
val heroes: ImmutableList<MatrixUser>,
|
val heroes: ImmutableList<MatrixUser>,
|
||||||
val canShowPinnedMessages: Boolean,
|
|
||||||
val canShowMediaGallery: Boolean,
|
|
||||||
val pinnedMessagesCount: Int?,
|
val pinnedMessagesCount: Int?,
|
||||||
val snackbarMessage: SnackbarMessage?,
|
val snackbarMessage: SnackbarMessage?,
|
||||||
val canShowKnockRequests: Boolean,
|
val canShowKnockRequests: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,6 @@ fun aRoomDetailsState(
|
||||||
isEncrypted: Boolean = true,
|
isEncrypted: Boolean = true,
|
||||||
canInvite: Boolean = false,
|
canInvite: Boolean = false,
|
||||||
canEdit: Boolean = false,
|
canEdit: Boolean = false,
|
||||||
canShowNotificationSettings: Boolean = true,
|
|
||||||
roomCallState: RoomCallState = aStandByCallState(),
|
roomCallState: RoomCallState = aStandByCallState(),
|
||||||
roomType: RoomDetailsType = RoomDetailsType.Room,
|
roomType: RoomDetailsType = RoomDetailsType.Room,
|
||||||
roomMemberDetailsState: UserProfileState? = null,
|
roomMemberDetailsState: UserProfileState? = null,
|
||||||
|
|
@ -110,8 +109,6 @@ fun aRoomDetailsState(
|
||||||
displayAdminSettings: Boolean = false,
|
displayAdminSettings: Boolean = false,
|
||||||
isPublic: Boolean = true,
|
isPublic: Boolean = true,
|
||||||
heroes: List<MatrixUser> = emptyList(),
|
heroes: List<MatrixUser> = emptyList(),
|
||||||
canShowPinnedMessages: Boolean = true,
|
|
||||||
canShowMediaGallery: Boolean = true,
|
|
||||||
pinnedMessagesCount: Int? = null,
|
pinnedMessagesCount: Int? = null,
|
||||||
snackbarMessage: SnackbarMessage? = null,
|
snackbarMessage: SnackbarMessage? = null,
|
||||||
canShowKnockRequests: Boolean = false,
|
canShowKnockRequests: Boolean = false,
|
||||||
|
|
@ -132,7 +129,6 @@ fun aRoomDetailsState(
|
||||||
isEncrypted = isEncrypted,
|
isEncrypted = isEncrypted,
|
||||||
canInvite = canInvite,
|
canInvite = canInvite,
|
||||||
canEdit = canEdit,
|
canEdit = canEdit,
|
||||||
canShowNotificationSettings = canShowNotificationSettings,
|
|
||||||
roomCallState = roomCallState,
|
roomCallState = roomCallState,
|
||||||
roomType = roomType,
|
roomType = roomType,
|
||||||
roomMemberDetailsState = roomMemberDetailsState,
|
roomMemberDetailsState = roomMemberDetailsState,
|
||||||
|
|
@ -142,8 +138,6 @@ fun aRoomDetailsState(
|
||||||
displayRolesAndPermissionsSettings = displayAdminSettings,
|
displayRolesAndPermissionsSettings = displayAdminSettings,
|
||||||
isPublic = isPublic,
|
isPublic = isPublic,
|
||||||
heroes = heroes.toPersistentList(),
|
heroes = heroes.toPersistentList(),
|
||||||
canShowPinnedMessages = canShowPinnedMessages,
|
|
||||||
canShowMediaGallery = canShowMediaGallery,
|
|
||||||
pinnedMessagesCount = pinnedMessagesCount,
|
pinnedMessagesCount = pinnedMessagesCount,
|
||||||
snackbarMessage = snackbarMessage,
|
snackbarMessage = snackbarMessage,
|
||||||
canShowKnockRequests = canShowKnockRequests,
|
canShowKnockRequests = canShowKnockRequests,
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ fun RoomDetailsView(
|
||||||
}
|
}
|
||||||
|
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
if (state.canShowNotificationSettings && state.roomNotificationSettings != null) {
|
if (state.roomNotificationSettings != null) {
|
||||||
NotificationItem(
|
NotificationItem(
|
||||||
isDefaultMode = state.roomNotificationSettings.isDefault,
|
isDefaultMode = state.roomNotificationSettings.isDefault,
|
||||||
openRoomNotificationSettings = openRoomNotificationSettings
|
openRoomNotificationSettings = openRoomNotificationSettings
|
||||||
|
|
@ -237,20 +237,16 @@ fun RoomDetailsView(
|
||||||
}
|
}
|
||||||
|
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
if (state.canShowPinnedMessages) {
|
PinnedMessagesItem(
|
||||||
PinnedMessagesItem(
|
pinnedMessagesCount = state.pinnedMessagesCount,
|
||||||
pinnedMessagesCount = state.pinnedMessagesCount,
|
onPinnedMessagesClick = onPinnedMessagesClick
|
||||||
onPinnedMessagesClick = onPinnedMessagesClick
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
PollsItem(
|
PollsItem(
|
||||||
openPollHistory = openPollHistory
|
openPollHistory = openPollHistory
|
||||||
)
|
)
|
||||||
if (state.canShowMediaGallery) {
|
MediaGalleryItem(
|
||||||
MediaGalleryItem(
|
onClick = openMediaGallery
|
||||||
onClick = openMediaGallery
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.roomType is RoomDetailsType.Dm && state.roomMemberDetailsState != null) {
|
if (state.roomType is RoomDetailsType.Dm && state.roomMemberDetailsState != null) {
|
||||||
|
|
@ -337,8 +333,7 @@ private fun MainActionsSection(
|
||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||||
) {
|
) {
|
||||||
val roomNotificationSettings = state.roomNotificationSettings
|
state.roomNotificationSettings?.let { roomNotificationSettings ->
|
||||||
if (state.canShowNotificationSettings && roomNotificationSettings != null) {
|
|
||||||
if (roomNotificationSettings.mode == RoomNotificationMode.MUTE) {
|
if (roomNotificationSettings.mode == RoomNotificationMode.MUTE) {
|
||||||
MainActionButton(
|
MainActionButton(
|
||||||
title = stringResource(CommonStrings.common_unmute),
|
title = stringResource(CommonStrings.common_unmute),
|
||||||
|
|
|
||||||
|
|
@ -79,11 +79,9 @@ class RoomDetailsPresenterTest {
|
||||||
analyticsService: AnalyticsService = FakeAnalyticsService(),
|
analyticsService: AnalyticsService = FakeAnalyticsService(),
|
||||||
featureFlagService: FeatureFlagService = FakeFeatureFlagService(
|
featureFlagService: FeatureFlagService = FakeFeatureFlagService(
|
||||||
mapOf(
|
mapOf(
|
||||||
FeatureFlags.NotificationSettings.key to true,
|
|
||||||
FeatureFlags.Knock.key to false,
|
FeatureFlags.Knock.key to false,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
isPinnedMessagesFeatureEnabled: Boolean = true,
|
|
||||||
encryptionService: FakeEncryptionService = FakeEncryptionService(),
|
encryptionService: FakeEncryptionService = FakeEncryptionService(),
|
||||||
clipboardHelper: ClipboardHelper = FakeClipboardHelper(),
|
clipboardHelper: ClipboardHelper = FakeClipboardHelper(),
|
||||||
appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore()
|
appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore()
|
||||||
|
|
@ -111,7 +109,6 @@ class RoomDetailsPresenterTest {
|
||||||
leaveRoomPresenter = { leaveRoomState },
|
leaveRoomPresenter = { leaveRoomState },
|
||||||
roomCallStatePresenter = { aStandByCallState() },
|
roomCallStatePresenter = { aStandByCallState() },
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
isPinnedMessagesFeatureEnabled = { isPinnedMessagesFeatureEnabled },
|
|
||||||
analyticsService = analyticsService,
|
analyticsService = analyticsService,
|
||||||
clipboardHelper = clipboardHelper,
|
clipboardHelper = clipboardHelper,
|
||||||
appPreferencesStore = appPreferencesStore,
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
|
@ -133,7 +130,6 @@ class RoomDetailsPresenterTest {
|
||||||
assertThat(initialState.roomAvatarUrl).isEqualTo(room.info().avatarUrl)
|
assertThat(initialState.roomAvatarUrl).isEqualTo(room.info().avatarUrl)
|
||||||
assertThat(initialState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.info().topic!!))
|
assertThat(initialState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.info().topic!!))
|
||||||
assertThat(initialState.memberCount).isEqualTo(room.info().joinedMembersCount)
|
assertThat(initialState.memberCount).isEqualTo(room.info().joinedMembersCount)
|
||||||
assertThat(initialState.canShowPinnedMessages).isTrue()
|
|
||||||
assertThat(initialState.pinnedMessagesCount).isEqualTo(0)
|
assertThat(initialState.pinnedMessagesCount).isEqualTo(0)
|
||||||
assertThat(initialState.canShowSecurityAndPrivacy).isFalse()
|
assertThat(initialState.canShowSecurityAndPrivacy).isFalse()
|
||||||
assertThat(initialState.showDebugInfo).isFalse()
|
assertThat(initialState.showDebugInfo).isFalse()
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.share.api
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
|
|
||||||
interface ShareService {
|
|
||||||
fun observeFeatureFlag(coroutineScope: CoroutineScope)
|
|
||||||
}
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.share.impl
|
|
||||||
|
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
|
||||||
import io.element.android.features.share.api.ShareService
|
|
||||||
import io.element.android.libraries.di.AppScope
|
|
||||||
import io.element.android.libraries.di.ApplicationContext
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
|
||||||
class DefaultShareService @Inject constructor(
|
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
@ApplicationContext private val context: Context,
|
|
||||||
) : ShareService {
|
|
||||||
override fun observeFeatureFlag(coroutineScope: CoroutineScope) {
|
|
||||||
val shareActivityComponent = getShareActivityComponent()
|
|
||||||
?: return Unit.also {
|
|
||||||
Timber.w("ShareActivity not found")
|
|
||||||
}
|
|
||||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.IncomingShare)
|
|
||||||
.onEach { enabled ->
|
|
||||||
shareActivityComponent.enableOrDisable(enabled)
|
|
||||||
}
|
|
||||||
.launchIn(coroutineScope)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getShareActivityComponent(): ComponentName? {
|
|
||||||
return context.packageManager
|
|
||||||
.getPackageInfo(
|
|
||||||
context.packageName,
|
|
||||||
PackageManager.GET_ACTIVITIES or PackageManager.MATCH_DISABLED_COMPONENTS
|
|
||||||
)
|
|
||||||
.activities
|
|
||||||
?.firstOrNull { it.name.endsWith(".ShareActivity") }
|
|
||||||
?.let { shareActivityInfo ->
|
|
||||||
ComponentName(
|
|
||||||
shareActivityInfo.packageName,
|
|
||||||
shareActivityInfo.name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ComponentName.enableOrDisable(enabled: Boolean) {
|
|
||||||
val state = if (enabled) {
|
|
||||||
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|
|
||||||
} else {
|
|
||||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
context.packageManager.setComponentEnabledSetting(
|
|
||||||
this,
|
|
||||||
state,
|
|
||||||
PackageManager.DONT_KILL_APP,
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "Failed to enable or disable the component")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 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-library")
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "io.element.android.features.share.test"
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(projects.features.share.api)
|
|
||||||
implementation(libs.coroutines.core)
|
|
||||||
implementation(projects.tests.testutils)
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.share.test
|
|
||||||
|
|
||||||
import io.element.android.features.share.api.ShareService
|
|
||||||
import io.element.android.tests.testutils.lambda.lambdaError
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
|
|
||||||
class FakeShareService(
|
|
||||||
private val observeFeatureFlagLambda: (CoroutineScope) -> Unit = { lambdaError() }
|
|
||||||
) : ShareService {
|
|
||||||
override fun observeFeatureFlag(coroutineScope: CoroutineScope) {
|
|
||||||
observeFeatureFlagLambda(coroutineScope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
package io.element.android.libraries.featureflag.api
|
package io.element.android.libraries.featureflag.api
|
||||||
|
|
||||||
import io.element.android.appconfig.OnBoardingConfig
|
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.core.meta.BuildType
|
import io.element.android.libraries.core.meta.BuildType
|
||||||
|
|
||||||
|
|
@ -21,46 +20,6 @@ enum class FeatureFlags(
|
||||||
override val defaultValue: (BuildMeta) -> Boolean,
|
override val defaultValue: (BuildMeta) -> Boolean,
|
||||||
override val isFinished: Boolean,
|
override val isFinished: Boolean,
|
||||||
) : Feature {
|
) : Feature {
|
||||||
LocationSharing(
|
|
||||||
key = "feature.locationsharing",
|
|
||||||
title = "Allow user to share location",
|
|
||||||
defaultValue = { true },
|
|
||||||
isFinished = true,
|
|
||||||
),
|
|
||||||
Polls(
|
|
||||||
key = "feature.polls",
|
|
||||||
title = "Polls",
|
|
||||||
description = "Create poll and render poll events in the timeline",
|
|
||||||
defaultValue = { true },
|
|
||||||
isFinished = true,
|
|
||||||
),
|
|
||||||
NotificationSettings(
|
|
||||||
key = "feature.notificationsettings",
|
|
||||||
title = "Show notification settings",
|
|
||||||
defaultValue = { true },
|
|
||||||
isFinished = true,
|
|
||||||
),
|
|
||||||
VoiceMessages(
|
|
||||||
key = "feature.voicemessages",
|
|
||||||
title = "Voice messages",
|
|
||||||
description = "Send and receive voice messages",
|
|
||||||
defaultValue = { true },
|
|
||||||
isFinished = true,
|
|
||||||
),
|
|
||||||
PinUnlock(
|
|
||||||
key = "feature.pinunlock",
|
|
||||||
title = "Pin unlock",
|
|
||||||
description = "Allow user to lock/unlock the app with a pin code or biometrics",
|
|
||||||
defaultValue = { true },
|
|
||||||
isFinished = true,
|
|
||||||
),
|
|
||||||
MarkAsUnread(
|
|
||||||
key = "feature.markAsUnread",
|
|
||||||
title = "Mark as unread",
|
|
||||||
description = "Allow user to mark a room as unread",
|
|
||||||
defaultValue = { true },
|
|
||||||
isFinished = false,
|
|
||||||
),
|
|
||||||
RoomDirectorySearch(
|
RoomDirectorySearch(
|
||||||
key = "feature.roomdirectorysearch",
|
key = "feature.roomdirectorysearch",
|
||||||
title = "Room directory search",
|
title = "Room directory search",
|
||||||
|
|
@ -75,27 +34,6 @@ enum class FeatureFlags(
|
||||||
defaultValue = { false },
|
defaultValue = { false },
|
||||||
isFinished = false,
|
isFinished = false,
|
||||||
),
|
),
|
||||||
QrCodeLogin(
|
|
||||||
key = "feature.qrCodeLogin",
|
|
||||||
title = "Enable login using QR code",
|
|
||||||
description = "Allow the user to login using the QR code flow",
|
|
||||||
defaultValue = { OnBoardingConfig.CAN_LOGIN_WITH_QR_CODE },
|
|
||||||
isFinished = false,
|
|
||||||
),
|
|
||||||
IncomingShare(
|
|
||||||
key = "feature.incomingShare",
|
|
||||||
title = "Incoming Share support",
|
|
||||||
description = "Allow the application to receive data from other applications",
|
|
||||||
defaultValue = { true },
|
|
||||||
isFinished = false,
|
|
||||||
),
|
|
||||||
PinnedEvents(
|
|
||||||
key = "feature.pinnedEvents",
|
|
||||||
title = "Pinned Events",
|
|
||||||
description = "Allow user to pin events in a room",
|
|
||||||
defaultValue = { true },
|
|
||||||
isFinished = false,
|
|
||||||
),
|
|
||||||
SyncOnPush(
|
SyncOnPush(
|
||||||
key = "feature.syncOnPush",
|
key = "feature.syncOnPush",
|
||||||
title = "Sync on push",
|
title = "Sync on push",
|
||||||
|
|
@ -137,34 +75,6 @@ enum class FeatureFlags(
|
||||||
defaultValue = { false },
|
defaultValue = { false },
|
||||||
isFinished = false,
|
isFinished = false,
|
||||||
),
|
),
|
||||||
MediaUploadOnSendQueue(
|
|
||||||
key = "feature.media_upload_through_send_queue",
|
|
||||||
title = "Media upload through send queue",
|
|
||||||
description = "Support for treating media uploads as regular events, with an improved retry and cancellation implementation.",
|
|
||||||
defaultValue = { true },
|
|
||||||
isFinished = true,
|
|
||||||
),
|
|
||||||
MediaCaptionCreation(
|
|
||||||
key = "feature.media_caption_creation",
|
|
||||||
title = "Allow creation of media captions",
|
|
||||||
description = null,
|
|
||||||
defaultValue = { true },
|
|
||||||
isFinished = false,
|
|
||||||
),
|
|
||||||
MediaCaptionWarning(
|
|
||||||
key = "feature.media_caption_creation_warning",
|
|
||||||
title = "Show a compatibility warning on media captions creation",
|
|
||||||
description = null,
|
|
||||||
defaultValue = { true },
|
|
||||||
isFinished = false,
|
|
||||||
),
|
|
||||||
MediaGallery(
|
|
||||||
key = "feature.media_gallery",
|
|
||||||
title = "Allow user to open the media gallery",
|
|
||||||
description = null,
|
|
||||||
defaultValue = { true },
|
|
||||||
isFinished = false,
|
|
||||||
),
|
|
||||||
PrintLogsToLogcat(
|
PrintLogsToLogcat(
|
||||||
key = "feature.print_logs_to_logcat",
|
key = "feature.print_logs_to_logcat",
|
||||||
title = "Print logs to logcat",
|
title = "Print logs to logcat",
|
||||||
|
|
@ -175,15 +85,6 @@ enum class FeatureFlags(
|
||||||
// False so it's displayed in the developer options screen
|
// False so it's displayed in the developer options screen
|
||||||
isFinished = false,
|
isFinished = false,
|
||||||
),
|
),
|
||||||
SharePos(
|
|
||||||
key = "feature.share_pos_v2",
|
|
||||||
title = "Share pos in sliding sync",
|
|
||||||
description = "Keep the sliding sync pos to make initial syncs faster. Requires an app restart to take effect." +
|
|
||||||
"\n\nWARNING: this may cause issues with syncs.",
|
|
||||||
defaultValue = { true },
|
|
||||||
// False so it's displayed in the developer options screen
|
|
||||||
isFinished = false,
|
|
||||||
),
|
|
||||||
SelectableMediaQuality(
|
SelectableMediaQuality(
|
||||||
key = "feature.selectable_media_quality",
|
key = "feature.selectable_media_quality",
|
||||||
title = "Select media quality per upload",
|
title = "Select media quality per upload",
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ class DefaultFeatureFlagServiceTest {
|
||||||
fun `given service without provider when feature is checked then it returns the default value`() = runTest {
|
fun `given service without provider when feature is checked then it returns the default value`() = runTest {
|
||||||
val buildMeta = aBuildMeta()
|
val buildMeta = aBuildMeta()
|
||||||
val featureFlagService = DefaultFeatureFlagService(emptySet(), buildMeta)
|
val featureFlagService = DefaultFeatureFlagService(emptySet(), buildMeta)
|
||||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.LocationSharing).test {
|
featureFlagService.isFeatureEnabledFlow(FeatureFlags.Space).test {
|
||||||
assertThat(awaitItem()).isEqualTo(FeatureFlags.LocationSharing.defaultValue(buildMeta))
|
assertThat(awaitItem()).isEqualTo(FeatureFlags.Space.defaultValue(buildMeta))
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -28,7 +28,7 @@ class DefaultFeatureFlagServiceTest {
|
||||||
@Test
|
@Test
|
||||||
fun `given service without provider when set enabled feature is called then it returns false`() = runTest {
|
fun `given service without provider when set enabled feature is called then it returns false`() = runTest {
|
||||||
val featureFlagService = DefaultFeatureFlagService(emptySet(), aBuildMeta())
|
val featureFlagService = DefaultFeatureFlagService(emptySet(), aBuildMeta())
|
||||||
val result = featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true)
|
val result = featureFlagService.setFeatureEnabled(FeatureFlags.Space, true)
|
||||||
assertThat(result).isFalse()
|
assertThat(result).isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ class DefaultFeatureFlagServiceTest {
|
||||||
val buildMeta = aBuildMeta()
|
val buildMeta = aBuildMeta()
|
||||||
val featureFlagProvider = FakeMutableFeatureFlagProvider(0, buildMeta)
|
val featureFlagProvider = FakeMutableFeatureFlagProvider(0, buildMeta)
|
||||||
val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider), buildMeta)
|
val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider), buildMeta)
|
||||||
val result = featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true)
|
val result = featureFlagService.setFeatureEnabled(FeatureFlags.Space, true)
|
||||||
assertThat(result).isTrue()
|
assertThat(result).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,10 +46,10 @@ class DefaultFeatureFlagServiceTest {
|
||||||
val buildMeta = aBuildMeta()
|
val buildMeta = aBuildMeta()
|
||||||
val featureFlagProvider = FakeMutableFeatureFlagProvider(0, buildMeta)
|
val featureFlagProvider = FakeMutableFeatureFlagProvider(0, buildMeta)
|
||||||
val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider), buildMeta)
|
val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider), buildMeta)
|
||||||
featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true)
|
featureFlagService.setFeatureEnabled(FeatureFlags.Space, true)
|
||||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.LocationSharing).test {
|
featureFlagService.isFeatureEnabledFlow(FeatureFlags.Space).test {
|
||||||
assertThat(awaitItem()).isTrue()
|
assertThat(awaitItem()).isTrue()
|
||||||
featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, false)
|
featureFlagService.setFeatureEnabled(FeatureFlags.Space, false)
|
||||||
assertThat(awaitItem()).isFalse()
|
assertThat(awaitItem()).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60,9 +60,9 @@ class DefaultFeatureFlagServiceTest {
|
||||||
val lowPriorityFeatureFlagProvider = FakeMutableFeatureFlagProvider(LOW_PRIORITY, buildMeta)
|
val lowPriorityFeatureFlagProvider = FakeMutableFeatureFlagProvider(LOW_PRIORITY, buildMeta)
|
||||||
val highPriorityFeatureFlagProvider = FakeMutableFeatureFlagProvider(HIGH_PRIORITY, buildMeta)
|
val highPriorityFeatureFlagProvider = FakeMutableFeatureFlagProvider(HIGH_PRIORITY, buildMeta)
|
||||||
val featureFlagService = DefaultFeatureFlagService(setOf(lowPriorityFeatureFlagProvider, highPriorityFeatureFlagProvider), buildMeta)
|
val featureFlagService = DefaultFeatureFlagService(setOf(lowPriorityFeatureFlagProvider, highPriorityFeatureFlagProvider), buildMeta)
|
||||||
lowPriorityFeatureFlagProvider.setFeatureEnabled(FeatureFlags.LocationSharing, false)
|
lowPriorityFeatureFlagProvider.setFeatureEnabled(FeatureFlags.Space, false)
|
||||||
highPriorityFeatureFlagProvider.setFeatureEnabled(FeatureFlags.LocationSharing, true)
|
highPriorityFeatureFlagProvider.setFeatureEnabled(FeatureFlags.Space, true)
|
||||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.LocationSharing).test {
|
featureFlagService.isFeatureEnabledFlow(FeatureFlags.Space).test {
|
||||||
assertThat(awaitItem()).isTrue()
|
assertThat(awaitItem()).isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import io.element.android.libraries.core.coroutine.childScope
|
||||||
import io.element.android.libraries.core.data.tryOrNull
|
import io.element.android.libraries.core.data.tryOrNull
|
||||||
import io.element.android.libraries.core.extensions.mapFailure
|
import io.element.android.libraries.core.extensions.mapFailure
|
||||||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||||
|
|
@ -136,7 +135,6 @@ class RustMatrixClient(
|
||||||
baseCacheDirectory: File,
|
baseCacheDirectory: File,
|
||||||
clock: SystemClock,
|
clock: SystemClock,
|
||||||
timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
|
timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
|
||||||
featureFlagService: FeatureFlagService,
|
|
||||||
) : MatrixClient {
|
) : MatrixClient {
|
||||||
override val sessionId: UserId = UserId(innerClient.userId())
|
override val sessionId: UserId = UserId(innerClient.userId())
|
||||||
override val deviceId: DeviceId = DeviceId(innerClient.deviceId())
|
override val deviceId: DeviceId = DeviceId(innerClient.deviceId())
|
||||||
|
|
@ -205,7 +203,6 @@ class RustMatrixClient(
|
||||||
roomContentForwarder = RoomContentForwarder(innerRoomListService),
|
roomContentForwarder = RoomContentForwarder(innerRoomListService),
|
||||||
roomSyncSubscriber = roomSyncSubscriber,
|
roomSyncSubscriber = roomSyncSubscriber,
|
||||||
timelineEventTypeFilterFactory = timelineEventTypeFilterFactory,
|
timelineEventTypeFilterFactory = timelineEventTypeFilterFactory,
|
||||||
featureFlagService = featureFlagService,
|
|
||||||
roomMembershipObserver = roomMembershipObserver,
|
roomMembershipObserver = roomMembershipObserver,
|
||||||
roomInfoMapper = roomInfoMapper,
|
roomInfoMapper = roomInfoMapper,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ class RustMatrixClientFactory @Inject constructor(
|
||||||
client.setUtdDelegate(UtdTracker(analyticsService))
|
client.setUtdDelegate(UtdTracker(analyticsService))
|
||||||
|
|
||||||
val syncService = client.syncService()
|
val syncService = client.syncService()
|
||||||
.withSharePos(enable = featureFlagService.isFeatureEnabled(FeatureFlags.SharePos))
|
.withSharePos(true)
|
||||||
.withOfflineMode()
|
.withOfflineMode()
|
||||||
.finish()
|
.finish()
|
||||||
|
|
||||||
|
|
@ -93,7 +93,6 @@ class RustMatrixClientFactory @Inject constructor(
|
||||||
baseCacheDirectory = cacheDirectory,
|
baseCacheDirectory = cacheDirectory,
|
||||||
clock = clock,
|
clock = clock,
|
||||||
timelineEventTypeFilterFactory = timelineEventTypeFilterFactory,
|
timelineEventTypeFilterFactory = timelineEventTypeFilterFactory,
|
||||||
featureFlagService = featureFlagService,
|
|
||||||
).also {
|
).also {
|
||||||
Timber.tag(it.toString()).d("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'")
|
Timber.tag(it.toString()).d("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.core.coroutine.childScope
|
import io.element.android.libraries.core.coroutine.childScope
|
||||||
import io.element.android.libraries.core.extensions.mapFailure
|
import io.element.android.libraries.core.extensions.mapFailure
|
||||||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||||
|
|
@ -84,7 +83,6 @@ class JoinedRustRoom(
|
||||||
private val coroutineDispatchers: CoroutineDispatchers,
|
private val coroutineDispatchers: CoroutineDispatchers,
|
||||||
private val systemClock: SystemClock,
|
private val systemClock: SystemClock,
|
||||||
private val roomContentForwarder: RoomContentForwarder,
|
private val roomContentForwarder: RoomContentForwarder,
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
) : JoinedRoom, BaseRoom by baseRoom {
|
) : JoinedRoom, BaseRoom by baseRoom {
|
||||||
// Create a dispatcher for all room methods...
|
// Create a dispatcher for all room methods...
|
||||||
private val roomDispatcher = coroutineDispatchers.io.limitedParallelism(32)
|
private val roomDispatcher = coroutineDispatchers.io.limitedParallelism(32)
|
||||||
|
|
@ -478,7 +476,6 @@ class JoinedRustRoom(
|
||||||
dispatcher = roomDispatcher,
|
dispatcher = roomDispatcher,
|
||||||
roomContentForwarder = roomContentForwarder,
|
roomContentForwarder = roomContentForwarder,
|
||||||
onNewSyncedEvent = onNewSyncedEvent,
|
onNewSyncedEvent = onNewSyncedEvent,
|
||||||
featureFlagsService = featureFlagService,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ package io.element.android.libraries.matrix.impl.room
|
||||||
|
|
||||||
import io.element.android.appconfig.TimelineConfig
|
import io.element.android.appconfig.TimelineConfig
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
|
@ -49,7 +48,6 @@ class RustRoomFactory(
|
||||||
private val innerRoomListService: InnerRoomListService,
|
private val innerRoomListService: InnerRoomListService,
|
||||||
private val roomSyncSubscriber: RoomSyncSubscriber,
|
private val roomSyncSubscriber: RoomSyncSubscriber,
|
||||||
private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
|
private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
|
||||||
private val featureFlagService: FeatureFlagService,
|
|
||||||
private val roomMembershipObserver: RoomMembershipObserver,
|
private val roomMembershipObserver: RoomMembershipObserver,
|
||||||
private val roomInfoMapper: RoomInfoMapper,
|
private val roomInfoMapper: RoomInfoMapper,
|
||||||
) {
|
) {
|
||||||
|
|
@ -127,7 +125,6 @@ class RustRoomFactory(
|
||||||
liveInnerTimeline = timeline,
|
liveInnerTimeline = timeline,
|
||||||
coroutineDispatchers = dispatchers,
|
coroutineDispatchers = dispatchers,
|
||||||
systemClock = systemClock,
|
systemClock = systemClock,
|
||||||
featureFlagService = featureFlagService,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@
|
||||||
package io.element.android.libraries.matrix.impl.timeline
|
package io.element.android.libraries.matrix.impl.timeline
|
||||||
|
|
||||||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -89,7 +87,6 @@ class RustTimeline(
|
||||||
private val coroutineScope: CoroutineScope,
|
private val coroutineScope: CoroutineScope,
|
||||||
private val dispatcher: CoroutineDispatcher,
|
private val dispatcher: CoroutineDispatcher,
|
||||||
private val roomContentForwarder: RoomContentForwarder,
|
private val roomContentForwarder: RoomContentForwarder,
|
||||||
private val featureFlagsService: FeatureFlagService,
|
|
||||||
onNewSyncedEvent: () -> Unit,
|
onNewSyncedEvent: () -> Unit,
|
||||||
) : Timeline {
|
) : Timeline {
|
||||||
private val _timelineItems: MutableSharedFlow<List<MatrixTimelineItem>> =
|
private val _timelineItems: MutableSharedFlow<List<MatrixTimelineItem>> =
|
||||||
|
|
@ -342,7 +339,6 @@ class RustTimeline(
|
||||||
progressCallback: ProgressCallback?,
|
progressCallback: ProgressCallback?,
|
||||||
inReplyToEventId: EventId?,
|
inReplyToEventId: EventId?,
|
||||||
): Result<MediaUploadHandler> {
|
): Result<MediaUploadHandler> {
|
||||||
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
|
||||||
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
|
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
|
||||||
inner.sendImage(
|
inner.sendImage(
|
||||||
params = UploadParameters(
|
params = UploadParameters(
|
||||||
|
|
@ -351,7 +347,7 @@ class RustTimeline(
|
||||||
formattedCaption = formattedCaption?.let {
|
formattedCaption = formattedCaption?.let {
|
||||||
FormattedBody(body = it, format = MessageFormat.Html)
|
FormattedBody(body = it, format = MessageFormat.Html)
|
||||||
},
|
},
|
||||||
useSendQueue = useSendQueue,
|
useSendQueue = true,
|
||||||
mentions = null,
|
mentions = null,
|
||||||
inReplyTo = inReplyToEventId?.value,
|
inReplyTo = inReplyToEventId?.value,
|
||||||
),
|
),
|
||||||
|
|
@ -371,7 +367,6 @@ class RustTimeline(
|
||||||
progressCallback: ProgressCallback?,
|
progressCallback: ProgressCallback?,
|
||||||
inReplyToEventId: EventId?,
|
inReplyToEventId: EventId?,
|
||||||
): Result<MediaUploadHandler> {
|
): Result<MediaUploadHandler> {
|
||||||
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
|
||||||
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
|
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
|
||||||
inner.sendVideo(
|
inner.sendVideo(
|
||||||
params = UploadParameters(
|
params = UploadParameters(
|
||||||
|
|
@ -380,7 +375,7 @@ class RustTimeline(
|
||||||
formattedCaption = formattedCaption?.let {
|
formattedCaption = formattedCaption?.let {
|
||||||
FormattedBody(body = it, format = MessageFormat.Html)
|
FormattedBody(body = it, format = MessageFormat.Html)
|
||||||
},
|
},
|
||||||
useSendQueue = useSendQueue,
|
useSendQueue = true,
|
||||||
mentions = null,
|
mentions = null,
|
||||||
inReplyTo = inReplyToEventId?.value,
|
inReplyTo = inReplyToEventId?.value,
|
||||||
),
|
),
|
||||||
|
|
@ -399,7 +394,6 @@ class RustTimeline(
|
||||||
progressCallback: ProgressCallback?,
|
progressCallback: ProgressCallback?,
|
||||||
inReplyToEventId: EventId?,
|
inReplyToEventId: EventId?,
|
||||||
): Result<MediaUploadHandler> {
|
): Result<MediaUploadHandler> {
|
||||||
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
|
||||||
return sendAttachment(listOf(file)) {
|
return sendAttachment(listOf(file)) {
|
||||||
inner.sendAudio(
|
inner.sendAudio(
|
||||||
params = UploadParameters(
|
params = UploadParameters(
|
||||||
|
|
@ -408,7 +402,7 @@ class RustTimeline(
|
||||||
formattedCaption = formattedCaption?.let {
|
formattedCaption = formattedCaption?.let {
|
||||||
FormattedBody(body = it, format = MessageFormat.Html)
|
FormattedBody(body = it, format = MessageFormat.Html)
|
||||||
},
|
},
|
||||||
useSendQueue = useSendQueue,
|
useSendQueue = true,
|
||||||
mentions = null,
|
mentions = null,
|
||||||
inReplyTo = inReplyToEventId?.value,
|
inReplyTo = inReplyToEventId?.value,
|
||||||
),
|
),
|
||||||
|
|
@ -426,7 +420,6 @@ class RustTimeline(
|
||||||
progressCallback: ProgressCallback?,
|
progressCallback: ProgressCallback?,
|
||||||
inReplyToEventId: EventId?,
|
inReplyToEventId: EventId?,
|
||||||
): Result<MediaUploadHandler> {
|
): Result<MediaUploadHandler> {
|
||||||
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
|
||||||
return sendAttachment(listOf(file)) {
|
return sendAttachment(listOf(file)) {
|
||||||
inner.sendFile(
|
inner.sendFile(
|
||||||
params = UploadParameters(
|
params = UploadParameters(
|
||||||
|
|
@ -435,7 +428,7 @@ class RustTimeline(
|
||||||
formattedCaption = formattedCaption?.let {
|
formattedCaption = formattedCaption?.let {
|
||||||
FormattedBody(body = it, format = MessageFormat.Html)
|
FormattedBody(body = it, format = MessageFormat.Html)
|
||||||
},
|
},
|
||||||
useSendQueue = useSendQueue,
|
useSendQueue = true,
|
||||||
mentions = null,
|
mentions = null,
|
||||||
inReplyTo = inReplyToEventId?.value,
|
inReplyTo = inReplyToEventId?.value,
|
||||||
),
|
),
|
||||||
|
|
@ -489,7 +482,6 @@ class RustTimeline(
|
||||||
progressCallback: ProgressCallback?,
|
progressCallback: ProgressCallback?,
|
||||||
inReplyToEventId: EventId?,
|
inReplyToEventId: EventId?,
|
||||||
): Result<MediaUploadHandler> {
|
): Result<MediaUploadHandler> {
|
||||||
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
|
||||||
return sendAttachment(listOf(file)) {
|
return sendAttachment(listOf(file)) {
|
||||||
inner.sendVoiceMessage(
|
inner.sendVoiceMessage(
|
||||||
params = UploadParameters(
|
params = UploadParameters(
|
||||||
|
|
@ -497,7 +489,7 @@ class RustTimeline(
|
||||||
// Maybe allow a caption in the future?
|
// Maybe allow a caption in the future?
|
||||||
caption = null,
|
caption = null,
|
||||||
formattedCaption = null,
|
formattedCaption = null,
|
||||||
useSendQueue = useSendQueue,
|
useSendQueue = true,
|
||||||
mentions = null,
|
mentions = null,
|
||||||
inReplyTo = inReplyToEventId?.value,
|
inReplyTo = inReplyToEventId?.value,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
package io.element.android.libraries.matrix.impl
|
package io.element.android.libraries.matrix.impl
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClient
|
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClient
|
||||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiSyncService
|
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiSyncService
|
||||||
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventTypeFilterFactory
|
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventTypeFilterFactory
|
||||||
|
|
@ -67,6 +66,5 @@ class RustMatrixClientTest {
|
||||||
baseCacheDirectory = File(""),
|
baseCacheDirectory = File(""),
|
||||||
clock = FakeSystemClock(),
|
clock = FakeSystemClock(),
|
||||||
timelineEventTypeFilterFactory = FakeTimelineEventTypeFilterFactory(),
|
timelineEventTypeFilterFactory = FakeTimelineEventTypeFilterFactory(),
|
||||||
featureFlagService = FakeFeatureFlagService(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ package io.element.android.libraries.matrix.impl.timeline
|
||||||
|
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
|
|
@ -98,7 +96,6 @@ private fun TestScope.createRustTimeline(
|
||||||
coroutineScope: CoroutineScope = backgroundScope,
|
coroutineScope: CoroutineScope = backgroundScope,
|
||||||
dispatcher: CoroutineDispatcher = testCoroutineDispatchers().io,
|
dispatcher: CoroutineDispatcher = testCoroutineDispatchers().io,
|
||||||
roomContentForwarder: RoomContentForwarder = RoomContentForwarder(FakeFfiRoomListService()),
|
roomContentForwarder: RoomContentForwarder = RoomContentForwarder(FakeFfiRoomListService()),
|
||||||
featureFlagsService: FeatureFlagService = FakeFeatureFlagService(),
|
|
||||||
onNewSyncedEvent: () -> Unit = {},
|
onNewSyncedEvent: () -> Unit = {},
|
||||||
): RustTimeline {
|
): RustTimeline {
|
||||||
return RustTimeline(
|
return RustTimeline(
|
||||||
|
|
@ -109,7 +106,6 @@ private fun TestScope.createRustTimeline(
|
||||||
coroutineScope = coroutineScope,
|
coroutineScope = coroutineScope,
|
||||||
dispatcher = dispatcher,
|
dispatcher = dispatcher,
|
||||||
roomContentForwarder = roomContentForwarder,
|
roomContentForwarder = roomContentForwarder,
|
||||||
featureFlagsService = featureFlagsService,
|
|
||||||
onNewSyncedEvent = onNewSyncedEvent,
|
onNewSyncedEvent = onNewSyncedEvent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,6 @@ import io.element.android.wysiwyg.compose.RichTextEditor
|
||||||
import io.element.android.wysiwyg.display.TextDisplay
|
import io.element.android.wysiwyg.display.TextDisplay
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import uniffi.wysiwyg_composer.MenuAction
|
import uniffi.wysiwyg_composer.MenuAction
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
@ -101,7 +100,6 @@ fun TextComposer(
|
||||||
state: TextEditorState,
|
state: TextEditorState,
|
||||||
voiceMessageState: VoiceMessageState,
|
voiceMessageState: VoiceMessageState,
|
||||||
composerMode: MessageComposerMode,
|
composerMode: MessageComposerMode,
|
||||||
enableVoiceMessages: Boolean,
|
|
||||||
onRequestFocus: () -> Unit,
|
onRequestFocus: () -> Unit,
|
||||||
onSendMessage: () -> Unit,
|
onSendMessage: () -> Unit,
|
||||||
onResetComposerMode: () -> Unit,
|
onResetComposerMode: () -> Unit,
|
||||||
|
|
@ -141,8 +139,8 @@ fun TextComposer(
|
||||||
}
|
}
|
||||||
|
|
||||||
val layoutModifier = modifier
|
val layoutModifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.height(IntrinsicSize.Min)
|
.height(IntrinsicSize.Min)
|
||||||
|
|
||||||
val composerOptionsButton: @Composable () -> Unit = remember(composerMode) {
|
val composerOptionsButton: @Composable () -> Unit = remember(composerMode) {
|
||||||
@Composable {
|
@Composable {
|
||||||
|
|
@ -171,22 +169,17 @@ fun TextComposer(
|
||||||
} else {
|
} else {
|
||||||
stringResource(id = R.string.rich_text_editor_composer_placeholder)
|
stringResource(id = R.string.rich_text_editor_composer_placeholder)
|
||||||
}
|
}
|
||||||
val textInput: @Composable () -> Unit = if ((composerMode as? MessageComposerMode.Attachment)?.allowCaption == false) {
|
val textInput: @Composable () -> Unit = when (state) {
|
||||||
{
|
is TextEditorState.Rich -> {
|
||||||
// No text input when in attachment mode and caption not allowed.
|
val coroutineScope = rememberCoroutineScope()
|
||||||
}
|
val view = LocalView.current
|
||||||
} else {
|
remember(state.richTextEditorState, composerMode, onResetComposerMode, onError) {
|
||||||
when (state) {
|
@Composable {
|
||||||
is TextEditorState.Rich -> {
|
TextInputBox(
|
||||||
val coroutineScope = rememberCoroutineScope()
|
modifier = Modifier
|
||||||
val view = LocalView.current
|
|
||||||
remember(state.richTextEditorState, composerMode, onResetComposerMode, onError) {
|
|
||||||
@Composable {
|
|
||||||
TextInputBox(
|
|
||||||
modifier = Modifier
|
|
||||||
.clickable(
|
.clickable(
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
indication = null,
|
indication = null,
|
||||||
) {
|
) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
state.requestFocus()
|
state.requestFocus()
|
||||||
|
|
@ -196,46 +189,45 @@ fun TextComposer(
|
||||||
.semantics {
|
.semantics {
|
||||||
hideFromAccessibility()
|
hideFromAccessibility()
|
||||||
},
|
},
|
||||||
composerMode = composerMode,
|
composerMode = composerMode,
|
||||||
onResetComposerMode = onResetComposerMode,
|
onResetComposerMode = onResetComposerMode,
|
||||||
isTextEmpty = state.richTextEditorState.messageHtml.isEmpty(),
|
isTextEmpty = state.richTextEditorState.messageHtml.isEmpty(),
|
||||||
) {
|
) {
|
||||||
RichTextEditor(
|
RichTextEditor(
|
||||||
state = state.richTextEditorState,
|
state = state.richTextEditorState,
|
||||||
placeholder = placeholder,
|
placeholder = placeholder,
|
||||||
registerStateUpdates = true,
|
registerStateUpdates = true,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = 6.dp, bottom = 6.dp)
|
.padding(top = 6.dp, bottom = 6.dp)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.richTextEditorState.hasFocus),
|
style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.richTextEditorState.hasFocus),
|
||||||
resolveMentionDisplay = resolveMentionDisplay,
|
resolveMentionDisplay = resolveMentionDisplay,
|
||||||
resolveRoomMentionDisplay = resolveAtRoomMentionDisplay,
|
resolveRoomMentionDisplay = resolveAtRoomMentionDisplay,
|
||||||
onError = onError,
|
onError = onError,
|
||||||
onRichContentSelected = onSelectRichContent,
|
onRichContentSelected = onSelectRichContent,
|
||||||
onTyping = onTyping,
|
onTyping = onTyping,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is TextEditorState.Markdown -> {
|
}
|
||||||
@Composable {
|
is TextEditorState.Markdown -> {
|
||||||
val style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.hasFocus())
|
@Composable {
|
||||||
TextInputBox(
|
val style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.hasFocus())
|
||||||
composerMode = composerMode,
|
TextInputBox(
|
||||||
onResetComposerMode = onResetComposerMode,
|
composerMode = composerMode,
|
||||||
isTextEmpty = state.state.text.value().isEmpty(),
|
onResetComposerMode = onResetComposerMode,
|
||||||
) {
|
isTextEmpty = state.state.text.value().isEmpty(),
|
||||||
MarkdownTextInput(
|
) {
|
||||||
state = state.state,
|
MarkdownTextInput(
|
||||||
placeholder = placeholder,
|
state = state.state,
|
||||||
placeholderColor = ElementTheme.colors.textSecondary,
|
placeholder = placeholder,
|
||||||
onTyping = onTyping,
|
placeholderColor = ElementTheme.colors.textSecondary,
|
||||||
onReceiveSuggestion = onReceiveSuggestion,
|
onTyping = onTyping,
|
||||||
richTextEditorStyle = style,
|
onReceiveSuggestion = onReceiveSuggestion,
|
||||||
onSelectRichContent = onSelectRichContent,
|
richTextEditorStyle = style,
|
||||||
)
|
onSelectRichContent = onSelectRichContent,
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -273,7 +265,7 @@ fun TextComposer(
|
||||||
}
|
}
|
||||||
|
|
||||||
val sendOrRecordButton = when {
|
val sendOrRecordButton = when {
|
||||||
enableVoiceMessages && !canSendMessage ->
|
!canSendMessage ->
|
||||||
when (voiceMessageState) {
|
when (voiceMessageState) {
|
||||||
VoiceMessageState.Idle,
|
VoiceMessageState.Idle,
|
||||||
is VoiceMessageState.Recording -> recordVoiceButton
|
is VoiceMessageState.Recording -> recordVoiceButton
|
||||||
|
|
@ -288,7 +280,6 @@ fun TextComposer(
|
||||||
val endButtonA11y = endButtonA11y(
|
val endButtonA11y = endButtonA11y(
|
||||||
composerMode = composerMode,
|
composerMode = composerMode,
|
||||||
voiceMessageState = voiceMessageState,
|
voiceMessageState = voiceMessageState,
|
||||||
enableVoiceMessages = enableVoiceMessages,
|
|
||||||
canSendMessage = canSendMessage,
|
canSendMessage = canSendMessage,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -341,7 +332,6 @@ fun TextComposer(
|
||||||
} else {
|
} else {
|
||||||
StandardLayout(
|
StandardLayout(
|
||||||
voiceMessageState = voiceMessageState,
|
voiceMessageState = voiceMessageState,
|
||||||
enableVoiceMessages = enableVoiceMessages,
|
|
||||||
isRoomEncrypted = state.isRoomEncrypted,
|
isRoomEncrypted = state.isRoomEncrypted,
|
||||||
modifier = layoutModifier,
|
modifier = layoutModifier,
|
||||||
composerOptionsButton = composerOptionsButton,
|
composerOptionsButton = composerOptionsButton,
|
||||||
|
|
@ -378,12 +368,11 @@ fun TextComposer(
|
||||||
private fun endButtonA11y(
|
private fun endButtonA11y(
|
||||||
composerMode: MessageComposerMode,
|
composerMode: MessageComposerMode,
|
||||||
voiceMessageState: VoiceMessageState,
|
voiceMessageState: VoiceMessageState,
|
||||||
enableVoiceMessages: Boolean,
|
|
||||||
canSendMessage: Boolean,
|
canSendMessage: Boolean,
|
||||||
): (SemanticsPropertyReceiver) -> Unit {
|
): (SemanticsPropertyReceiver) -> Unit {
|
||||||
val a11ySendButtonDescription = stringResource(
|
val a11ySendButtonDescription = stringResource(
|
||||||
id = when {
|
id = when {
|
||||||
enableVoiceMessages && !canSendMessage ->
|
!canSendMessage ->
|
||||||
when (voiceMessageState) {
|
when (voiceMessageState) {
|
||||||
VoiceMessageState.Idle,
|
VoiceMessageState.Idle,
|
||||||
is VoiceMessageState.Recording -> if (voiceMessageState is VoiceMessageState.Recording) {
|
is VoiceMessageState.Recording -> if (voiceMessageState is VoiceMessageState.Recording) {
|
||||||
|
|
@ -410,7 +399,6 @@ private fun endButtonA11y(
|
||||||
@Composable
|
@Composable
|
||||||
private fun StandardLayout(
|
private fun StandardLayout(
|
||||||
voiceMessageState: VoiceMessageState,
|
voiceMessageState: VoiceMessageState,
|
||||||
enableVoiceMessages: Boolean,
|
|
||||||
isRoomEncrypted: Boolean?,
|
isRoomEncrypted: Boolean?,
|
||||||
textInput: @Composable () -> Unit,
|
textInput: @Composable () -> Unit,
|
||||||
composerOptionsButton: @Composable () -> Unit,
|
composerOptionsButton: @Composable () -> Unit,
|
||||||
|
|
@ -427,12 +415,12 @@ private fun StandardLayout(
|
||||||
Spacer(Modifier.height(4.dp))
|
Spacer(Modifier.height(4.dp))
|
||||||
}
|
}
|
||||||
Row(verticalAlignment = Alignment.Bottom) {
|
Row(verticalAlignment = Alignment.Bottom) {
|
||||||
if (enableVoiceMessages && voiceMessageState !is VoiceMessageState.Idle) {
|
if (voiceMessageState !is VoiceMessageState.Idle) {
|
||||||
if (voiceMessageState is VoiceMessageState.Preview || voiceMessageState is VoiceMessageState.Recording) {
|
if (voiceMessageState is VoiceMessageState.Preview || voiceMessageState is VoiceMessageState.Recording) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(bottom = 5.dp, top = 5.dp, end = 3.dp, start = 3.dp)
|
.padding(bottom = 5.dp, top = 5.dp, end = 3.dp, start = 3.dp)
|
||||||
.size(48.dp),
|
.size(48.dp),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
voiceDeleteButton()
|
voiceDeleteButton()
|
||||||
|
|
@ -442,8 +430,8 @@ private fun StandardLayout(
|
||||||
}
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(bottom = 8.dp, top = 8.dp)
|
.padding(bottom = 8.dp, top = 8.dp)
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
) {
|
) {
|
||||||
voiceRecording()
|
voiceRecording()
|
||||||
}
|
}
|
||||||
|
|
@ -456,17 +444,17 @@ private fun StandardLayout(
|
||||||
}
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(bottom = 8.dp, top = 8.dp)
|
.padding(bottom = 8.dp, top = 8.dp)
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
) {
|
) {
|
||||||
textInput()
|
textInput()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.padding(bottom = 5.dp, top = 5.dp, end = 6.dp, start = 6.dp)
|
.padding(bottom = 5.dp, top = 5.dp, end = 6.dp, start = 6.dp)
|
||||||
.size(48.dp)
|
.size(48.dp)
|
||||||
.clearAndSetSemantics(endButtonA11y),
|
.clearAndSetSemantics(endButtonA11y),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
endButton()
|
endButton()
|
||||||
|
|
@ -517,8 +505,8 @@ private fun TextFormattingLayout(
|
||||||
}
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.padding(horizontal = 12.dp)
|
.padding(horizontal = 12.dp)
|
||||||
) {
|
) {
|
||||||
textInput()
|
textInput()
|
||||||
}
|
}
|
||||||
|
|
@ -537,11 +525,11 @@ private fun TextFormattingLayout(
|
||||||
}
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(
|
.padding(
|
||||||
start = 14.dp,
|
start = 14.dp,
|
||||||
end = 6.dp,
|
end = 6.dp,
|
||||||
)
|
)
|
||||||
.clearAndSetSemantics(endButtonA11y)
|
.clearAndSetSemantics(endButtonA11y)
|
||||||
) {
|
) {
|
||||||
sendButton()
|
sendButton()
|
||||||
}
|
}
|
||||||
|
|
@ -563,12 +551,12 @@ private fun TextInputBox(
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clip(roundedCorners)
|
.clip(roundedCorners)
|
||||||
.border(0.5.dp, borderColor, roundedCorners)
|
.border(0.5.dp, borderColor, roundedCorners)
|
||||||
.background(color = bgColor)
|
.background(color = bgColor)
|
||||||
.requiredHeightIn(min = 42.dp)
|
.requiredHeightIn(min = 42.dp)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.then(modifier),
|
.then(modifier),
|
||||||
) {
|
) {
|
||||||
if (composerMode is MessageComposerMode.Special) {
|
if (composerMode is MessageComposerMode.Special) {
|
||||||
ComposerModeView(
|
ComposerModeView(
|
||||||
|
|
@ -578,8 +566,8 @@ private fun TextInputBox(
|
||||||
}
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 12.dp)
|
.padding(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 12.dp)
|
||||||
.then(Modifier.testTag(TestTags.textEditor)),
|
.then(Modifier.testTag(TestTags.textEditor)),
|
||||||
contentAlignment = Alignment.CenterStart,
|
contentAlignment = Alignment.CenterStart,
|
||||||
) {
|
) {
|
||||||
textInput()
|
textInput()
|
||||||
|
|
@ -587,9 +575,9 @@ private fun TextInputBox(
|
||||||
var showBottomSheet by remember { mutableStateOf(false) }
|
var showBottomSheet by remember { mutableStateOf(false) }
|
||||||
Icon(
|
Icon(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable { showBottomSheet = true }
|
.clickable { showBottomSheet = true }
|
||||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||||
.align(Alignment.CenterEnd),
|
.align(Alignment.CenterEnd),
|
||||||
imageVector = CompoundIcons.InfoSolid(),
|
imageVector = CompoundIcons.InfoSolid(),
|
||||||
tint = ElementTheme.colors.iconCriticalPrimary,
|
tint = ElementTheme.colors.iconCriticalPrimary,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
|
|
@ -631,12 +619,11 @@ private fun aTextEditorStateRichList(isRoomEncrypted: Boolean? = null) = persist
|
||||||
internal fun TextComposerSimplePreview() = ElementPreview {
|
internal fun TextComposerSimplePreview() = ElementPreview {
|
||||||
PreviewColumn(
|
PreviewColumn(
|
||||||
items = aTextEditorStateMarkdownList()
|
items = aTextEditorStateMarkdownList()
|
||||||
) { _, textEditorState ->
|
) { textEditorState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = textEditorState,
|
state = textEditorState,
|
||||||
voiceMessageState = VoiceMessageState.Idle,
|
voiceMessageState = VoiceMessageState.Idle,
|
||||||
composerMode = MessageComposerMode.Normal,
|
composerMode = MessageComposerMode.Normal,
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -646,12 +633,11 @@ internal fun TextComposerSimplePreview() = ElementPreview {
|
||||||
internal fun TextComposerSimpleNotEncryptedPreview() = ElementPreview {
|
internal fun TextComposerSimpleNotEncryptedPreview() = ElementPreview {
|
||||||
PreviewColumn(
|
PreviewColumn(
|
||||||
items = aTextEditorStateMarkdownList(isRoomEncrypted = false),
|
items = aTextEditorStateMarkdownList(isRoomEncrypted = false),
|
||||||
) { _, textEditorState ->
|
) { textEditorState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = textEditorState,
|
state = textEditorState,
|
||||||
voiceMessageState = VoiceMessageState.Idle,
|
voiceMessageState = VoiceMessageState.Idle,
|
||||||
composerMode = MessageComposerMode.Normal,
|
composerMode = MessageComposerMode.Normal,
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -661,13 +647,12 @@ internal fun TextComposerSimpleNotEncryptedPreview() = ElementPreview {
|
||||||
internal fun TextComposerFormattingPreview() = ElementPreview {
|
internal fun TextComposerFormattingPreview() = ElementPreview {
|
||||||
PreviewColumn(
|
PreviewColumn(
|
||||||
items = aTextEditorStateRichList()
|
items = aTextEditorStateRichList()
|
||||||
) { _, textEditorState ->
|
) { textEditorState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = textEditorState,
|
state = textEditorState,
|
||||||
voiceMessageState = VoiceMessageState.Idle,
|
voiceMessageState = VoiceMessageState.Idle,
|
||||||
showTextFormatting = true,
|
showTextFormatting = true,
|
||||||
composerMode = MessageComposerMode.Normal,
|
composerMode = MessageComposerMode.Normal,
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -677,13 +662,12 @@ internal fun TextComposerFormattingPreview() = ElementPreview {
|
||||||
internal fun TextComposerFormattingNotEncryptedPreview() = ElementPreview {
|
internal fun TextComposerFormattingNotEncryptedPreview() = ElementPreview {
|
||||||
PreviewColumn(
|
PreviewColumn(
|
||||||
items = aTextEditorStateRichList(isRoomEncrypted = false)
|
items = aTextEditorStateRichList(isRoomEncrypted = false)
|
||||||
) { _, textEditorState ->
|
) { textEditorState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = textEditorState,
|
state = textEditorState,
|
||||||
voiceMessageState = VoiceMessageState.Idle,
|
voiceMessageState = VoiceMessageState.Idle,
|
||||||
showTextFormatting = true,
|
showTextFormatting = true,
|
||||||
composerMode = MessageComposerMode.Normal,
|
composerMode = MessageComposerMode.Normal,
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -693,12 +677,11 @@ internal fun TextComposerFormattingNotEncryptedPreview() = ElementPreview {
|
||||||
internal fun TextComposerEditPreview() = ElementPreview {
|
internal fun TextComposerEditPreview() = ElementPreview {
|
||||||
PreviewColumn(
|
PreviewColumn(
|
||||||
items = aTextEditorStateRichList()
|
items = aTextEditorStateRichList()
|
||||||
) { _, textEditorState ->
|
) { textEditorState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = textEditorState,
|
state = textEditorState,
|
||||||
voiceMessageState = VoiceMessageState.Idle,
|
voiceMessageState = VoiceMessageState.Idle,
|
||||||
composerMode = aMessageComposerModeEdit(),
|
composerMode = aMessageComposerModeEdit(),
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -708,12 +691,11 @@ internal fun TextComposerEditPreview() = ElementPreview {
|
||||||
internal fun TextComposerEditNotEncryptedPreview() = ElementPreview {
|
internal fun TextComposerEditNotEncryptedPreview() = ElementPreview {
|
||||||
PreviewColumn(
|
PreviewColumn(
|
||||||
items = aTextEditorStateRichList(isRoomEncrypted = false)
|
items = aTextEditorStateRichList(isRoomEncrypted = false)
|
||||||
) { _, textEditorState ->
|
) { textEditorState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = textEditorState,
|
state = textEditorState,
|
||||||
voiceMessageState = VoiceMessageState.Idle,
|
voiceMessageState = VoiceMessageState.Idle,
|
||||||
composerMode = aMessageComposerModeEdit(),
|
composerMode = aMessageComposerModeEdit(),
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -723,7 +705,7 @@ internal fun TextComposerEditNotEncryptedPreview() = ElementPreview {
|
||||||
internal fun TextComposerEditCaptionPreview() = ElementPreview {
|
internal fun TextComposerEditCaptionPreview() = ElementPreview {
|
||||||
PreviewColumn(
|
PreviewColumn(
|
||||||
items = aTextEditorStateRichList()
|
items = aTextEditorStateRichList()
|
||||||
) { _, textEditorState ->
|
) { textEditorState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = textEditorState,
|
state = textEditorState,
|
||||||
voiceMessageState = VoiceMessageState.Idle,
|
voiceMessageState = VoiceMessageState.Idle,
|
||||||
|
|
@ -731,7 +713,6 @@ internal fun TextComposerEditCaptionPreview() = ElementPreview {
|
||||||
// Set an existing caption so that the UI will be in edit caption mode
|
// Set an existing caption so that the UI will be in edit caption mode
|
||||||
content = "An existing caption",
|
content = "An existing caption",
|
||||||
),
|
),
|
||||||
enableVoiceMessages = false,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -741,16 +722,14 @@ internal fun TextComposerEditCaptionPreview() = ElementPreview {
|
||||||
internal fun TextComposerAddCaptionPreview() = ElementPreview {
|
internal fun TextComposerAddCaptionPreview() = ElementPreview {
|
||||||
PreviewColumn(
|
PreviewColumn(
|
||||||
items = aTextEditorStateRichList()
|
items = aTextEditorStateRichList()
|
||||||
) { index, textEditorState ->
|
) { textEditorState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = textEditorState,
|
state = textEditorState,
|
||||||
voiceMessageState = VoiceMessageState.Idle,
|
voiceMessageState = VoiceMessageState.Idle,
|
||||||
composerMode = aMessageComposerModeEditCaption(
|
composerMode = aMessageComposerModeEditCaption(
|
||||||
// No caption so that the UI will be in add caption mode
|
// No caption so that the UI will be in add caption mode
|
||||||
content = "",
|
content = "",
|
||||||
showCompatibilityWarning = index == 0,
|
|
||||||
),
|
),
|
||||||
enableVoiceMessages = false,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -760,12 +739,11 @@ internal fun TextComposerAddCaptionPreview() = ElementPreview {
|
||||||
internal fun MarkdownTextComposerEditPreview() = ElementPreview {
|
internal fun MarkdownTextComposerEditPreview() = ElementPreview {
|
||||||
PreviewColumn(
|
PreviewColumn(
|
||||||
items = aTextEditorStateMarkdownList()
|
items = aTextEditorStateMarkdownList()
|
||||||
) { _, textEditorState ->
|
) { textEditorState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = textEditorState,
|
state = textEditorState,
|
||||||
voiceMessageState = VoiceMessageState.Idle,
|
voiceMessageState = VoiceMessageState.Idle,
|
||||||
composerMode = aMessageComposerModeEdit(),
|
composerMode = aMessageComposerModeEdit(),
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -775,14 +753,13 @@ internal fun MarkdownTextComposerEditPreview() = ElementPreview {
|
||||||
internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsProvider::class) inReplyToDetails: InReplyToDetails) = ElementPreview {
|
internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsProvider::class) inReplyToDetails: InReplyToDetails) = ElementPreview {
|
||||||
PreviewColumn(
|
PreviewColumn(
|
||||||
items = aTextEditorStateRichList()
|
items = aTextEditorStateRichList()
|
||||||
) { _, textEditorState ->
|
) { textEditorState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = textEditorState,
|
state = textEditorState,
|
||||||
voiceMessageState = VoiceMessageState.Idle,
|
voiceMessageState = VoiceMessageState.Idle,
|
||||||
composerMode = aMessageComposerModeReply(
|
composerMode = aMessageComposerModeReply(
|
||||||
replyToDetails = inReplyToDetails,
|
replyToDetails = inReplyToDetails,
|
||||||
),
|
),
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -800,14 +777,13 @@ internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsProvider
|
||||||
internal fun TextComposerReplyNotEncryptedPreview(@PreviewParameter(InReplyToDetailsProvider::class) inReplyToDetails: InReplyToDetails) = ElementPreview {
|
internal fun TextComposerReplyNotEncryptedPreview(@PreviewParameter(InReplyToDetailsProvider::class) inReplyToDetails: InReplyToDetails) = ElementPreview {
|
||||||
PreviewColumn(
|
PreviewColumn(
|
||||||
items = aTextEditorStateRichList(isRoomEncrypted = false)
|
items = aTextEditorStateRichList(isRoomEncrypted = false)
|
||||||
) { _, textEditorState ->
|
) { textEditorState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = textEditorState,
|
state = textEditorState,
|
||||||
voiceMessageState = VoiceMessageState.Idle,
|
voiceMessageState = VoiceMessageState.Idle,
|
||||||
composerMode = aMessageComposerModeReply(
|
composerMode = aMessageComposerModeReply(
|
||||||
replyToDetails = inReplyToDetails,
|
replyToDetails = inReplyToDetails,
|
||||||
),
|
),
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -817,16 +793,12 @@ internal fun TextComposerReplyNotEncryptedPreview(@PreviewParameter(InReplyToDet
|
||||||
internal fun TextComposerCaptionPreview() = ElementPreview {
|
internal fun TextComposerCaptionPreview() = ElementPreview {
|
||||||
val list = aTextEditorStateMarkdownList()
|
val list = aTextEditorStateMarkdownList()
|
||||||
PreviewColumn(
|
PreviewColumn(
|
||||||
items = (list + aTextEditorStateMarkdown(initialText = "NO_CAPTION", initialFocus = true)).toPersistentList()
|
items = list,
|
||||||
) { index, textEditorState ->
|
) { textEditorState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = textEditorState,
|
state = textEditorState,
|
||||||
voiceMessageState = VoiceMessageState.Idle,
|
voiceMessageState = VoiceMessageState.Idle,
|
||||||
composerMode = MessageComposerMode.Attachment(
|
composerMode = MessageComposerMode.Attachment,
|
||||||
allowCaption = index < list.size,
|
|
||||||
showCaptionCompatibilityWarning = index == 0,
|
|
||||||
),
|
|
||||||
enableVoiceMessages = false,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -862,12 +834,11 @@ internal fun TextComposerVoicePreview() = ElementPreview {
|
||||||
playbackProgress = 0.0f
|
playbackProgress = 0.0f
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
) { _, voiceMessageState ->
|
) { voiceMessageState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = aTextEditorStateRich(initialFocus = true),
|
state = aTextEditorStateRich(initialFocus = true),
|
||||||
voiceMessageState = voiceMessageState,
|
voiceMessageState = voiceMessageState,
|
||||||
composerMode = MessageComposerMode.Normal,
|
composerMode = MessageComposerMode.Normal,
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -903,12 +874,11 @@ internal fun TextComposerVoiceNotEncryptedPreview() = ElementPreview {
|
||||||
playbackProgress = 0.0f
|
playbackProgress = 0.0f
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
) { _, voiceMessageState ->
|
) { voiceMessageState ->
|
||||||
ATextComposer(
|
ATextComposer(
|
||||||
state = aTextEditorStateRich(initialFocus = true, isRoomEncrypted = false),
|
state = aTextEditorStateRich(initialFocus = true, isRoomEncrypted = false),
|
||||||
voiceMessageState = voiceMessageState,
|
voiceMessageState = voiceMessageState,
|
||||||
composerMode = MessageComposerMode.Normal,
|
composerMode = MessageComposerMode.Normal,
|
||||||
enableVoiceMessages = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -916,15 +886,15 @@ internal fun TextComposerVoiceNotEncryptedPreview() = ElementPreview {
|
||||||
@Composable
|
@Composable
|
||||||
private fun <T> PreviewColumn(
|
private fun <T> PreviewColumn(
|
||||||
items: ImmutableList<T>,
|
items: ImmutableList<T>,
|
||||||
view: @Composable (Int, T) -> Unit,
|
view: @Composable (T) -> Unit,
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
items.forEachIndexed { index, item ->
|
items.forEach { item ->
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.height(IntrinsicSize.Min)
|
modifier = Modifier.height(IntrinsicSize.Min)
|
||||||
) {
|
) {
|
||||||
view(index, item)
|
view(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -935,7 +905,6 @@ private fun ATextComposer(
|
||||||
state: TextEditorState,
|
state: TextEditorState,
|
||||||
voiceMessageState: VoiceMessageState,
|
voiceMessageState: VoiceMessageState,
|
||||||
composerMode: MessageComposerMode,
|
composerMode: MessageComposerMode,
|
||||||
enableVoiceMessages: Boolean,
|
|
||||||
showTextFormatting: Boolean = false,
|
showTextFormatting: Boolean = false,
|
||||||
) {
|
) {
|
||||||
TextComposer(
|
TextComposer(
|
||||||
|
|
@ -943,7 +912,6 @@ private fun ATextComposer(
|
||||||
showTextFormatting = showTextFormatting,
|
showTextFormatting = showTextFormatting,
|
||||||
voiceMessageState = voiceMessageState,
|
voiceMessageState = voiceMessageState,
|
||||||
composerMode = composerMode,
|
composerMode = composerMode,
|
||||||
enableVoiceMessages = enableVoiceMessages,
|
|
||||||
onRequestFocus = {},
|
onRequestFocus = {},
|
||||||
onSendMessage = {},
|
onSendMessage = {},
|
||||||
onResetComposerMode = {},
|
onResetComposerMode = {},
|
||||||
|
|
@ -973,11 +941,9 @@ fun aMessageComposerModeEdit(
|
||||||
fun aMessageComposerModeEditCaption(
|
fun aMessageComposerModeEditCaption(
|
||||||
eventOrTransactionId: EventOrTransactionId = EventId("$1234").toEventOrTransactionId(),
|
eventOrTransactionId: EventOrTransactionId = EventId("$1234").toEventOrTransactionId(),
|
||||||
content: String,
|
content: String,
|
||||||
showCompatibilityWarning: Boolean = false,
|
|
||||||
) = MessageComposerMode.EditCaption(
|
) = MessageComposerMode.EditCaption(
|
||||||
eventOrTransactionId = eventOrTransactionId,
|
eventOrTransactionId = eventOrTransactionId,
|
||||||
content = content,
|
content = content,
|
||||||
showCaptionCompatibilityWarning = showCompatibilityWarning,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun aMessageComposerModeReply(
|
fun aMessageComposerModeReply(
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,7 @@ import io.element.android.libraries.matrix.ui.messages.reply.eventId
|
||||||
sealed interface MessageComposerMode {
|
sealed interface MessageComposerMode {
|
||||||
data object Normal : MessageComposerMode
|
data object Normal : MessageComposerMode
|
||||||
|
|
||||||
data class Attachment(
|
data object Attachment : MessageComposerMode
|
||||||
val allowCaption: Boolean,
|
|
||||||
val showCaptionCompatibilityWarning: Boolean,
|
|
||||||
) : MessageComposerMode
|
|
||||||
|
|
||||||
sealed interface Special : MessageComposerMode
|
sealed interface Special : MessageComposerMode
|
||||||
|
|
||||||
|
|
@ -33,7 +30,6 @@ sealed interface MessageComposerMode {
|
||||||
data class EditCaption(
|
data class EditCaption(
|
||||||
val eventOrTransactionId: EventOrTransactionId,
|
val eventOrTransactionId: EventOrTransactionId,
|
||||||
val content: String,
|
val content: String,
|
||||||
val showCaptionCompatibilityWarning: Boolean,
|
|
||||||
) : Special
|
) : Special
|
||||||
|
|
||||||
data class Reply(
|
data class Reply(
|
||||||
|
|
@ -58,8 +54,8 @@ sealed interface MessageComposerMode {
|
||||||
|
|
||||||
fun MessageComposerMode.showCaptionCompatibilityWarning(): Boolean {
|
fun MessageComposerMode.showCaptionCompatibilityWarning(): Boolean {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is MessageComposerMode.Attachment -> showCaptionCompatibilityWarning
|
is MessageComposerMode.Attachment -> true
|
||||||
is MessageComposerMode.EditCaption -> showCaptionCompatibilityWarning && content.isEmpty()
|
is MessageComposerMode.EditCaption -> content.isEmpty()
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:dbeb0287f75afc662c28cca13fe39f9d24e67ee5b507cf976324d5d9dd4c381b
|
|
||||||
size 83505
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ba8fe03e073a2279c68c49c25388568182ddb1da1e20d7df1ce72d150df51d86
|
oid sha256:b8119afe2e6a12bd41f8e838461c9e3f311ed0b5e5ce3c524a70226feb120996
|
||||||
size 395881
|
size 72836
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:400426592b0020e5de30899574ce0abb70cebccbcff10c43728ee6dae4a421f3
|
oid sha256:8e0cf2a3fc5f92c5b7e8caff9e60ad9495ba1703dbb160aa64f9fd0a1bd4822f
|
||||||
size 400259
|
size 406105
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b8119afe2e6a12bd41f8e838461c9e3f311ed0b5e5ce3c524a70226feb120996
|
oid sha256:dbeb0287f75afc662c28cca13fe39f9d24e67ee5b507cf976324d5d9dd4c381b
|
||||||
size 72836
|
size 83505
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:8e0cf2a3fc5f92c5b7e8caff9e60ad9495ba1703dbb160aa64f9fd0a1bd4822f
|
|
||||||
size 406105
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:55a3aa36196ff755f207e47abb3060a4727a6baa79fe02ce5f5a62d3648619b5
|
oid sha256:4d33345e7d8cb2a55e6d291ae9dcabe66c8ec1ea4124ebc32b679566e585ca0e
|
||||||
size 53661
|
size 54020
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:2c8cd2e6427bc0ffacca88900511cb3761e7bf57fcafb3dbc9ec7f519ec8349d
|
oid sha256:a6af3cf20ad9b57abbe61107f758c143a676dec960fdf583b517d18e404411a3
|
||||||
size 51678
|
size 51953
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9d9c7a841dbe183f84c8527a8745d30aaeee6d7b4aa8a472794b7b4d739b21cd
|
oid sha256:936a9e86eddcb00e20de8af1fe60ae2153c3dfa384f8070466026969a34eb057
|
||||||
size 47268
|
size 45074
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e8275ab2ec26aed30fc162be1d5b49d6b9e9d73c303b00220ac91306dd04280e
|
oid sha256:dee196ccc9872a15897426f2581c178ff92a393136aefc5f7342ba1984d8e418
|
||||||
size 45645
|
size 43492
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:59d57f02efee0ab0f413e3d14d22d069862143e9d6d69fb29fa6972348356016
|
oid sha256:dea3e3f516afb98543f458e0db6472a7157428e25d8170bee6abbc505ece7bd7
|
||||||
size 52332
|
size 52677
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ae8804def7f6922e29ee4564f96d490d233909d6383dc5b92ed091d239ffb609
|
oid sha256:8ff7e86b3aefc4092514c0312a91747ac0a8a3f90234ca9774cd52ab90eeab17
|
||||||
size 50797
|
size 51072
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue