diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/RecordingIndicators.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/RecordingIndicators.kt index 9b81d710fa..a4e3ecd650 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/RecordingIndicators.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/RecordingIndicators.kt @@ -175,18 +175,27 @@ fun FloatingRecordingIndicator( modifier: Modifier = Modifier, isRecording: Boolean, elapsedSeconds: Int, + isCompact: Boolean = false, ) { if (!isRecording) return val recordingLabel = stringRes(id = R.string.recording_indicator_description) - val recordingWithTime = stringRes(id = R.string.recording_indicator_with_time, formatSecondsToTime(elapsedSeconds)) + val recordingWithTime = + if (isCompact) { + formatSecondsToTime(elapsedSeconds) + } else { + stringRes(id = R.string.recording_indicator_with_time, formatSecondsToTime(elapsedSeconds)) + } + val horizontalPadding = if (isCompact) 8.dp else 16.dp + val innerPadding = if (isCompact) 6.dp else 12.dp + val textSize = if (isCompact) 12.sp else 14.sp Box( modifier = modifier .fillMaxWidth() .height(48.dp) - .padding(horizontal = 16.dp) + .padding(horizontal = horizontalPadding) .background( color = MaterialTheme.colorScheme.primary, shape = RoundedCornerShape(12.dp), @@ -195,7 +204,7 @@ fun FloatingRecordingIndicator( ) { Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(horizontal = 12.dp), + modifier = Modifier.padding(horizontal = innerPadding), ) { // Pulsing red dot val infiniteTransition = rememberInfiniteTransition(label = "recording_dot") @@ -223,8 +232,10 @@ fun FloatingRecordingIndicator( Text( text = recordingWithTime, color = Color.White, - fontSize = 14.sp, + fontSize = textSize, fontWeight = FontWeight.Medium, + maxLines = 1, + softWrap = false, ) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt index 7a1e53541a..279a3414f3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt @@ -47,6 +47,8 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults @@ -61,6 +63,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -109,6 +112,7 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNo import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteZaps import com.vitorpamplona.amethyst.service.relayClient.reqCommand.nwc.NWCFinderFilterAssemblerSubscription import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled +import com.vitorpamplona.amethyst.ui.actions.uploads.FloatingRecordingIndicator import com.vitorpamplona.amethyst.ui.actions.uploads.RecordAudioBox import com.vitorpamplona.amethyst.ui.components.AnimatedBorderTextCornerRadius import com.vitorpamplona.amethyst.ui.components.ClickableBox @@ -205,9 +209,12 @@ private fun InnerReactionRow( accountViewModel: AccountViewModel, nav: INav, ) { + val voiceRecordingState = remember(baseNote.idHex) { mutableStateOf(false) } + GenericInnerReactionRow( showReactionDetail = showReactionDetail, addPadding = addPadding, + weightTwo = if (voiceRecordingState.value) 2f else 1f, one = { WatchReactionsZapsBoostsAndDisplayIfExists(baseNote, accountViewModel) { RenderShowIndividualReactionsButton(wantsToSeeReactions, accountViewModel) @@ -219,6 +226,7 @@ private fun InnerReactionRow( MaterialTheme.colorScheme.placeholderText, accountViewModel, nav, + voiceRecordingState = voiceRecordingState, ) }, three = { @@ -289,6 +297,7 @@ fun ShareReaction( private fun GenericInnerReactionRow( showReactionDetail: Boolean, addPadding: Boolean, + weightTwo: Float = 1f, one: @Composable () -> Unit, two: @Composable () -> Unit, three: @Composable () -> Unit, @@ -310,7 +319,7 @@ private fun GenericInnerReactionRow( } } - Row(verticalAlignment = CenterVertically, horizontalArrangement = RowColSpacing, modifier = Modifier.weight(1f)) { two() } + Row(verticalAlignment = CenterVertically, horizontalArrangement = RowColSpacing, modifier = Modifier.weight(weightTwo)) { two() } Row(verticalAlignment = CenterVertically, horizontalArrangement = RowColSpacing, modifier = Modifier.weight(1f)) { three() } @@ -584,9 +593,16 @@ private fun ReplyReactionWithDialog( grayTint: Color, accountViewModel: AccountViewModel, nav: INav, + voiceRecordingState: MutableState? = null, ) { if (baseNote.event is BaseVoiceEvent) { - ReplyViaVoiceReaction(baseNote, grayTint, accountViewModel, nav) + ReplyViaVoiceReaction( + baseNote, + grayTint, + accountViewModel, + nav, + voiceRecordingState = voiceRecordingState, + ) } else { ReplyReaction(baseNote, grayTint, accountViewModel) { nav.nav { routeReplyTo(baseNote, accountViewModel.account) } @@ -603,9 +619,10 @@ fun ReplyViaVoiceReaction( nav: INav, showCounter: Boolean = true, iconSizeModifier: Modifier = Size19Modifier, + voiceRecordingState: MutableState? = null, ) { RecordAudioBox( - modifier = iconSizeModifier, + modifier = Modifier, onRecordTaken = { audio -> nav.nav { Route.VoiceReply( @@ -617,8 +634,27 @@ fun ReplyViaVoiceReaction( ) } }, - ) { _, _ -> - VoiceReplyIcon(iconSizeModifier, grayTint) + ) { isRecording, elapsedSeconds -> + if (voiceRecordingState != null) { + SideEffect { + if (voiceRecordingState.value != isRecording) { + voiceRecordingState.value = isRecording + } + } + } + if (isRecording) { + FloatingRecordingIndicator( + modifier = + Modifier + .fillMaxWidth() + .height(40.dp), + isRecording = true, + elapsedSeconds = elapsedSeconds, + isCompact = true, + ) + } else { + VoiceReplyIcon(iconSizeModifier, grayTint) + } } if (showCounter) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt index 1fcd3daab4..05bf255fcf 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt @@ -190,8 +190,8 @@ val VideoReactionColumnPadding = Modifier.padding(bottom = 75.dp) val DividerThickness = 0.25.dp -val ReactionRowHeight = Modifier.padding(vertical = 7.dp).height(24.dp) -val ReactionRowHeightWithPadding = Modifier.padding(vertical = 6.dp).height(24.dp).padding(horizontal = 10.dp) +val ReactionRowHeight = Modifier.padding(vertical = 7.dp).heightIn(min = 24.dp) +val ReactionRowHeightWithPadding = Modifier.padding(vertical = 6.dp).heightIn(min = 24.dp).padding(horizontal = 10.dp) val ReactionRowHeightChat = Modifier.height(20.dp) val ReactionRowHeightChatMaxWidth = Modifier.height(25.dp).fillMaxWidth() val UserNameRowHeight = Modifier.fillMaxWidth()