Browse Source

Cover channel closure and keep stream selection usable for long lists

master
evgeny 6 days ago
parent
commit
dd034a9d42
  1. 3
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChatScreen.kt
  2. 13
      tools/chatgui/tests/test_stream_view.cpp
  3. 1
      tools/chatgui/transport/stream_bridge.cpp

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

@ -15,6 +15,7 @@ import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
@ -107,7 +108,7 @@ fun ChatScreen(
onDismissRequest = { chooseStream = false }, onDismissRequest = { chooseStream = false },
title = { Text(stringResource(R.string.stream_choose_source)) }, title = { Text(stringResource(R.string.stream_choose_source)) },
text = { text = {
Column { Column(Modifier.heightIn(max = 360.dp).verticalScroll(rememberScrollState())) {
streams.forEach { source -> streams.forEach { source ->
TextButton(onClick = { chooseStream = false; onWatchStream(source) }) { TextButton(onClick = { chooseStream = false; onWatchStream(source) }) {
Text("${source.displayName} · ${source.id.toULong()}") Text("${source.displayName} · ${source.id.toULong()}")

13
tools/chatgui/tests/test_stream_view.cpp

@ -45,7 +45,7 @@ class StreamViewTest : public QObject {
} }
void publish(const QByteArray& video, const QByteArray& audio = {}) { void publish(const QByteArray& video, const QByteArray& audio = {}) {
stream_media_block media{}; media.format = {160, 90, 90, STREAM_MEDIA_H264, static_cast<uint8_t>(audio.isEmpty() ? 0 : STREAM_MEDIA_OPUS)}; stream_media_block media{}; media.format = {160, 90, 90, STREAM_MEDIA_H264, STREAM_MEDIA_OPUS};
media.first_frame_offset = 0; media.video_count = 1; media.first_frame_offset = 0; media.video_count = 1;
media.video[0] = {++frameId, frameId * 100000, static_cast<uint32_t>(video.size()), 0, media.video[0] = {++frameId, frameId * 100000, static_cast<uint32_t>(video.size()), 0,
static_cast<uint32_t>(video.size()), STREAM_MEDIA_KEY, reinterpret_cast<const uint8_t*>(video.constData())}; static_cast<uint32_t>(video.size()), STREAM_MEDIA_KEY, reinterpret_cast<const uint8_t*>(video.constData())};
@ -153,6 +153,17 @@ private slots:
QCOMPARE(stream_get_info(publisher, &info), STREAM_OK); QCOMPARE(info.owners, 1u); QCOMPARE(stream_get_info(publisher, &info), STREAM_OK); QCOMPARE(info.owners, 1u);
} }
void channelClosureNotifiesViewer() {
QVERIFY(gui_stream_attach(inst)); QVERIFY(gui_stream_watch(group));
stream_info info{}; QCOMPARE(stream_get_info(publisher, &info), STREAM_OK);
QVERIFY(gui_stream_open(105, group, inst->node_id, info.descriptor.source_epoch));
StreamRecord event; QVERIFY(receive(105, event)); QCOMPARE(event.kind, StreamRecordKind::Opened);
QByteArray channel = QByteArray::number(group); chat_core_delete_channel(inst, channel.constData());
QVERIFY(receive(105, event)); QCOMPARE(event.kind, StreamRecordKind::Closed); QCOMPARE(event.reason, STREAM_CLOSE_GROUP);
QTRY_COMPARE(snapshots.last().mid(9), QByteArray("[]"));
QVERIFY(!stream_handle_active(publisher)); QCOMPARE(stream_handle_reason(publisher), STREAM_CLOSE_GROUP);
}
void cleanupTestCase() { void cleanupTestCase() {
poll.stop(); gui_stream_detach(inst); stream_close(publisher); uasync_mark_stopped(ua); poll.stop(); gui_stream_detach(inst); stream_close(publisher); uasync_mark_stopped(ua);
utun_instance_destroy(inst); uasync_destroy(ua, 0); utun_instance_destroy(inst); uasync_destroy(ua, 0);

1
tools/chatgui/transport/stream_bridge.cpp

@ -157,6 +157,7 @@ void gui_stream_detach(UTUN_INSTANCE* inst) {
std::lock_guard<std::mutex> guard(bridge.lock); std::lock_guard<std::mutex> guard(bridge.lock);
if (!inst || bridge.inst != inst) return; if (!inst || bridge.inst != inst) return;
if (bridge.pending) uasync_cancel_post(inst->ua, bridge.pending); if (bridge.pending) uasync_cancel_post(inst->ua, bridge.pending);
stream_watch_sources(inst, 0);
bridge.pending = nullptr; stream_set_event_callback(inst, nullptr, nullptr); bridge.pending = nullptr; stream_set_event_callback(inst, nullptr, nullptr);
if (bridge.viewer.id) closeViewer(StreamRecordKind::Closed, STREAM_CLOSE_SERVICE); if (bridge.viewer.id) closeViewer(StreamRecordKind::Closed, STREAM_CLOSE_SERVICE);
if (bridge.opening) output({bridge.requested.id, 0, StreamRecordKind::Closed, {}, 0, STREAM_CLOSE_SERVICE}); if (bridge.opening) output({bridge.requested.id, 0, StreamRecordKind::Closed, {}, 0, STREAM_CLOSE_SERVICE});

Loading…
Cancel
Save