From 59858a726afeb38eaad8b6a2eb73af9f361ec83f Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 20 Oct 2026 19:56:50 +0200 Subject: [PATCH] Share active stream button across Android group and home headers --- .../chat/ui/components/OwnStreamButton.kt | 29 +++++++++++++++++++ .../utun/chat/ui/screens/ChannelListScreen.kt | 3 ++ .../com/utun/chat/ui/screens/ChatScreen.kt | 17 ++--------- 3 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/OwnStreamButton.kt diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/OwnStreamButton.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/OwnStreamButton.kt new file mode 100644 index 00000000..edb01e70 --- /dev/null +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/OwnStreamButton.kt @@ -0,0 +1,29 @@ +package com.utun.chat.ui.components + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Videocam +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.runtime.* +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import com.utun.chat.R +import com.utun.chat.data.LogManager +import com.utun.chat.data.StreamController + +/** Общая кнопка локального publisher: доступна из любого канала и главного экрана до окончания публикации. */ +@Composable +fun OwnStreamButton(location: String, onOpen: (Long) -> Unit) { + val state by StreamController.state.collectAsState() + val publisher = state?.takeIf { it.ongoing } + LaunchedEffect(location, publisher?.id, publisher?.phase) { + LogManager.addLog("INFO", "stream", "own stream header location=$location publisher=${publisher?.id} " + + "group=${publisher?.group} phase=${publisher?.phase} visible=${publisher != null}") + } + if (publisher != null) IconButton(onClick = { + LogManager.addLog("INFO", "stream", "own stream header clicked location=$location publisher=${publisher.id} group=${publisher.group}") + onOpen(publisher.group) + }) { + Icon(Icons.Filled.Videocam, stringResource(R.string.stream_manage), tint = Color(0xFFE53935)) + } +} 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 bb576cf8..31db4179 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 @@ -23,6 +23,7 @@ import androidx.lifecycle.compose.LifecycleResumeEffect import com.utun.chat.ui.components.GroupAvatar import com.utun.chat.ui.components.UserAvatar import com.utun.chat.ui.components.GroupPropertiesDialog +import com.utun.chat.ui.components.OwnStreamButton import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -59,6 +60,7 @@ fun ChannelListScreen( onDeleteChannel: (Channel) -> Unit, onSettings: () -> Unit, onLogs: () -> Unit, + onOwnStream: (Long) -> Unit = {}, onRefresh: () -> Unit = {} ) { LifecycleResumeEffect(Unit) { @@ -87,6 +89,7 @@ fun ChannelListScreen( .background(if (isStuck) Color(0xFFFFB4AB) else Color(0xFF81C784), CircleShape) ) Spacer(Modifier.width(4.dp)) + OwnStreamButton("channel-list", onOwnStream) IconButton(onClick = onJoinChannel) { Icon(Icons.Default.QrCode, stringResource(R.string.join)) } IconButton(onClick = onLogs) { Icon(Icons.Default.Build, stringResource(R.string.logs)) } IconButton(onClick = onSettings) { Icon(Icons.Default.Settings, stringResource(R.string.settings)) } 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 3921df53..1d8e401c 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 @@ -69,12 +69,11 @@ 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.ui.components.OwnStreamButton import com.utun.chat.data.ConversationTarget import com.utun.chat.viewmodel.ChatViewModel -import com.utun.chat.data.StreamController import com.utun.chat.data.StreamSource import androidx.compose.material.icons.filled.LiveTv -import androidx.compose.material.icons.filled.Videocam import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -94,6 +93,7 @@ fun ChatScreen( onRecordVideo: () -> Unit = {}, onCapturePhoto: () -> Unit = {}, onStreams: () -> Unit = {}, + onOwnStream: (Long) -> Unit = {}, onWatchStream: (StreamSource) -> Unit = {}, onWatchAllStreams: () -> Unit = {} ) { @@ -106,12 +106,6 @@ fun ChatScreen( var menu by remember(channel.id) { mutableStateOf(false) } val streams by viewModel.streamSources.collectAsState() val watchSources = streams.filterNot { it.self } - val publisher by StreamController.state.collectAsState() - val ownStream = publisher?.takeIf { it.ongoing && it.group.toULong().toString() == channel.id } - LaunchedEffect(channel.id, ownStream?.id, ownStream?.phase) { - LogManager.addLog("INFO", "stream", "own stream header channel=${channel.id} publisher=${ownStream?.id} " + - "phase=${ownStream?.phase} visible=${ownStream != null}") - } var chooseStream by remember(channel.id) { mutableStateOf(false) } if (chooseStream) AlertDialog( onDismissRequest = { chooseStream = false }, @@ -216,12 +210,7 @@ fun ChatScreen( Icon(Icons.Filled.LiveTv, stringResource(R.string.stream_available, watchSources.size), tint = Color(0xFFE53935)) } } - if (ownStream != null) IconButton(onClick = { - LogManager.addLog("INFO", "stream", "own stream header clicked channel=${channel.id} publisher=${ownStream.id}") - onStreams() - }) { - Icon(Icons.Filled.Videocam, stringResource(R.string.stream_manage), tint = Color(0xFFE53935)) - } + OwnStreamButton("channel=${channel.id}", onOwnStream) IconButton( onClick = { viewModel.toggleRadio() }, modifier = Modifier.background(