From c77066162a0dd29a198ab2aeb6276fd5bc2278c7 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 6 Oct 2026 16:25:51 +0200 Subject: [PATCH] android: show group icons and open properties from chat header --- .../utun/chat/ui/screens/ChannelListScreen.kt | 42 ++++--------------- .../com/utun/chat/ui/screens/ChatScreen.kt | 29 ++++++++++--- .../utun/chat/ui/screens/SettingsScreen.kt | 3 ++ 3 files changed, 34 insertions(+), 40 deletions(-) diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChannelListScreen.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChannelListScreen.kt index db77f306..57bb982b 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChannelListScreen.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChannelListScreen.kt @@ -18,6 +18,9 @@ import androidx.compose.material.icons.filled.Build import androidx.compose.material3.* import androidx.compose.runtime.* import com.utun.chat.ui.components.PeerStatusDot +import com.utun.chat.ui.components.GroupAvatar +import com.utun.chat.ui.components.UserAvatar +import com.utun.chat.ui.components.GroupPropertiesDialog import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -115,7 +118,7 @@ fun ChannelListScreen( Row(Modifier.fillMaxWidth().padding(12.dp)) { Box(Modifier.size(44.dp), contentAlignment = Alignment.Center) { IconButton(onClick = { menuChannelId = ch.id }, modifier = Modifier.size(44.dp)) { - Text("#", fontSize = 22.sp) + GroupAvatar(ch.id, ch.name) } DropdownMenu( expanded = menuChannelId == ch.id, @@ -126,7 +129,7 @@ fun ChannelListScreen( onClick = { menuChannelId = null; onInviteClick(ch) } ) DropdownMenuItem( - text = { Text(stringResource(R.string.options)) }, + text = { Text(stringResource(R.string.group_properties)) }, onClick = { menuChannelId = null; optionsChannel = ch } ) DropdownMenuItem( @@ -183,7 +186,7 @@ fun ChannelListScreen( Box(Modifier.width(4.dp).fillMaxHeight().background(Color(0xFFD2A8FF))) Row(Modifier.fillMaxWidth().padding(12.dp)) { Box(Modifier.size(44.dp), contentAlignment = Alignment.Center) { - Text("@", fontSize = 22.sp, color = Color(0xFFD2A8FF)) + UserAvatar(null, dm.peerNodeId, dm.displayName, size = 44.dp) PeerStatusDot(dm.online, Modifier.align(Alignment.TopStart).offset(x = (-4).dp, y = (-6).dp)) } Spacer(Modifier.width(10.dp)) @@ -212,7 +215,7 @@ fun ChannelListScreen( } optionsChannel?.let { ch -> - ChannelOptionsDialog( + GroupPropertiesDialog( channel = ch, onDismiss = { optionsChannel = null } ) @@ -236,37 +239,6 @@ fun ChannelListScreen( } } -@Composable -fun ChannelOptionsDialog(channel: Channel, onDismiss: () -> Unit) { - val scope = rememberCoroutineScope() - var soundOn by remember(channel.id) { mutableStateOf(!NotificationSound.isMuted(channel.id)) } - - AlertDialog( - onDismissRequest = onDismiss, - title = { Text(channel.name) }, - text = { - Column { - Row(verticalAlignment = Alignment.CenterVertically) { - Text(stringResource(R.string.sound), modifier = Modifier.weight(1f)) - Switch( - checked = soundOn, - onCheckedChange = { - soundOn = it - NotificationSound.setMuted(channel.id, !it) - scope.launch { - ChatApplication.instance.configProvider.setChannelMuted(channel.id, !it) - } - } - ) - } - } - }, - confirmButton = { - TextButton(onClick = onDismiss) { Text(stringResource(R.string.close)) } - } - ) -} - @Composable fun CreateChannelDialog(onDismiss: () -> Unit, onCreate: (String) -> Unit) { var name by remember { mutableStateOf("") } diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChatScreen.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChatScreen.kt index 9ab074c1..9084c0c2 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChatScreen.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChatScreen.kt @@ -23,6 +23,8 @@ import androidx.compose.material.icons.filled.HeadsetMic import androidx.compose.material.icons.automirrored.filled.VolumeUp import androidx.compose.material.icons.filled.Link import androidx.compose.material.icons.filled.People +import androidx.compose.material.icons.filled.Settings +import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -51,6 +53,8 @@ import com.utun.chat.data.RadioOverlayService import com.utun.chat.ui.components.ConversationMessages import com.utun.chat.ui.components.ConversationComposer import com.utun.chat.ui.components.PortableRadioIcon +import com.utun.chat.ui.components.GroupAvatar +import com.utun.chat.ui.components.GroupPropertiesDialog import com.utun.chat.data.ConversationTarget import com.utun.chat.viewmodel.ChatViewModel import kotlinx.coroutines.Dispatchers @@ -77,6 +81,8 @@ fun ChatScreen( val view = LocalView.current val pttCenterOffset = with(LocalDensity.current) { 60.dp.toPx() } val scope = rememberCoroutineScope() + var properties by remember(channel.id) { mutableStateOf(false) } + var menu by remember(channel.id) { mutableStateOf(false) } /* рация */ val radioActive by viewModel.radioActive.collectAsState() @@ -114,14 +120,16 @@ fun ChatScreen( modifier = Modifier.fillMaxSize().imePadding() ) { TopAppBar( - title = { Text(channel.name) }, + title = { + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) { + GroupAvatar(channel.id, channel.name, size = 32.dp) + Text(channel.name, maxLines = 1, overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis) + } + }, navigationIcon = { IconButton(onClick = onBack) { Icon(Icons.AutoMirrored.Filled.ArrowBack, stringResource(R.string.back)) } }, actions = { - IconButton(onClick = { viewModel.toggleSound() }) { - Text(if (soundOn) "\uD83D\uDD14" else "\uD83D\uDD15", fontSize = 18.sp) - } IconButton( onClick = { viewModel.toggleRadio() }, modifier = Modifier.background( @@ -130,10 +138,21 @@ fun ChatScreen( val action = stringResource(if (radioActive) R.string.radio_turn_off else R.string.radio_turn_on) PortableRadioIcon(stringResource(R.string.radio_toggle, action)) } - IconButton(onClick = onInvite) { Icon(Icons.Filled.Link, stringResource(R.string.invite)) } + IconButton(onClick = { properties = true }) { Icon(Icons.Filled.Settings, stringResource(R.string.group_properties)) } IconButton(onClick = onMembersClick) { Icon(Icons.Filled.People, stringResource(R.string.members)) } + Box { + IconButton(onClick = { menu = true }) { Icon(Icons.Filled.MoreVert, stringResource(R.string.options)) } + DropdownMenu(expanded = menu, onDismissRequest = { menu = false }) { + DropdownMenuItem(text = { Text(stringResource(R.string.invite)) }, + leadingIcon = { Icon(Icons.Filled.Link, null) }, onClick = { menu = false; onInvite() }) + DropdownMenuItem(text = { Text(stringResource(R.string.sound)) }, + trailingIcon = { Text(if (soundOn) "\uD83D\uDD14" else "\uD83D\uDD15") }, + onClick = { menu = false; viewModel.toggleSound() }) + } + } } ) + if (properties) GroupPropertiesDialog(channel, onDismiss = { properties = false }) if (radioActive) { RadioSourceBar(viewModel) } diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/SettingsScreen.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/SettingsScreen.kt index aa821561..af9d953d 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/SettingsScreen.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/SettingsScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import com.utun.chat.ChatApplication import com.utun.chat.data.NativeLib +import com.utun.chat.ui.components.ProfileAvatarControls import com.utun.chat.data.NotificationSound import com.utun.chat.data.IncomingCallManager import com.utun.chat.data.RadioOverlayService @@ -255,6 +256,8 @@ private fun GeneralSettingsTab(vm: ChatViewModel, dirty: Boolean, onDirty: (Bool } } + item(key = "profile-avatar") { ProfileAvatarControls() } + /* Keys */ item(key = "keys-title") { Text(stringResource(R.string.keys), style = MaterialTheme.typography.titleMedium) } item(key = "public-key") { OutlinedTextField(pubKey, {}, label = { Text(stringResource(R.string.public_key)) }, readOnly = true, singleLine = true) }