Revert "Resolve merge conflicts:"

This reverts commit 3384cce51d, reversing
changes made to 69d18aae3c.
This commit is contained in:
callebtc
2025-09-21 08:17:04 +02:00
parent 3384cce51d
commit 14475fe7f1
95 changed files with 1669 additions and 7993 deletions
@@ -1,6 +1,4 @@
package com.bitchat.android.ui
// [Goose] TODO: Replace inline file attachment stub with FilePickerButton abstraction that dispatches via FileShareDispatcher
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
@@ -18,6 +16,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
@@ -25,6 +24,7 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.input.OffsetMapping
import androidx.compose.ui.text.input.TransformedText
import androidx.compose.ui.text.input.VisualTransformation
@@ -33,16 +33,9 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.bitchat.android.R
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.withStyle
import com.bitchat.android.ui.theme.BASE_FONT_SIZE
import com.bitchat.android.features.voice.normalizeAmplitudeSample
import com.bitchat.android.features.voice.AudioWaveformExtractor
import com.bitchat.android.ui.media.RealtimeScrollingWaveform
import com.bitchat.android.ui.media.ImagePickerButton
import com.bitchat.android.ui.media.FilePickerButton
import androidx.compose.foundation.isSystemInDarkTheme
/**
* Input components for ChatScreen
@@ -164,9 +157,6 @@ fun MessageInput(
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
onSend: () -> Unit,
onSendVoiceNote: (String?, String?, String) -> Unit,
onSendImageNote: (String?, String?, String) -> Unit,
onSendFileNote: (String?, String?, String) -> Unit,
selectedPrivatePeer: String?,
currentChannel: String?,
nickname: String,
@@ -175,22 +165,16 @@ fun MessageInput(
val colorScheme = MaterialTheme.colorScheme
val isFocused = remember { mutableStateOf(false) }
val hasText = value.text.isNotBlank() // Check if there's text for send button state
val keyboard = LocalSoftwareKeyboardController.current
val focusRequester = remember { FocusRequester() }
var isRecording by remember { mutableStateOf(false) }
var elapsedMs by remember { mutableStateOf(0L) }
var amplitude by remember { mutableStateOf(0) }
Row(
modifier = modifier.padding(horizontal = 12.dp, vertical = 8.dp), // Reduced padding
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
// Text input with placeholder OR visualizer when recording
// Text input with placeholder
Box(
modifier = Modifier.weight(1f)
) {
// Always keep the text field mounted to retain focus and avoid IME collapse
BasicTextField(
value = value,
onValueChange = onValueChange,
@@ -198,7 +182,7 @@ fun MessageInput(
color = colorScheme.primary,
fontFamily = FontFamily.Monospace
),
cursorBrush = SolidColor(if (isRecording) Color.Transparent else colorScheme.primary),
cursorBrush = SolidColor(colorScheme.primary),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Send),
keyboardActions = KeyboardActions(onSend = {
if (hasText) onSend() // Only send if there's text
@@ -208,14 +192,13 @@ fun MessageInput(
),
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
.onFocusChanged { focusState ->
isFocused.value = focusState.isFocused
}
)
// Show placeholder when there's no text and not recording
if (value.text.isEmpty() && !isRecording) {
// Show placeholder when there's no text
if (value.text.isEmpty()) {
Text(
text = "type a message...",
style = MaterialTheme.typography.bodyMedium.copy(
@@ -225,94 +208,23 @@ fun MessageInput(
modifier = Modifier.fillMaxWidth()
)
}
// Overlay the real-time scrolling waveform while recording
if (isRecording) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
RealtimeScrollingWaveform(
modifier = Modifier.weight(1f).height(32.dp),
amplitudeNorm = normalizeAmplitudeSample(amplitude)
)
Spacer(Modifier.width(20.dp))
val secs = (elapsedMs / 1000).toInt()
val mm = secs / 60
val ss = secs % 60
val maxSecs = 10 // 10 second max recording time
val maxMm = maxSecs / 60
val maxSs = maxSecs % 60
Text(
text = String.format("%02d:%02d / %02d:%02d", mm, ss, maxMm, maxSs),
fontFamily = FontFamily.Monospace,
color = colorScheme.primary,
fontSize = (BASE_FONT_SIZE - 4).sp
)
}
}
}
Spacer(modifier = Modifier.width(8.dp)) // Reduced spacing
// Voice and image buttons when no text (always visible for mesh + channels + private)
// Command quick access button
if (value.text.isEmpty()) {
// Hold-to-record microphone
val bg = if (colorScheme.background == Color.Black) Color(0xFF00FF00).copy(alpha = 0.75f) else Color(0xFF008000).copy(alpha = 0.75f)
// Ensure latest values are used when finishing recording
val latestSelectedPeer = rememberUpdatedState(selectedPrivatePeer)
val latestChannel = rememberUpdatedState(currentChannel)
val latestOnSendVoiceNote = rememberUpdatedState(onSendVoiceNote)
// Image button (image picker) - hide during recording
if (!isRecording) {
// Revert to original separate buttons: round File button (left) and the old Image plus button (right)
Row(horizontalArrangement = Arrangement.spacedBy(6.dp), verticalAlignment = Alignment.CenterVertically) {
// DISABLE FILE PICKER
//FilePickerButton(
// onFileReady = { path ->
// onSendFileNote(latestSelectedPeer.value, latestChannel.value, path)
// }
//)
ImagePickerButton(
onImageReady = { outPath ->
onSendImageNote(latestSelectedPeer.value, latestChannel.value, outPath)
}
)
}
FilledTonalIconButton(
onClick = {
onValueChange(TextFieldValue(text = "/", selection = TextRange("/".length)))
},
modifier = Modifier.size(32.dp)
) {
Text(
text = "/",
textAlign = TextAlign.Center
)
}
Spacer(Modifier.width(1.dp))
VoiceRecordButton(
backgroundColor = bg,
onStart = {
isRecording = true
elapsedMs = 0L
// Keep existing focus to avoid IME collapse, but do not force-show keyboard
if (isFocused.value) {
try { focusRequester.requestFocus() } catch (_: Exception) {}
}
},
onAmplitude = { amp, ms ->
amplitude = amp
elapsedMs = ms
},
onFinish = { path ->
isRecording = false
// Extract and cache waveform from the actual audio file to match receiver rendering
AudioWaveformExtractor.extractAsync(path, sampleCount = 120) { arr ->
if (arr != null) {
try { com.bitchat.android.features.voice.VoiceWaveformCache.put(path, arr) } catch (_: Exception) {}
}
}
// BLE path (private or public) — use latest values to avoid stale captures
latestOnSendVoiceNote.value(
latestSelectedPeer.value,
latestChannel.value,
path
)
}
)
} else {
// Send button with enabled/disabled state
IconButton(
@@ -360,8 +272,6 @@ fun MessageInput(
}
}
}
// Auto-stop handled inside VoiceRecordButton
}
@Composable