Browse Source

Add Android PM and member invitation buttons with group picker

master
evgeny 6 days ago
parent
commit
d13663eb23
  1. 36
      tools/chatgui-android/app/src/main/java/com/utun/chat/MainActivity.kt
  2. 4
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/DmChatScreen.kt
  3. 7
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/MemberListScreen.kt
  4. 46
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/PmInviteDialog.kt

36
tools/chatgui-android/app/src/main/java/com/utun/chat/MainActivity.kt

@ -201,6 +201,14 @@ class MainActivity : AppCompatActivity() {
var screenBeforeCall by rememberSaveable { mutableStateOf<Screen?>(null) }
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
val pmInvite by vm.pmInvite.collectAsState()
val linkJoin by vm.linkJoin.collectAsState()
val joinedChannel by vm.joinedChannel.collectAsState()
LaunchedEffect(joinedChannel) {
joinedChannel?.let { channel ->
vm.consumeJoinedChannel(); vm.selectChannel(channel); screen = Screen.Chat
}
}
val callState by vm.callState.collectAsState()
@ -440,6 +448,34 @@ class MainActivity : AppCompatActivity() {
}
}
pmInvite?.let { state ->
PmInviteDialog(state, vm::selectPmInviteGroup, vm::sendPmInvite, vm::dismissPmInvite)
}
linkJoin?.let { state ->
AlertDialog(
onDismissRequest = vm::dismissLinkJoin,
title = { Text(stringResource(R.string.join_channel)) },
text = {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(stringResource(R.string.invite_lifetime))
if (state.pending) {
LinearProgressIndicator(Modifier.fillMaxWidth())
Text(stringResource(R.string.connecting_peer))
} else {
Text(if (state.channelId == 0L) stringResource(R.string.invalid_invite_link)
else stringResource(R.string.invite_join_failed, state.result ?: -1),
color = MaterialTheme.colorScheme.error)
}
}
},
confirmButton = {
if (!state.pending && state.channelId != 0L) TextButton(onClick = { vm.openInviteLink(state.link) }) {
Text(stringResource(R.string.retry))
}
},
dismissButton = { TextButton(onClick = vm::dismissLinkJoin) { Text(stringResource(R.string.close)) } }
)
}
if (showJoinDialog) {
JoinChannelDialog(
onConnect = { data: InviteData ->

4
tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/DmChatScreen.kt

@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Call
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.*
@ -70,6 +71,9 @@ fun DmChatScreen(conv: DmConversation, messages: List<Message>, viewModel: ChatV
}, navigationIcon = {
IconButton(onClick = onBack) { Icon(Icons.AutoMirrored.Filled.ArrowBack, stringResource(R.string.back)) }
}, actions = {
IconButton(onClick = { viewModel.showPmInvite(conv.peerNodeId, conv.displayName) }) {
Icon(Icons.Filled.Add, stringResource(R.string.invite_to_group))
}
IconButton(onClick = { viewModel.startDmCall() }, enabled = callAvailable && callState?.ongoing != true) {
Icon(Icons.Filled.Call, stringResource(if (callAvailable) R.string.call else R.string.peer_unavailable))
}

7
tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/MemberListScreen.kt

@ -92,7 +92,8 @@ fun MemberListScreen(
isSelected = selectedMember?.nodeId == m.nodeId,
onClick = { viewModel.showMemberDetail(channel.id, m.nodeId) },
onCall = { viewModel.startCall(channel.id, m.nodeId, m.name) },
onMessage = { viewModel.startDm(channel.id, m.nodeId, m.name) }
onMessage = { viewModel.startDm(channel.id, m.nodeId, m.name) },
onInvite = { viewModel.showPmInvite(m.nodeId, m.name, channel.id) }
)
}
}
@ -152,7 +153,8 @@ private fun MemberListItem(
isSelected: Boolean = false,
onClick: () -> Unit,
onCall: () -> Unit,
onMessage: () -> Unit
onMessage: () -> Unit,
onInvite: () -> Unit
) {
val onlineColor = when {
member.connecting -> Color(0xFFFFEB3B) // yellow: connecting
@ -232,6 +234,7 @@ private fun MemberListItem(
}
if (!member.isSelf) {
IconButton(onClick = onInvite) { Icon(Icons.Filled.Add, stringResource(R.string.invite_to_group)) }
IconButton(onClick = onCall, enabled = member.online) { Icon(Icons.Filled.Call, stringResource(R.string.call)) }
IconButton(onClick = onMessage) { Icon(Icons.AutoMirrored.Filled.Message, stringResource(R.string.message)) }
}

46
tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/PmInviteDialog.kt

@ -0,0 +1,46 @@
package com.utun.chat.ui.screens
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.utun.chat.R
import com.utun.chat.viewmodel.ChatViewModel
/** Один диалог для строки участника и шапки PM; адресат и выбор переживают пересоздание Activity. */
@Composable
fun PmInviteDialog(state: ChatViewModel.PmInviteState, onChoose: (String) -> Unit, onSend: () -> Unit, onDismiss: () -> Unit) {
AlertDialog(
onDismissRequest = onDismiss,
title = { Text(stringResource(R.string.invite_to_group)) },
text = {
Column(Modifier.verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(stringResource(R.string.invite_recipient, state.name))
Text(stringResource(R.string.invite_lifetime), style = MaterialTheme.typography.bodySmall)
Text(stringResource(if (state.groups.isEmpty()) R.string.invite_no_groups else R.string.invite_choose_group))
state.groups.forEach { group ->
Row(Modifier.fillMaxWidth().clickable(enabled = !state.pending) { onChoose(group.id) },
verticalAlignment = Alignment.CenterVertically) {
RadioButton(selected = state.groupId == group.id, onClick = { onChoose(group.id) }, enabled = !state.pending)
Text(group.name, modifier = Modifier.weight(1f))
}
}
if (state.pending) {
LinearProgressIndicator(Modifier.fillMaxWidth())
Text(stringResource(R.string.invite_sending))
}
if (state.errorRes != 0) Text(stringResource(state.errorRes), color = MaterialTheme.colorScheme.error)
}
},
confirmButton = {
TextButton(onClick = onSend, enabled = !state.pending && state.groupId.isNotEmpty()) { Text(stringResource(R.string.invite)) }
},
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) } }
)
}
Loading…
Cancel
Save