Merge branch 'feature/fga/update-rust-sdk-0.1.27' into feature/fga/room_detail_factory_improvement
This commit is contained in:
@@ -75,6 +75,7 @@ private fun LeaveRoomConfirmationDialog(
|
||||
eventSink: (LeaveRoomEvent) -> Unit,
|
||||
) {
|
||||
ConfirmationDialog(
|
||||
title = stringResource(CommonStrings.action_leave_room),
|
||||
content = stringResource(text),
|
||||
submitText = stringResource(CommonStrings.action_leave),
|
||||
onSubmitClicked = { eventSink(LeaveRoomEvent.LeaveRoom(roomId)) },
|
||||
|
||||
@@ -14,14 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalAnimationApi::class)
|
||||
|
||||
package io.element.android.features.messages.impl.timeline
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
@@ -30,7 +37,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDownward
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.FloatingActionButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
@@ -41,6 +48,8 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
@@ -60,6 +69,7 @@ import io.element.android.libraries.designsystem.theme.components.FloatingAction
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -230,13 +240,11 @@ internal fun BoxScope.TimelineScrollHelper(
|
||||
val firstVisibleItemIndex by remember { derivedStateOf { lazyListState.firstVisibleItemIndex } }
|
||||
val isScrollFinished by remember { derivedStateOf { !lazyListState.isScrollInProgress } }
|
||||
val shouldAutoScrollToBottom by remember { derivedStateOf { lazyListState.firstVisibleItemIndex < 2 } }
|
||||
val showScrollToBottomButton by remember { derivedStateOf { lazyListState.firstVisibleItemIndex > 0 } }
|
||||
|
||||
LaunchedEffect(timelineItems, firstVisibleItemIndex, isScrollFinished) {
|
||||
LaunchedEffect(timelineItems, firstVisibleItemIndex) {
|
||||
if (!isScrollFinished) return@LaunchedEffect
|
||||
|
||||
// Notify the parent composable about the first visible item index when scrolling finishes
|
||||
onScrollFinishedAt(firstVisibleItemIndex)
|
||||
|
||||
// Auto-scroll when new timeline items appear
|
||||
if (shouldAutoScrollToBottom) {
|
||||
coroutineScope.launch {
|
||||
@@ -244,9 +252,22 @@ internal fun BoxScope.TimelineScrollHelper(
|
||||
}
|
||||
}
|
||||
}
|
||||
LaunchedEffect(isScrollFinished) {
|
||||
if (!isScrollFinished) return@LaunchedEffect
|
||||
|
||||
// Jump to bottom button
|
||||
if (!shouldAutoScrollToBottom) {
|
||||
// Notify the parent composable about the first visible item index when scrolling finishes
|
||||
onScrollFinishedAt(firstVisibleItemIndex)
|
||||
}
|
||||
|
||||
// Jump to bottom button (display also in previews)
|
||||
AnimatedVisibility(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(end = 24.dp, bottom = 12.dp),
|
||||
visible = showScrollToBottomButton || LocalInspectionMode.current,
|
||||
enter = scaleIn(),
|
||||
exit = scaleOut(),
|
||||
) {
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
@@ -257,14 +278,24 @@ internal fun BoxScope.TimelineScrollHelper(
|
||||
}
|
||||
}
|
||||
},
|
||||
elevation = FloatingActionButtonDefaults.elevation(4.dp, 4.dp, 4.dp, 4.dp),
|
||||
shape = CircleShape,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.size(40.dp),
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
contentColor = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
.shadow(
|
||||
elevation = 4.dp,
|
||||
shape = CircleShape,
|
||||
ambientColor = ElementTheme.materialColors.primary,
|
||||
spotColor = ElementTheme.materialColors.primary,
|
||||
)
|
||||
.size(36.dp),
|
||||
containerColor = ElementTheme.colors.bgSubtleSecondary,
|
||||
contentColor = ElementTheme.colors.iconSecondary
|
||||
) {
|
||||
Icon(Icons.Default.ArrowDownward, "")
|
||||
Icon(
|
||||
modifier = Modifier.size(24.dp),
|
||||
imageVector = Icons.Filled.ArrowDownward,
|
||||
contentDescription = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import io.element.android.features.roomdetails.impl.R
|
||||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsEvents
|
||||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
|
||||
import io.element.android.features.roomdetails.impl.R
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
|
||||
@@ -71,6 +71,7 @@ internal fun BlockUserDialogs(state: RoomMemberDetailsState) {
|
||||
@Composable
|
||||
internal fun BlockConfirmationDialog(onBlockAction: () -> Unit, onDismiss: () -> Unit) {
|
||||
ConfirmationDialog(
|
||||
title = stringResource(R.string.screen_dm_details_block_user),
|
||||
content = stringResource(R.string.screen_dm_details_block_alert_description),
|
||||
submitText = stringResource(R.string.screen_dm_details_block_alert_action),
|
||||
onSubmitClicked = onBlockAction,
|
||||
@@ -81,6 +82,7 @@ internal fun BlockConfirmationDialog(onBlockAction: () -> Unit, onDismiss: () ->
|
||||
@Composable
|
||||
internal fun UnblockConfirmationDialog(onUnblockAction: () -> Unit, onDismiss: () -> Unit) {
|
||||
ConfirmationDialog(
|
||||
title = stringResource(R.string.screen_dm_details_unblock_user),
|
||||
content = stringResource(R.string.screen_dm_details_unblock_alert_description),
|
||||
submitText = stringResource(R.string.screen_dm_details_unblock_alert_action),
|
||||
onSubmitClicked = onUnblockAction,
|
||||
|
||||
@@ -142,7 +142,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
|
||||
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
||||
molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }
|
||||
timber = "com.jakewharton.timber:timber:5.0.1"
|
||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.26"
|
||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.27"
|
||||
sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
|
||||
sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" }
|
||||
sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
|
||||
@@ -156,7 +156,7 @@ vanniktech_emoji = "com.vanniktech:emoji-google:0.16.0"
|
||||
telephoto_zoomableimage = { module = "me.saket.telephoto:zoomable-image-coil", version.ref = "telephoto" }
|
||||
statemachine = "com.freeletics.flowredux:compose:1.1.0"
|
||||
maplibre = "org.maplibre.gl:android-sdk:10.2.0"
|
||||
maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:1.0.0"
|
||||
maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.0"
|
||||
|
||||
# Analytics
|
||||
posthog = "com.posthog.android:posthog:2.0.3"
|
||||
|
||||
@@ -36,12 +36,12 @@ import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventType
|
||||
import io.element.android.libraries.matrix.impl.core.toProgressWatcher
|
||||
import io.element.android.libraries.matrix.impl.media.map
|
||||
import io.element.android.libraries.matrix.impl.media.sendAttachment
|
||||
import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline
|
||||
import io.element.android.libraries.matrix.impl.timeline.timelineDiffFlow
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -54,6 +54,7 @@ import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.RoomMember
|
||||
import org.matrix.rustcomponents.sdk.RoomSubscription
|
||||
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
|
||||
import org.matrix.rustcomponents.sdk.genTransactionId
|
||||
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
|
||||
import timber.log.Timber
|
||||
@@ -272,30 +273,26 @@ class RustMatrixRoom(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo, progressCallback: ProgressCallback?): Result<Unit> = withContext(
|
||||
coroutineDispatchers.io
|
||||
) {
|
||||
runCatching {
|
||||
override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo, progressCallback: ProgressCallback?): Result<Unit> {
|
||||
return sendAttachment {
|
||||
innerRoom.sendImage(file.path, thumbnailFile.path, imageInfo.map(), progressCallback?.toProgressWatcher())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result<Unit> = withContext(
|
||||
coroutineDispatchers.io
|
||||
) {
|
||||
runCatching {
|
||||
override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result<Unit> {
|
||||
return sendAttachment {
|
||||
innerRoom.sendVideo(file.path, thumbnailFile.path, videoInfo.map(), progressCallback?.toProgressWatcher())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result<Unit> = withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result<Unit> {
|
||||
return sendAttachment {
|
||||
innerRoom.sendAudio(file.path, audioInfo.map(), progressCallback?.toProgressWatcher())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result<Unit> = withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result<Unit> {
|
||||
return sendAttachment {
|
||||
innerRoom.sendFile(file.path, fileInfo.map(), progressCallback?.toProgressWatcher())
|
||||
}
|
||||
}
|
||||
@@ -372,12 +369,23 @@ class RustMatrixRoom(
|
||||
}
|
||||
}
|
||||
|
||||
//TODO expose inner parameters
|
||||
override suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String
|
||||
): Result<Unit> = withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
innerRoom.sendLocation(body, geoUri, genTransactionId())
|
||||
innerRoom.sendLocation(body, geoUri, null, null, null, genTransactionId())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO handle cancellation, need refactoring of how we are catching errors
|
||||
private suspend fun sendAttachment(handle: () -> SendAttachmentJoinHandle): Result<Unit> {
|
||||
return runCatching {
|
||||
handle().use {
|
||||
it.join()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ fun TextComposer(
|
||||
lineCount = it.lineCount
|
||||
},
|
||||
textStyle = defaultTypography.copy(color = MaterialTheme.colorScheme.primary),
|
||||
cursorBrush = SolidColor(ElementTheme.legacyColors.accentColor),
|
||||
cursorBrush = SolidColor(ElementTheme.colors.iconAccentTertiary),
|
||||
decorationBox = { innerTextField ->
|
||||
TextFieldDefaults.DecorationBox(
|
||||
value = text,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user