mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-08-29 07:16:08 +00:00
wear: rename from People screen - 'you' row first opens nickname editor; nickname field auto-focuses with cursor at end, IME Done only closes keyboard for review, confirm button is the single commit path, trim while typing
This commit is contained in:
parent
86a04bc8a2
commit
c1e6204686
@ -54,6 +54,7 @@ import com.bitchat.watch.ui.theme.BitchatWearTheme
|
||||
sealed interface WearScreen {
|
||||
data object Chat : WearScreen
|
||||
data object People : WearScreen
|
||||
data object Nickname : WearScreen
|
||||
data class Dm(val peerID: String) : WearScreen
|
||||
data class TextInput(val peerID: String?) : WearScreen
|
||||
}
|
||||
@ -209,7 +210,23 @@ fun WearNavHost(openDmPeer: String?, onOpenDmHandled: () -> Unit) {
|
||||
onOpenPeople = { navigate(WearScreen.People) },
|
||||
onOpenTextInput = { navigate(WearScreen.TextInput(null)) }
|
||||
)
|
||||
is WearScreen.People -> PeopleScreen(onOpenDm = { navigate(WearScreen.Dm(it)) })
|
||||
is WearScreen.People -> PeopleScreen(
|
||||
onOpenDm = { navigate(WearScreen.Dm(it)) },
|
||||
onEditNickname = { navigate(WearScreen.Nickname) }
|
||||
)
|
||||
is WearScreen.Nickname -> {
|
||||
val mesh = WearMeshService.peek()
|
||||
NicknameSetupScreen(
|
||||
initialNickname = mesh?.nickname ?: "",
|
||||
title = "you",
|
||||
subtitle = "this is how nearby peers see you",
|
||||
confirmLabel = "save",
|
||||
onConfirm = { name ->
|
||||
mesh?.setNickname(name)
|
||||
goBack()
|
||||
}
|
||||
)
|
||||
}
|
||||
is WearScreen.Dm -> DmScreen(
|
||||
peerID = current.peerID,
|
||||
onOpenTextInput = { navigate(WearScreen.TextInput(current.peerID)) }
|
||||
|
||||
@ -12,6 +12,7 @@ import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@ -19,9 +20,14 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.text.TextRange
|
||||
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.unit.dp
|
||||
import androidx.wear.compose.material3.Button
|
||||
@ -30,13 +36,33 @@ import androidx.wear.compose.material3.Text
|
||||
import com.bitchat.watch.ui.theme.ChatVisualTokens
|
||||
import com.bitchat.watch.ui.theme.LocalBitchatPalette
|
||||
|
||||
/**
|
||||
* Nickname entry, used both for first-run onboarding and for renaming later. The IME's
|
||||
* Done action only closes the keyboard so the user can review the name; the confirm
|
||||
* button is the single commit path.
|
||||
*/
|
||||
@Composable
|
||||
fun NicknameSetupScreen(
|
||||
initialNickname: String,
|
||||
title: String = "bitchat",
|
||||
subtitle: String = "pick a nickname",
|
||||
confirmLabel: String = "join the mesh",
|
||||
onConfirm: (String) -> Unit
|
||||
) {
|
||||
val palette = LocalBitchatPalette.current
|
||||
var name by remember { mutableStateOf(initialNickname) }
|
||||
// Pre-fill with the cursor at the end of the existing name, not the start.
|
||||
var name by remember {
|
||||
mutableStateOf(
|
||||
TextFieldValue(
|
||||
text = initialNickname,
|
||||
selection = TextRange(initialNickname.length)
|
||||
)
|
||||
)
|
||||
}
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@ -46,13 +72,13 @@ fun NicknameSetupScreen(
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
text = "bitchat",
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Text(
|
||||
text = "pick a nickname",
|
||||
text = subtitle,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = palette.textTertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
@ -60,7 +86,14 @@ fun NicknameSetupScreen(
|
||||
)
|
||||
BasicTextField(
|
||||
value = name,
|
||||
onValueChange = { name = it.take(24) },
|
||||
onValueChange = { newValue ->
|
||||
val trimmed = newValue.text.trim().take(24)
|
||||
name = if (trimmed == newValue.text) {
|
||||
newValue
|
||||
} else {
|
||||
newValue.copy(text = trimmed, selection = TextRange(trimmed.length))
|
||||
}
|
||||
},
|
||||
singleLine = true,
|
||||
textStyle = ChatVisualTokens.MessageBodyStyle.copy(
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
@ -69,16 +102,17 @@ fun NicknameSetupScreen(
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {
|
||||
if (name.isNotBlank()) onConfirm(name.trim())
|
||||
keyboardController?.hide()
|
||||
}),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(focusRequester)
|
||||
.clip(RoundedCornerShape(18.dp))
|
||||
.background(palette.inputSurface)
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
decorationBox = { innerTextField ->
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
if (name.isEmpty()) {
|
||||
if (name.text.isEmpty()) {
|
||||
Text(
|
||||
text = "nickname",
|
||||
style = ChatVisualTokens.MessageBodyStyle,
|
||||
@ -90,11 +124,11 @@ fun NicknameSetupScreen(
|
||||
}
|
||||
)
|
||||
Button(
|
||||
onClick = { if (name.isNotBlank()) onConfirm(name.trim()) },
|
||||
enabled = name.isNotBlank(),
|
||||
onClick = { if (name.text.isNotBlank()) onConfirm(name.text.trim()) },
|
||||
enabled = name.text.isNotBlank(),
|
||||
modifier = Modifier.padding(top = 10.dp)
|
||||
) {
|
||||
Text("join the mesh")
|
||||
Text(confirmLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ import com.bitchat.watch.ui.theme.LocalBitchatPalette
|
||||
import com.bitchat.watch.ui.theme.colorForPeer
|
||||
|
||||
@Composable
|
||||
fun PeopleScreen(onOpenDm: (String) -> Unit) {
|
||||
fun PeopleScreen(onOpenDm: (String) -> Unit, onEditNickname: () -> Unit) {
|
||||
val peers by AppStateStore.peers.collectAsState()
|
||||
val unread by WearChatState.unreadDms.collectAsState()
|
||||
val mesh = WearMeshService.peek()
|
||||
@ -77,6 +77,12 @@ fun PeopleScreen(onOpenDm: (String) -> Unit) {
|
||||
)
|
||||
}
|
||||
}
|
||||
item(key = "self") {
|
||||
SelfRow(
|
||||
nickname = mesh?.nickname ?: "me",
|
||||
onClick = onEditNickname
|
||||
)
|
||||
}
|
||||
items(sortedPeers, key = { it }) { peerID ->
|
||||
val nick = nicknames[peerID] ?: peerID.take(8)
|
||||
PersonRow(
|
||||
@ -91,6 +97,40 @@ fun PeopleScreen(onOpenDm: (String) -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SelfRow(nickname: String, onClick: () -> Unit) {
|
||||
val palette = LocalBitchatPalette.current
|
||||
Card(
|
||||
onClick = onClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 10.dp, vertical = 2.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = nickname,
|
||||
style = ChatVisualTokens.SenderStyle,
|
||||
color = palette.accentOrange,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f, fill = false)
|
||||
)
|
||||
Text(
|
||||
text = " (you)",
|
||||
style = ChatVisualTokens.SenderStyle,
|
||||
color = palette.textTertiary
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = "tap to rename",
|
||||
style = ChatVisualTokens.SystemActionStyle,
|
||||
color = palette.textTertiary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PersonRow(
|
||||
nickname: String,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user