Merge branch 'develop' into dla/feature/custom_room_notification_settings_list

This commit is contained in:
David Langley 2023-10-24 22:18:38 +01:00 committed by GitHub
commit 6fa73963c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 173 additions and 10 deletions

View file

@ -65,10 +65,11 @@ import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.textcomposer.components.ComposerOptionsButton
import io.element.android.libraries.textcomposer.components.DismissTextFormattingButton
import io.element.android.libraries.textcomposer.components.RecordButton
import io.element.android.libraries.textcomposer.components.VoiceMessagePreview
import io.element.android.libraries.textcomposer.components.VoiceMessageRecording
import io.element.android.libraries.textcomposer.components.SendButton
import io.element.android.libraries.textcomposer.components.TextFormatting
import io.element.android.libraries.textcomposer.components.VoiceMessageDeleteButton
import io.element.android.libraries.textcomposer.components.VoiceMessagePreview
import io.element.android.libraries.textcomposer.components.VoiceMessageRecording
import io.element.android.libraries.textcomposer.components.textInputRoundedCornerShape
import io.element.android.libraries.textcomposer.model.Message
import io.element.android.libraries.textcomposer.model.MessageComposerMode
@ -99,6 +100,7 @@ fun TextComposer(
onDismissTextFormatting: () -> Unit = {},
onVoiceRecordButtonEvent: (PressEvent) -> Unit = {},
onSendVoiceMessage: () -> Unit = {},
onDeleteVoiceMessage: () -> Unit = {},
onError: (Throwable) -> Unit = {},
) {
val onSendClicked = {
@ -176,7 +178,7 @@ fun TextComposer(
}
val voiceRecording = @Composable {
when(voiceMessageState) {
when (voiceMessageState) {
VoiceMessageState.Preview ->
VoiceMessagePreview(isInteractive = true)
VoiceMessageState.Sending ->
@ -187,6 +189,16 @@ fun TextComposer(
}
}
val voiceDeleteButton = @Composable {
val enabled = when (voiceMessageState) {
VoiceMessageState.Preview -> true
VoiceMessageState.Sending,
is VoiceMessageState.Recording,
VoiceMessageState.Idle -> false
}
VoiceMessageDeleteButton(enabled = enabled, onClick = onDeleteVoiceMessage)
}
if (showTextFormatting) {
TextFormattingLayout(
modifier = layoutModifier,
@ -206,6 +218,7 @@ fun TextComposer(
textInput = textInput,
endButton = sendOrRecordButton,
voiceRecording = voiceRecording,
voiceDeleteButton = voiceDeleteButton,
)
}
@ -225,6 +238,7 @@ private fun StandardLayout(
textInput: @Composable () -> Unit,
composerOptionsButton: @Composable () -> Unit,
voiceRecording: @Composable () -> Unit,
voiceDeleteButton: @Composable () -> Unit,
endButton: @Composable () -> Unit,
modifier: Modifier = Modifier,
) {
@ -233,9 +247,21 @@ private fun StandardLayout(
verticalAlignment = Alignment.Bottom,
) {
if (enableVoiceMessages && voiceMessageState !is VoiceMessageState.Idle) {
if (voiceMessageState is VoiceMessageState.Preview || voiceMessageState is VoiceMessageState.Sending) {
Box(
modifier = Modifier
.padding(bottom = 5.dp, top = 5.dp, end = 3.dp, start = 3.dp)
.size(48.dp.applyScaleUp()),
contentAlignment = Alignment.Center,
) {
voiceDeleteButton()
}
} else {
Spacer(modifier = Modifier.width(16.dp))
}
Box(
modifier = Modifier
.padding(start = 16.dp, bottom = 8.dp, top = 8.dp)
.padding(bottom = 8.dp, top = 8.dp)
.weight(1f)
) {
voiceRecording()

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.textcomposer.components
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.applyScaleUp
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun VoiceMessageDeleteButton(
enabled: Boolean,
modifier: Modifier = Modifier,
onClick: () -> Unit = {},
) {
IconButton(
modifier = modifier
.size(48.dp),
enabled = enabled,
onClick = onClick,
) {
Icon(
modifier = Modifier.size(24.dp.applyScaleUp()),
resourceId = CommonDrawables.ic_compound_delete,
contentDescription = stringResource(CommonStrings.a11y_delete),
tint = if (enabled) {
ElementTheme.colors.iconCriticalPrimary
} else {
ElementTheme.colors.iconDisabled
},
)
}
}
@PreviewsDayNight
@Composable
internal fun VoiceMessageDeleteButtonPreview() = ElementPreview {
Row {
VoiceMessageDeleteButton(enabled = true)
VoiceMessageDeleteButton(enabled = false)
}
}

View file

@ -27,4 +27,5 @@ dependencies {
implementation(projects.tests.testutils)
implementation(libs.coroutines.test)
implementation(libs.test.truth)
}

View file

@ -16,6 +16,7 @@
package io.element.android.libraries.voicerecorder.test
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.voicerecorder.api.VoiceRecorder
import io.element.android.libraries.voicerecorder.api.VoiceRecorderState
import kotlinx.coroutines.flow.MutableStateFlow
@ -37,7 +38,12 @@ class FakeVoiceRecorder(
private var securityException: SecurityException? = null
private var startedCount = 0
private var stoppedCount = 0
private var deletedCount = 0
override suspend fun startRecord() {
startedCount += 1
val startedAt = timeSource.markNow()
securityException?.let { throw it }
@ -55,6 +61,8 @@ class FakeVoiceRecorder(
override suspend fun stopRecord(
cancelled: Boolean
) {
stoppedCount++
if (cancelled) {
deleteRecording()
}
@ -68,6 +76,7 @@ class FakeVoiceRecorder(
}
override suspend fun deleteRecording() {
deletedCount++
curRecording = null
_state.emit(
@ -75,6 +84,17 @@ class FakeVoiceRecorder(
)
}
fun assertCalls(
started: Int = 0,
stopped: Int = 0,
deleted: Int = 0,
) {
assertThat(startedCount).isEqualTo(started)
assertThat(stoppedCount).isEqualTo(stopped)
assertThat(deletedCount).isEqualTo(deleted)
}
fun givenThrowsSecurityException(exception: SecurityException) {
this.securityException = exception
}