Browse Source

android: show group icons and open properties from chat header

master
evgeny 7 days ago
parent
commit
c77066162a
  1. 42
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChannelListScreen.kt
  2. 29
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChatScreen.kt
  3. 3
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/SettingsScreen.kt

42
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.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import com.utun.chat.ui.components.PeerStatusDot 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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
@ -115,7 +118,7 @@ fun ChannelListScreen(
Row(Modifier.fillMaxWidth().padding(12.dp)) { Row(Modifier.fillMaxWidth().padding(12.dp)) {
Box(Modifier.size(44.dp), contentAlignment = Alignment.Center) { Box(Modifier.size(44.dp), contentAlignment = Alignment.Center) {
IconButton(onClick = { menuChannelId = ch.id }, modifier = Modifier.size(44.dp)) { IconButton(onClick = { menuChannelId = ch.id }, modifier = Modifier.size(44.dp)) {
Text("#", fontSize = 22.sp) GroupAvatar(ch.id, ch.name)
} }
DropdownMenu( DropdownMenu(
expanded = menuChannelId == ch.id, expanded = menuChannelId == ch.id,
@ -126,7 +129,7 @@ fun ChannelListScreen(
onClick = { menuChannelId = null; onInviteClick(ch) } onClick = { menuChannelId = null; onInviteClick(ch) }
) )
DropdownMenuItem( DropdownMenuItem(
text = { Text(stringResource(R.string.options)) }, text = { Text(stringResource(R.string.group_properties)) },
onClick = { menuChannelId = null; optionsChannel = ch } onClick = { menuChannelId = null; optionsChannel = ch }
) )
DropdownMenuItem( DropdownMenuItem(
@ -183,7 +186,7 @@ fun ChannelListScreen(
Box(Modifier.width(4.dp).fillMaxHeight().background(Color(0xFFD2A8FF))) Box(Modifier.width(4.dp).fillMaxHeight().background(Color(0xFFD2A8FF)))
Row(Modifier.fillMaxWidth().padding(12.dp)) { Row(Modifier.fillMaxWidth().padding(12.dp)) {
Box(Modifier.size(44.dp), contentAlignment = Alignment.Center) { 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)) PeerStatusDot(dm.online, Modifier.align(Alignment.TopStart).offset(x = (-4).dp, y = (-6).dp))
} }
Spacer(Modifier.width(10.dp)) Spacer(Modifier.width(10.dp))
@ -212,7 +215,7 @@ fun ChannelListScreen(
} }
optionsChannel?.let { ch -> optionsChannel?.let { ch ->
ChannelOptionsDialog( GroupPropertiesDialog(
channel = ch, channel = ch,
onDismiss = { optionsChannel = null } 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 @Composable
fun CreateChannelDialog(onDismiss: () -> Unit, onCreate: (String) -> Unit) { fun CreateChannelDialog(onDismiss: () -> Unit, onCreate: (String) -> Unit) {
var name by remember { mutableStateOf("") } var name by remember { mutableStateOf("") }

29
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.automirrored.filled.VolumeUp
import androidx.compose.material.icons.filled.Link import androidx.compose.material.icons.filled.Link
import androidx.compose.material.icons.filled.People 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.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment 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.ConversationMessages
import com.utun.chat.ui.components.ConversationComposer import com.utun.chat.ui.components.ConversationComposer
import com.utun.chat.ui.components.PortableRadioIcon 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.data.ConversationTarget
import com.utun.chat.viewmodel.ChatViewModel import com.utun.chat.viewmodel.ChatViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -77,6 +81,8 @@ fun ChatScreen(
val view = LocalView.current val view = LocalView.current
val pttCenterOffset = with(LocalDensity.current) { 60.dp.toPx() } val pttCenterOffset = with(LocalDensity.current) { 60.dp.toPx() }
val scope = rememberCoroutineScope() 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() val radioActive by viewModel.radioActive.collectAsState()
@ -114,14 +120,16 @@ fun ChatScreen(
modifier = Modifier.fillMaxSize().imePadding() modifier = Modifier.fillMaxSize().imePadding()
) { ) {
TopAppBar( 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 = { navigationIcon = {
IconButton(onClick = onBack) { Icon(Icons.AutoMirrored.Filled.ArrowBack, stringResource(R.string.back)) } IconButton(onClick = onBack) { Icon(Icons.AutoMirrored.Filled.ArrowBack, stringResource(R.string.back)) }
}, },
actions = { actions = {
IconButton(onClick = { viewModel.toggleSound() }) {
Text(if (soundOn) "\uD83D\uDD14" else "\uD83D\uDD15", fontSize = 18.sp)
}
IconButton( IconButton(
onClick = { viewModel.toggleRadio() }, onClick = { viewModel.toggleRadio() },
modifier = Modifier.background( 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) val action = stringResource(if (radioActive) R.string.radio_turn_off else R.string.radio_turn_on)
PortableRadioIcon(stringResource(R.string.radio_toggle, action)) 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)) } 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) { if (radioActive) {
RadioSourceBar(viewModel) RadioSourceBar(viewModel)
} }

3
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 androidx.compose.ui.unit.dp
import com.utun.chat.ChatApplication import com.utun.chat.ChatApplication
import com.utun.chat.data.NativeLib import com.utun.chat.data.NativeLib
import com.utun.chat.ui.components.ProfileAvatarControls
import com.utun.chat.data.NotificationSound import com.utun.chat.data.NotificationSound
import com.utun.chat.data.IncomingCallManager import com.utun.chat.data.IncomingCallManager
import com.utun.chat.data.RadioOverlayService 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 */ /* Keys */
item(key = "keys-title") { Text(stringResource(R.string.keys), style = MaterialTheme.typography.titleMedium) } 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) } item(key = "public-key") { OutlinedTextField(pubKey, {}, label = { Text(stringResource(R.string.public_key)) }, readOnly = true, singleLine = true) }

Loading…
Cancel
Save