[Media upload] Add media pickers to the Room screen and the composer (#380)
* Add media pickers to the Room screen and the composer. * Fix exclude rules for translations
This commit is contained in:
parent
581c5ab2d2
commit
014c0f4186
40 changed files with 438 additions and 114 deletions
1
changelog.d/360.feature
Normal file
1
changelog.d/360.feature
Normal file
|
|
@ -0,0 +1 @@
|
|||
Add media pickers to the room screen.
|
||||
|
|
@ -10,9 +10,9 @@
|
|||
<string name="screen_create_room_public_option_title">"Public room (anyone)"</string>
|
||||
<string name="screen_create_room_room_name_label">"Room name"</string>
|
||||
<string name="screen_create_room_room_name_placeholder">"e.g. Product Sprint"</string>
|
||||
<string name="screen_create_room_title">"Create a room"</string>
|
||||
<string name="screen_create_room_topic_label">"Topic (optional)"</string>
|
||||
<string name="screen_create_room_topic_placeholder">"What is this room about?"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"An error occurred when trying to start a chat"</string>
|
||||
<string name="screen_start_chat_unknown_profile">"We can’t validate this user’s Matrix ID. The invite might not be received."</string>
|
||||
<string name="screen_create_room_title">"Create a room"</string>
|
||||
</resources>
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_signout_confirmation_dialog_content">"Are you sure you want to sign out?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Sign out"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Sign out"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Signing out…"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Sign out"</string>
|
||||
<string name="screen_signout_preference_item">"Sign out"</string>
|
||||
</resources>
|
||||
|
|
@ -32,6 +32,7 @@ dependencies {
|
|||
implementation(projects.anvilannotations)
|
||||
anvil(projects.anvilcodegen)
|
||||
api(projects.features.messages.api)
|
||||
implementation(projects.libraries.androidutils)
|
||||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import dagger.assisted.AssistedInject
|
|||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.messages.api.MessagesEntryPoint
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
@ContributesNode(RoomScope::class)
|
||||
class MessagesNode @AssistedInject constructor(
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package io.element.android.features.messages.impl
|
|||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.messages.impl.actionlist.anActionListState
|
||||
import io.element.android.features.messages.impl.textcomposer.AttachmentSourcePicker
|
||||
import io.element.android.features.messages.impl.textcomposer.aMessageComposerState
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemContent
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemList
|
||||
|
|
@ -32,6 +33,8 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
|
|||
get() = sequenceOf(
|
||||
aMessagesState(),
|
||||
aMessagesState().copy(hasNetworkConnection = false),
|
||||
aMessagesState().copy(composerState = aMessageComposerState().copy(attachmentSourcePicker = AttachmentSourcePicker.AllMedia)),
|
||||
aMessagesState().copy(composerState = aMessageComposerState().copy(attachmentSourcePicker = AttachmentSourcePicker.Camera)),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
package io.element.android.features.messages.impl
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
|
@ -35,6 +36,7 @@ import androidx.compose.foundation.layout.statusBars
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ListItem
|
||||
import androidx.compose.material.ModalBottomSheetValue
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
|
|
@ -43,11 +45,14 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -57,20 +62,24 @@ import androidx.compose.ui.unit.sp
|
|||
import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListView
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.textcomposer.AttachmentSourcePicker
|
||||
import io.element.android.features.messages.impl.textcomposer.MessageComposerEvents
|
||||
import io.element.android.features.messages.impl.textcomposer.MessageComposerView
|
||||
import io.element.android.features.messages.impl.timeline.TimelineView
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
|
||||
import io.element.android.libraries.androidutils.ui.hideKeyboard
|
||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.IconButton
|
||||
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheetLayout
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.designsystem.utils.LogCompositions
|
||||
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
|
|
@ -86,9 +95,24 @@ fun MessagesView(
|
|||
initialValue = ModalBottomSheetValue.Hidden,
|
||||
)
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val focusManager = LocalFocusManager.current
|
||||
val composerState = state.composerState
|
||||
val initialBottomSheetState = if (LocalInspectionMode.current && composerState.attachmentSourcePicker != null) {
|
||||
ModalBottomSheetValue.Expanded
|
||||
} else {
|
||||
ModalBottomSheetValue.Hidden
|
||||
}
|
||||
val bottomSheetState = rememberModalBottomSheetState(initialValue = initialBottomSheetState)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
BackHandler(enabled = bottomSheetState.isVisible) {
|
||||
coroutineScope.launch {
|
||||
bottomSheetState.hide()
|
||||
}
|
||||
}
|
||||
|
||||
// This is needed because the composer is inside an AndroidView that can't be affected by the FocusManager in Compose
|
||||
val localView = LocalView.current
|
||||
|
||||
LogCompositions(tag = "MessagesScreen", msg = "Content")
|
||||
|
||||
fun onMessageClicked(event: TimelineItem.Event) {
|
||||
|
|
@ -97,7 +121,7 @@ fun MessagesView(
|
|||
|
||||
fun onMessageLongClicked(event: TimelineItem.Event) {
|
||||
Timber.v("OnMessageLongClicked= ${event.id}")
|
||||
focusManager.clearFocus(force = true)
|
||||
localView.hideKeyboard()
|
||||
state.actionListState.eventSink(ActionListEvents.ComputeForMessage(event))
|
||||
coroutineScope.launch {
|
||||
itemActionsBottomSheetState.show()
|
||||
|
|
@ -108,41 +132,67 @@ fun MessagesView(
|
|||
state.eventSink(MessagesEvents.HandleAction(action, event))
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
contentWindowInsets = WindowInsets.statusBars,
|
||||
topBar = {
|
||||
Column {
|
||||
ConnectivityIndicatorView(isOnline = state.hasNetworkConnection)
|
||||
MessagesViewTopBar(
|
||||
roomTitle = state.roomName,
|
||||
roomAvatar = state.roomAvatar,
|
||||
onBackPressed = onBackPressed,
|
||||
onRoomDetailsClicked = onRoomDetailsClicked,
|
||||
LaunchedEffect(composerState.attachmentSourcePicker) {
|
||||
if (composerState.attachmentSourcePicker != null) {
|
||||
// We need to use this instead of `LocalFocusManager.clearFocus()` to hide the keyboard when focus is on an Android View
|
||||
localView.hideKeyboard()
|
||||
bottomSheetState.show()
|
||||
} else {
|
||||
bottomSheetState.hide()
|
||||
}
|
||||
}
|
||||
// Send 'DismissAttachmentMenu' event when the bottomsheet was just hidden
|
||||
LaunchedEffect(bottomSheetState.isVisible) {
|
||||
if (!bottomSheetState.isVisible) {
|
||||
composerState.eventSink(MessageComposerEvents.DismissAttachmentMenu)
|
||||
}
|
||||
}
|
||||
ModalBottomSheetLayout(
|
||||
sheetState = bottomSheetState,
|
||||
displayHandle = true,
|
||||
sheetContent = {
|
||||
MediaPickerMenu(
|
||||
addAttachmentSourcePicker = composerState.attachmentSourcePicker,
|
||||
eventSink = composerState.eventSink
|
||||
)
|
||||
}
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
contentWindowInsets = WindowInsets.statusBars,
|
||||
topBar = {
|
||||
Column {
|
||||
ConnectivityIndicatorView(isOnline = state.hasNetworkConnection)
|
||||
MessagesViewTopBar(
|
||||
roomTitle = state.roomName,
|
||||
roomAvatar = state.roomAvatar,
|
||||
onBackPressed = onBackPressed,
|
||||
onRoomDetailsClicked = onRoomDetailsClicked,
|
||||
)
|
||||
}
|
||||
},
|
||||
content = { padding ->
|
||||
MessagesViewContent(
|
||||
state = state,
|
||||
modifier = Modifier.padding(padding),
|
||||
onMessageClicked = ::onMessageClicked,
|
||||
onMessageLongClicked = ::onMessageLongClicked
|
||||
)
|
||||
}
|
||||
},
|
||||
content = { padding ->
|
||||
MessagesViewContent(
|
||||
state = state,
|
||||
modifier = Modifier.padding(padding),
|
||||
onMessageClicked = ::onMessageClicked,
|
||||
onMessageLongClicked = ::onMessageLongClicked
|
||||
)
|
||||
},
|
||||
snackbarHost = {
|
||||
SnackbarHost(
|
||||
snackbarHostState,
|
||||
modifier = Modifier.navigationBarsPadding()
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
snackbarHost = {
|
||||
SnackbarHost(
|
||||
snackbarHostState,
|
||||
modifier = Modifier.navigationBarsPadding()
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
ActionListView(
|
||||
state = state.actionListState,
|
||||
modalBottomSheetState = itemActionsBottomSheetState,
|
||||
onActionSelected = ::onActionSelected
|
||||
)
|
||||
ActionListView(
|
||||
state = state.actionListState,
|
||||
modalBottomSheetState = itemActionsBottomSheetState,
|
||||
onActionSelected = ::onActionSelected
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -216,6 +266,53 @@ fun MessagesViewTopBar(
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun MediaPickerMenu(
|
||||
addAttachmentSourcePicker: AttachmentSourcePicker?,
|
||||
eventSink: (MessageComposerEvents) -> Unit,
|
||||
) {
|
||||
when (addAttachmentSourcePicker) {
|
||||
null -> return
|
||||
AttachmentSourcePicker.AllMedia -> AllMediaSourcePickerMenu(eventSink = eventSink)
|
||||
AttachmentSourcePicker.Camera -> CameraSourcePickerMenu(eventSink = eventSink)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
internal fun AllMediaSourcePickerMenu(
|
||||
eventSink: (MessageComposerEvents) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier) {
|
||||
ListItem(Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) }) {
|
||||
Text(stringResource(R.string.screen_room_attachment_source_gallery))
|
||||
}
|
||||
ListItem(Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) }) {
|
||||
Text(stringResource(R.string.screen_room_attachment_source_files))
|
||||
}
|
||||
ListItem(Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.FromCamera) }) {
|
||||
Text(stringResource(R.string.screen_room_attachment_source_camera))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
internal fun CameraSourcePickerMenu(
|
||||
eventSink: (MessageComposerEvents) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier) {
|
||||
ListItem(Modifier.clickable { eventSink(MessageComposerEvents.PickCameraAttachmentSource.Photo) }) {
|
||||
Text(stringResource(R.string.screen_room_attachment_source_camera_photo))
|
||||
}
|
||||
ListItem(Modifier.clickable { eventSink(MessageComposerEvents.PickCameraAttachmentSource.Video) }) {
|
||||
Text(stringResource(R.string.screen_room_attachment_source_camera_video))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
internal fun MessagesViewLightPreview(@PreviewParameter(MessagesStateProvider::class) state: MessagesState) =
|
||||
|
|
|
|||
|
|
@ -24,6 +24,15 @@ sealed interface MessageComposerEvents {
|
|||
object CloseSpecialMode : MessageComposerEvents
|
||||
data class SetMode(val composerMode: MessageComposerMode) : MessageComposerEvents
|
||||
data class UpdateText(val text: CharSequence) : MessageComposerEvents
|
||||
|
||||
object TakePhoto : MessageComposerEvents
|
||||
object AddAttachment : MessageComposerEvents
|
||||
object DismissAttachmentMenu : MessageComposerEvents
|
||||
sealed interface PickAttachmentSource : MessageComposerEvents {
|
||||
object FromGallery : PickAttachmentSource
|
||||
object FromCamera : PickAttachmentSource
|
||||
object FromFiles : PickAttachmentSource
|
||||
}
|
||||
sealed interface PickCameraAttachmentSource : MessageComposerEvents {
|
||||
object Photo : PickCameraAttachmentSource
|
||||
object Video : PickCameraAttachmentSource
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,13 +19,17 @@ package io.element.android.features.messages.impl.textcomposer
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.data.StableCharSequence
|
||||
import io.element.android.libraries.core.data.toStableCharSequence
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
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.MatrixRoom
|
||||
|
|
@ -36,6 +40,7 @@ import kotlinx.coroutines.launch
|
|||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@SingleIn(RoomScope::class)
|
||||
class MessageComposerPresenter @Inject constructor(
|
||||
private val appCoroutineScope: CoroutineScope,
|
||||
private val room: MatrixRoom,
|
||||
|
|
@ -47,11 +52,22 @@ class MessageComposerPresenter @Inject constructor(
|
|||
override fun present(): MessageComposerState {
|
||||
val localCoroutineScope = rememberCoroutineScope()
|
||||
|
||||
// Example usage of custom pickers
|
||||
val galleryMediaPicker = mediaPickerProvider.registerGalleryPicker(onResult = { uri ->
|
||||
Timber.d("Media picked from $uri")
|
||||
})
|
||||
|
||||
val filesPicker = mediaPickerProvider.registerFilePicker(onResult = { uri ->
|
||||
Timber.d("File picked from $uri")
|
||||
})
|
||||
|
||||
val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker(onResult = { uri ->
|
||||
Timber.d("Photo saved at $uri")
|
||||
})
|
||||
|
||||
val cameraVideoPicker = mediaPickerProvider.registerCameraVideoPicker(onResult = { uri ->
|
||||
Timber.d("Video saved at $uri")
|
||||
})
|
||||
|
||||
val isFullScreen = rememberSaveable {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
|
@ -62,6 +78,8 @@ class MessageComposerPresenter @Inject constructor(
|
|||
mutableStateOf(MessageComposerMode.Normal(""))
|
||||
}
|
||||
|
||||
var attachmentSourcePicker: AttachmentSourcePicker? by remember { mutableStateOf(null) }
|
||||
|
||||
LaunchedEffect(composerMode.value) {
|
||||
when (val modeValue = composerMode.value) {
|
||||
is MessageComposerMode.Edit -> text.value = modeValue.defaultContent.toStableCharSequence()
|
||||
|
|
@ -80,21 +98,47 @@ class MessageComposerPresenter @Inject constructor(
|
|||
|
||||
is MessageComposerEvents.SendMessage -> appCoroutineScope.sendMessage(event.message, composerMode, text)
|
||||
is MessageComposerEvents.SetMode -> composerMode.value = event.composerMode
|
||||
MessageComposerEvents.TakePhoto -> localCoroutineScope.launch {
|
||||
if (featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow)) {
|
||||
cameraPhotoPicker.launch()
|
||||
}
|
||||
}}
|
||||
MessageComposerEvents.AddAttachment -> localCoroutineScope.ifMediaPickersEnabled {
|
||||
attachmentSourcePicker = AttachmentSourcePicker.AllMedia
|
||||
}
|
||||
MessageComposerEvents.DismissAttachmentMenu -> attachmentSourcePicker = null
|
||||
MessageComposerEvents.PickAttachmentSource.FromGallery -> localCoroutineScope.ifMediaPickersEnabled {
|
||||
attachmentSourcePicker = null
|
||||
galleryMediaPicker.launch()
|
||||
}
|
||||
MessageComposerEvents.PickAttachmentSource.FromFiles -> localCoroutineScope.ifMediaPickersEnabled {
|
||||
attachmentSourcePicker = null
|
||||
filesPicker.launch()
|
||||
}
|
||||
MessageComposerEvents.PickAttachmentSource.FromCamera -> localCoroutineScope.ifMediaPickersEnabled {
|
||||
attachmentSourcePicker = AttachmentSourcePicker.Camera
|
||||
}
|
||||
MessageComposerEvents.PickCameraAttachmentSource.Photo -> localCoroutineScope.ifMediaPickersEnabled {
|
||||
attachmentSourcePicker = null
|
||||
cameraPhotoPicker.launch()
|
||||
}
|
||||
MessageComposerEvents.PickCameraAttachmentSource.Video -> localCoroutineScope.ifMediaPickersEnabled {
|
||||
attachmentSourcePicker = null
|
||||
cameraVideoPicker.launch()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MessageComposerState(
|
||||
text = text.value,
|
||||
isFullScreen = isFullScreen.value,
|
||||
mode = composerMode.value,
|
||||
attachmentSourcePicker = attachmentSourcePicker,
|
||||
eventSink = ::handleEvents
|
||||
)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.ifMediaPickersEnabled(action: suspend () -> Unit) = launch {
|
||||
if (featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow)) {
|
||||
action()
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableState<MessageComposerMode>.setToNormal() {
|
||||
value = MessageComposerMode.Normal("")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,13 @@ data class MessageComposerState(
|
|||
val text: StableCharSequence?,
|
||||
val isFullScreen: Boolean,
|
||||
val mode: MessageComposerMode,
|
||||
val attachmentSourcePicker: AttachmentSourcePicker?,
|
||||
val eventSink: (MessageComposerEvents) -> Unit
|
||||
) {
|
||||
val isSendButtonVisible: Boolean = text?.charSequence.isNullOrEmpty().not()
|
||||
}
|
||||
|
||||
sealed interface AttachmentSourcePicker {
|
||||
object AllMedia : AttachmentSourcePicker
|
||||
object Camera : AttachmentSourcePicker
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,5 +31,6 @@ fun aMessageComposerState() = MessageComposerState(
|
|||
text = StableCharSequence(""),
|
||||
isFullScreen = false,
|
||||
mode = MessageComposerMode.Normal(content = ""),
|
||||
attachmentSourcePicker = null,
|
||||
eventSink = {}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ fun MessageComposerView(
|
|||
onCloseSpecialMode = ::onCloseSpecialMode,
|
||||
onComposerTextChange = ::onComposerTextChange,
|
||||
onAddAttachment = {
|
||||
state.eventSink(MessageComposerEvents.TakePhoto)
|
||||
state.eventSink(MessageComposerEvents.AddAttachment)
|
||||
},
|
||||
composerCanSendMessage = state.isSendButtonVisible,
|
||||
composerText = state.text?.charSequence?.toString(),
|
||||
|
|
|
|||
9
features/messages/impl/src/main/res/values/localazy.xml
Normal file
9
features/messages/impl/src/main/res/values/localazy.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_attachment_source_camera">"Camera"</string>
|
||||
<string name="screen_room_attachment_source_camera_photo">"Take photo"</string>
|
||||
<string name="screen_room_attachment_source_camera_video">"Record a video"</string>
|
||||
<string name="screen_room_attachment_source_files">"Attachment"</string>
|
||||
<string name="screen_room_attachment_source_gallery">"Photo & Video Library"</string>
|
||||
<string name="screen_room_error_failed_processing_media">"Failed processing media to upload, please try again."</string>
|
||||
</resources>
|
||||
|
|
@ -22,7 +22,9 @@ import app.cash.molecule.RecompositionClock
|
|||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.ReceiveTurbine
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.textcomposer.AttachmentSourcePicker
|
||||
import io.element.android.features.messages.impl.textcomposer.MessageComposerEvents
|
||||
import io.element.android.features.messages.impl.textcomposer.MessageComposerPresenter
|
||||
import io.element.android.features.messages.impl.textcomposer.MessageComposerState
|
||||
|
|
@ -279,6 +281,103 @@ class MessageComposerPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - Open attachments menu`() = runTest {
|
||||
val fakeMatrixRoom = FakeMatrixRoom()
|
||||
val presenter = MessageComposerPresenter(
|
||||
this,
|
||||
fakeMatrixRoom,
|
||||
pickerProvider,
|
||||
featureFlagService,
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessageComposerEvents.AddAttachment)
|
||||
|
||||
assertThat(awaitItem().attachmentSourcePicker).isEqualTo(AttachmentSourcePicker.AllMedia)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - Open camera attachments menu`() = runTest {
|
||||
val fakeMatrixRoom = FakeMatrixRoom()
|
||||
val presenter = MessageComposerPresenter(
|
||||
this,
|
||||
fakeMatrixRoom,
|
||||
pickerProvider,
|
||||
featureFlagService,
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromCamera)
|
||||
|
||||
assertThat(awaitItem().attachmentSourcePicker).isEqualTo(AttachmentSourcePicker.Camera)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - Dismiss attachments menu`() = runTest {
|
||||
val fakeMatrixRoom = FakeMatrixRoom()
|
||||
val presenter = MessageComposerPresenter(
|
||||
this,
|
||||
fakeMatrixRoom,
|
||||
pickerProvider,
|
||||
featureFlagService,
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessageComposerEvents.AddAttachment)
|
||||
skipItems(1)
|
||||
|
||||
initialState.eventSink(MessageComposerEvents.DismissAttachmentMenu)
|
||||
assertThat(awaitItem().attachmentSourcePicker).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - Pick media from gallery`() = runTest {
|
||||
val fakeMatrixRoom = FakeMatrixRoom()
|
||||
val presenter = MessageComposerPresenter(
|
||||
this,
|
||||
fakeMatrixRoom,
|
||||
pickerProvider,
|
||||
featureFlagService,
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery)
|
||||
|
||||
// TODO verify some post processing of the selected media is done
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - Pick file from storage`() = runTest {
|
||||
val fakeMatrixRoom = FakeMatrixRoom()
|
||||
val presenter = MessageComposerPresenter(
|
||||
this,
|
||||
fakeMatrixRoom,
|
||||
pickerProvider,
|
||||
featureFlagService,
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles)
|
||||
|
||||
// TODO verify some post processing of the selected media is done
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - Take photo`() = runTest {
|
||||
val fakeMatrixRoom = FakeMatrixRoom()
|
||||
|
|
@ -292,11 +391,30 @@ class MessageComposerPresenterTest {
|
|||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessageComposerEvents.TakePhoto)
|
||||
initialState.eventSink(MessageComposerEvents.PickCameraAttachmentSource.Photo)
|
||||
|
||||
// TODO verify some post processing of the captured image is done
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - Record video`() = runTest {
|
||||
val fakeMatrixRoom = FakeMatrixRoom()
|
||||
val presenter = MessageComposerPresenter(
|
||||
this,
|
||||
fakeMatrixRoom,
|
||||
pickerProvider,
|
||||
featureFlagService,
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MessageComposerEvents.PickCameraAttachmentSource.Video)
|
||||
|
||||
// TODO verify some post processing of the captured video is done
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun anEditMode() = MessageComposerMode.Edit(AN_EVENT_ID, A_MESSAGE)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
</plurals>
|
||||
<string name="screen_room_details_encryption_enabled_subtitle">"Messages are secured with locks. Only you and the recipients have the unique keys to unlock them."</string>
|
||||
<string name="screen_room_details_encryption_enabled_title">"Message encryption enabled"</string>
|
||||
<string name="screen_room_details_invite_people_title">"Invite people"</string>
|
||||
<string name="screen_room_details_share_room_title">"Share room"</string>
|
||||
<string name="screen_dm_details_block_alert_action">"Block"</string>
|
||||
<string name="screen_dm_details_block_alert_description">"Blocked users will not be able to send you messages and all message by them will be hidden. You can reverse this action anytime."</string>
|
||||
|
|
@ -14,6 +13,7 @@
|
|||
<string name="screen_dm_details_unblock_alert_action">"Unblock"</string>
|
||||
<string name="screen_dm_details_unblock_alert_description">"On unblocking the user, you will be able to see all messages by them again."</string>
|
||||
<string name="screen_dm_details_unblock_user">"Unblock user"</string>
|
||||
<string name="screen_room_details_invite_people_title">"Invite people"</string>
|
||||
<string name="screen_room_details_leave_room_title">"Leave room"</string>
|
||||
<string name="screen_room_details_people_title">"People"</string>
|
||||
<string name="screen_room_details_security_title">"Security"</string>
|
||||
|
|
|
|||
|
|
@ -19,9 +19,17 @@
|
|||
package io.element.android.libraries.designsystem.theme.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CornerSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ModalBottomSheetDefaults
|
||||
import androidx.compose.material.ModalBottomSheetState
|
||||
|
|
@ -30,12 +38,14 @@ import androidx.compose.material.rememberModalBottomSheetState
|
|||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.modifiers.applyIf
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
|
|
@ -46,15 +56,36 @@ fun ModalBottomSheetLayout(
|
|||
sheetContent: @Composable ColumnScope.() -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
sheetState: ModalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden),
|
||||
sheetShape: Shape = MaterialTheme.shapes.large,
|
||||
sheetShape: Shape = MaterialTheme.shapes.large.copy(bottomStart = CornerSize(0.dp), bottomEnd = CornerSize(0.dp)),
|
||||
sheetElevation: Dp = ModalBottomSheetDefaults.Elevation,
|
||||
sheetBackgroundColor: Color = MaterialTheme.colorScheme.surface,
|
||||
sheetContentColor: Color = contentColorFor(sheetBackgroundColor),
|
||||
scrimColor: Color = ModalBottomSheetDefaults.scrimColor,
|
||||
displayHandle: Boolean = false,
|
||||
useSystemPadding: Boolean = true,
|
||||
content: @Composable () -> Unit = {}
|
||||
) {
|
||||
androidx.compose.material.ModalBottomSheetLayout(
|
||||
sheetContent = sheetContent,
|
||||
sheetContent = {
|
||||
Column(
|
||||
Modifier.fillMaxWidth()
|
||||
.applyIf(useSystemPadding, ifTrue = {
|
||||
navigationBarsPadding()
|
||||
})
|
||||
) {
|
||||
if (displayHandle) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.onSurfaceVariant, RoundedCornerShape(2.dp))
|
||||
.size(width = 32.dp, height = 4.dp)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
sheetContent()
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
sheetState = sheetState,
|
||||
sheetShape = sheetShape,
|
||||
|
|
@ -79,10 +110,13 @@ internal fun ModalBottomSheetLayoutDarkPreview() =
|
|||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
ModalBottomSheetLayout(
|
||||
modifier = Modifier.height(100.dp),
|
||||
modifier = Modifier.height(140.dp),
|
||||
displayHandle = true,
|
||||
sheetState = ModalBottomSheetState(ModalBottomSheetValue.Expanded),
|
||||
sheetContent = {
|
||||
Text(text = "Sheet Content", modifier = Modifier.padding(16.dp).background(color = Color.Green))
|
||||
Text(text = "Sheet Content", modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 20.dp)
|
||||
.background(color = Color.Green))
|
||||
}
|
||||
) {
|
||||
Text(text = "Content", modifier = Modifier.background(color = Color.Red))
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import androidx.compose.material3.MaterialTheme
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -55,8 +57,9 @@ fun TextComposer(
|
|||
if (LocalInspectionMode.current) {
|
||||
FakeComposer(modifier)
|
||||
} else {
|
||||
val focusRequester = FocusRequester()
|
||||
AndroidView(
|
||||
modifier = modifier,
|
||||
modifier = modifier.focusRequester(focusRequester),
|
||||
factory = { context ->
|
||||
RichTextComposerLayout(context).apply {
|
||||
// Sets up listeners for View -> Compose communication
|
||||
|
|
|
|||
|
|
@ -95,14 +95,10 @@
|
|||
<string name="room_timeline_read_marker_title">"Neu"</string>
|
||||
<string name="screen_analytics_settings_share_data">"Teile Analyse-Daten"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Medienauswahl fehlgeschlagen, bitte versuche es erneut."</string>
|
||||
<string name="screen_room_member_details_block_alert_action">"Blockieren"</string>
|
||||
<string name="screen_room_member_details_block_user">"Nutzer blockieren"</string>
|
||||
<string name="screen_room_member_details_unblock_alert_action">"Blockierung aufheben"</string>
|
||||
<string name="screen_room_member_details_unblock_user">"Nutzer entblockieren"</string>
|
||||
<string name="settings_rageshake_detection_threshold">"Erkennungsschwelle"</string>
|
||||
<string name="settings_version_number">"Version: %1$s (%2$s)"</string>
|
||||
<string name="test_language_identifier">"de"</string>
|
||||
<string name="dialog_title_error">"Fehler"</string>
|
||||
<string name="dialog_title_success">"Erfolg"</string>
|
||||
<string name="screen_report_content_block_user">"Nutzer blockieren"</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -128,12 +128,6 @@
|
|||
<string name="room_timeline_beginning_of_room_no_name">"Este es el principio de esta conversación."</string>
|
||||
<string name="room_timeline_read_marker_title">"Nuevos"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Marque si quieres ocultar todos los mensajes actuales y futuros de este usuario"</string>
|
||||
<string name="screen_room_member_details_block_alert_action">"Bloquear"</string>
|
||||
<string name="screen_room_member_details_block_alert_description">"Los usuarios bloqueados no podrán enviarte mensajes y se ocultarán todos sus mensajes. Puede revertir esta acción en cualquier momento."</string>
|
||||
<string name="screen_room_member_details_block_user">"Bloquear usuario"</string>
|
||||
<string name="screen_room_member_details_unblock_alert_action">"Desbloquear"</string>
|
||||
<string name="screen_room_member_details_unblock_alert_description">"Al desbloquear al usuario, podrás volver a ver todos sus mensajes."</string>
|
||||
<string name="screen_room_member_details_unblock_user">"Desbloquear usuario"</string>
|
||||
<string name="settings_rageshake">"Agitar con fuerza"</string>
|
||||
<string name="settings_rageshake_detection_threshold">"Umbral de detección"</string>
|
||||
<string name="settings_title_general">"General"</string>
|
||||
|
|
@ -142,4 +136,4 @@
|
|||
<string name="dialog_title_error">"Error"</string>
|
||||
<string name="dialog_title_success">"Terminado"</string>
|
||||
<string name="screen_report_content_block_user">"Bloquear usuario"</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -128,12 +128,6 @@
|
|||
<string name="room_timeline_beginning_of_room_no_name">"Questo è l\'inizio della conversazione."</string>
|
||||
<string name="room_timeline_read_marker_title">"Nuovo"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Seleziona se vuoi nascondere tutti i messaggi attuali e futuri di questo utente"</string>
|
||||
<string name="screen_room_member_details_block_alert_action">"Blocca"</string>
|
||||
<string name="screen_room_member_details_block_alert_description">"Gli utenti bloccati non saranno in grado di inviarti nuovi messaggi e tutti quelli già esistenti saranno nascosti. Potrai annullare questa azione in qualsiasi momento."</string>
|
||||
<string name="screen_room_member_details_block_user">"Blocca utente"</string>
|
||||
<string name="screen_room_member_details_unblock_alert_action">"Sblocca"</string>
|
||||
<string name="screen_room_member_details_unblock_alert_description">"Dopo aver sbloccato l\'utente, potrai vedere nuovamente tutti i suoi messaggi."</string>
|
||||
<string name="screen_room_member_details_unblock_user">"Sblocca utente"</string>
|
||||
<string name="settings_rageshake">"Rageshake"</string>
|
||||
<string name="settings_rageshake_detection_threshold">"Soglia di rilevamento"</string>
|
||||
<string name="settings_title_general">"Generali"</string>
|
||||
|
|
@ -142,4 +136,4 @@
|
|||
<string name="dialog_title_error">"Errore"</string>
|
||||
<string name="dialog_title_success">"Operazione riuscita"</string>
|
||||
<string name="screen_report_content_block_user">"Blocca utente"</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -143,12 +143,6 @@
|
|||
<string name="screen_analytics_prompt_third_party_sharing"><b>"Nu"</b>" împărtășim informații cu terți"</string>
|
||||
<string name="screen_analytics_prompt_title">"Ajutați la îmbunătățirea %1$s"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Confirmați că doriți să ascundeți toate mesajele curente și viitoare de la acest utilizator"</string>
|
||||
<string name="screen_room_member_details_block_alert_action">"Blocați"</string>
|
||||
<string name="screen_room_member_details_block_alert_description">"Utilizatorii blocați nu vă vor putea trimite mesaje și toate mesajele lor vor fi ascunse. Puteți anula această acțiune oricând."</string>
|
||||
<string name="screen_room_member_details_block_user">"Blocați utilizatorul"</string>
|
||||
<string name="screen_room_member_details_unblock_alert_action">"Deblocați"</string>
|
||||
<string name="screen_room_member_details_unblock_alert_description">"La deblocarea utilizatorului, veți putea vedea din nou toate mesajele de la acesta."</string>
|
||||
<string name="screen_room_member_details_unblock_user">"Deblocați utilizatorul"</string>
|
||||
<string name="settings_rageshake">"Rageshake"</string>
|
||||
<string name="settings_rageshake_detection_threshold">"Prag de detecție"</string>
|
||||
<string name="settings_title_general">"General"</string>
|
||||
|
|
@ -160,4 +154,4 @@
|
|||
<string name="screen_analytics_settings_read_terms">"Puteți citi toate condițiile noastre %1$s."</string>
|
||||
<string name="screen_analytics_settings_read_terms_content_link">"aici"</string>
|
||||
<string name="screen_report_content_block_user">"Blocați utilizatorul"</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -147,12 +147,6 @@
|
|||
<string name="screen_media_upload_preview_error_failed_processing">"Failed processing media to upload, please try again."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Failed uploading media, please try again."</string>
|
||||
<string name="screen_report_content_block_user_hint">"Check if you want to hide all current and future messages from this user"</string>
|
||||
<string name="screen_room_member_details_block_alert_action">"Block"</string>
|
||||
<string name="screen_room_member_details_block_alert_description">"Blocked users will not be able to send you messages and all message by them will be hidden. You can reverse this action anytime."</string>
|
||||
<string name="screen_room_member_details_block_user">"Block user"</string>
|
||||
<string name="screen_room_member_details_unblock_alert_action">"Unblock"</string>
|
||||
<string name="screen_room_member_details_unblock_alert_description">"On unblocking the user, you will be able to see all messages by them again."</string>
|
||||
<string name="screen_room_member_details_unblock_user">"Unblock user"</string>
|
||||
<string name="settings_rageshake">"Rageshake"</string>
|
||||
<string name="settings_rageshake_detection_threshold">"Detection threshold"</string>
|
||||
<string name="settings_title_general">"General"</string>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:21698ff1c1f2b30ee9c3cc0c2539b35fe7cf54aac07cb0dc376d7c1a03c8814b
|
||||
size 4483
|
||||
oid sha256:e3cb476c16c2cae9f3230cc4030b66662b9f63cef22208fc5bf577d0b16bf946
|
||||
size 4484
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:21698ff1c1f2b30ee9c3cc0c2539b35fe7cf54aac07cb0dc376d7c1a03c8814b
|
||||
size 4483
|
||||
oid sha256:e3cb476c16c2cae9f3230cc4030b66662b9f63cef22208fc5bf577d0b16bf946
|
||||
size 4484
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:db69f27f60dd9d93bb4d313741b84aa4a3ed008d229590338514c7683c0e3a11
|
||||
size 14786
|
||||
oid sha256:dea394d708a714603ea77543a7ab31550baaea72c75255c56ac9162589096128
|
||||
size 14453
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fc3bf884b0425c72cafecdd4afa4e2c28064799f695962360ae4c979a3fe542e
|
||||
size 4490
|
||||
oid sha256:54b434198b8b6b534e0e82310e58eec162f18aba876f0dca9a1790c137230595
|
||||
size 4496
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fc3bf884b0425c72cafecdd4afa4e2c28064799f695962360ae4c979a3fe542e
|
||||
size 4490
|
||||
oid sha256:54b434198b8b6b534e0e82310e58eec162f18aba876f0dca9a1790c137230595
|
||||
size 4496
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6c630475e03d86195a0ebcc57bd12934b799fee956c635b30df60913cd9a3f50
|
||||
size 16032
|
||||
oid sha256:f3080445c87d85fd5c51228e33ef7f91eb3a718f2f8288bdfa2a48d6769a25a1
|
||||
size 15480
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d6de6a4dee8a62839c15a84de8cb9817b3de8ae9fd2317e723c47bb679a72b7d
|
||||
size 39603
|
||||
oid sha256:01f54909964a4ed07d8850ab2bffad8b99ed641d731c1808b04f164ff3e0cbba
|
||||
size 39632
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4d8f233685a21a72081b24ee91bc90e94b340c39f7e2c498f76a69e5b7e129ff
|
||||
size 41480
|
||||
oid sha256:4ea39fdf1cb61657bc1eafb7d353ee15517c26c0e6bb2804b7c8350fa651299e
|
||||
size 41508
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b7d7470933e27c64182c5f8acec7b4ce3896db1cc3836ff6aa6cf33fe8688f10
|
||||
size 37524
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3ac05963f89ac0d3045700447a40a4a6f38cc155812de7eb9a87fb9a01643033
|
||||
size 36300
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:caaf172ddb39cbd1a77bfb2295202e5c0aa95c846353a17da6abe6c50316cf63
|
||||
size 38510
|
||||
oid sha256:c6c558d2a8e6adb4831b91aff249d16dfc48389adef5ada6e1215df1d597639a
|
||||
size 38654
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:91a5fe69790f195bba47a878efb1d93a4e35bff9ec11f262cdf9c237b4cc3639
|
||||
size 40563
|
||||
oid sha256:946802d97d29f9fe41a71f1ee343e298e1b0d9dbbc6561cc82fed712e623f89c
|
||||
size 40708
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:97f10ca5290a0b4b1e034e157abb0f069fac2f2c2e88234edb1efc25c6c111f0
|
||||
size 36228
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cfc76bb6a8d8b269f53c2cc1c369a5c5ffb0f0fb858ebfa50c613a793bc1b421
|
||||
size 35195
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4b36389c997a7796eb7c070d4927c9a5197bca08b24cf58797de9d622c8a6176
|
||||
size 13945
|
||||
oid sha256:5eb57bf5069755caaa2188db848517a915b298ec3ba72334e27c90b354d0b4ef
|
||||
size 14302
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1579ab462c62f872774fbdb820da1f6abc09e4b8e6fb2e3baaf3d46a0722ed94
|
||||
size 15471
|
||||
oid sha256:4c37402ebdad96583f879614d07ea45f5193f6976ce5f2382333106dda77f095
|
||||
size 15520
|
||||
|
|
|
|||
|
|
@ -85,6 +85,18 @@
|
|||
"screen_room_member_list_.*",
|
||||
"screen_dm_details_.*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": ":features:messages:impl",
|
||||
"includeRegex": [
|
||||
"screen_room_.*",
|
||||
"screen_dm_details_.*"
|
||||
],
|
||||
"excludeRegex": [
|
||||
"screen_room_details_.*",
|
||||
"screen_room_member.*",
|
||||
"screen_dm_.*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,10 +36,13 @@ allActions = []
|
|||
# Iterating on the config
|
||||
for entry in config["modules"]:
|
||||
# Create action for the default language
|
||||
excludeRegex = regexToAlwaysExclude
|
||||
if "excludeRegex" in entry:
|
||||
excludeRegex += entry["excludeRegex"]
|
||||
action = baseAction | {
|
||||
"output": convertModuleToPath(entry["name"]) + "/src/main/res/values/localazy.xml",
|
||||
"includeKeys": list(map(lambda i: "REGEX:" + i, entry["includeRegex"])),
|
||||
"excludeKeys": list(map(lambda i: "REGEX:" + i, regexToAlwaysExclude)),
|
||||
"excludeKeys": list(map(lambda i: "REGEX:" + i, excludeRegex)),
|
||||
"conditions": [
|
||||
"equals: ${languageCode}, en"
|
||||
]
|
||||
|
|
@ -51,7 +54,7 @@ for entry in config["modules"]:
|
|||
actionTranslation = baseAction | {
|
||||
"output": convertModuleToPath(entry["name"]) + "/src/main/res/values-${langAndroidResNoScript}/translations.xml",
|
||||
"includeKeys": list(map(lambda i: "REGEX:" + i, entry["includeRegex"])),
|
||||
"excludeKeys": list(map(lambda i: "REGEX:" + i, regexToAlwaysExclude)),
|
||||
"excludeKeys": list(map(lambda i: "REGEX:" + i, excludeRegex)),
|
||||
"conditions": [
|
||||
"!equals: ${languageCode}, en"
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue