Redesign desktop lobby as a workspace

This commit is contained in:
Peter Stockings
2026-10-05 16:25:38 +11:00
parent 21a35d81f0
commit 0c5d4ee546
4 changed files with 330 additions and 141 deletions
+4
View File
@@ -10,6 +10,10 @@ address remains fixed. Detached chat opens docked on the next launch.
## Active sessions ## Active sessions
The desktop workspace has a sidebar for your saved display name, **Create room**,
and **Join by code**. Creation and code entry open in separate dialogs. Drag a
video onto the lobby to prepare a new room. Enter your name to enable room actions.
The lobby checks for listed rooms at startup and every 15 seconds while open. The lobby checks for listed rooms at startup and every 15 seconds while open.
Select a session and click **Choose file & join**, or double-click it, to use the Select a session and click **Choose file & join**, or double-click it, to use the
existing file verification flow. Enter your display name first. **Refresh** checks existing file verification flow. Enter your display name first. **Refresh** checks
+295 -141
View File
@@ -2,9 +2,38 @@ import os
import re import re
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton,
QFileDialog, QFrame, QApplication, QCheckBox, QListWidget, QListWidgetItem QFileDialog, QFrame, QApplication, QCheckBox, QListWidget, QListWidgetItem, QDialog,
) QStyledItemDelegate, QStyle
from PyQt6.QtCore import Qt, pyqtSignal )
from PyQt6.QtCore import Qt, pyqtSignal, QSize
from PyQt6.QtGui import QColor, QFont, QFontMetrics
class SessionDelegate(QStyledItemDelegate):
"""Draw readable session rows with elided filenames and secondary details."""
def paint(self, painter, option, index):
painter.save()
selected = bool(option.state & QStyle.StateFlag.State_Selected)
hovered = bool(option.state & QStyle.StateFlag.State_MouseOver)
painter.fillRect(option.rect, QColor("#29243f" if selected else
"#222839" if hovered else "#191e29"))
rect = option.rect.adjusted(22, 20, -22, -20)
lines = str(index.data()).splitlines()
for i, text in enumerate(lines[:3]):
font = QFont("Segoe UI", 10)
font.setWeight(QFont.Weight.DemiBold if i == 0 else QFont.Weight.Normal)
painter.setFont(font)
painter.setPen(QColor("#edf1fa" if i == 0 else "#9ca9c0"))
line = rect.adjusted(0, i * 24, 0, 0)
line.setHeight(24)
text = QFontMetrics(font).elidedText(text, Qt.TextElideMode.ElideRight, line.width())
painter.drawText(line, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, text)
painter.setPen(QColor("#2c3546"))
painter.drawLine(option.rect.bottomLeft(), option.rect.bottomRight())
if option.state & QStyle.StateFlag.State_HasFocus:
painter.setPen(QColor("#9b8bff"))
painter.drawRect(option.rect.adjusted(1, 1, -1, -1))
painter.restore()
class RoomCodeInput(QLineEdit): class RoomCodeInput(QLineEdit):
"""QLineEdit that auto-fills from clipboard if it looks like a room code.""" """QLineEdit that auto-fills from clipboard if it looks like a room code."""
@@ -23,9 +52,9 @@ class RoomCodeInput(QLineEdit):
class LobbyWidget(QWidget): class LobbyWidget(QWidget):
# Signals to communicate to VlcSyncApp # Signals to communicate to VlcSyncApp
create_requested = pyqtSignal(str, str, str, object) # username, path, filename, size create_requested = pyqtSignal(str, str, str, object) # username, path, filename, size
join_requested = pyqtSignal(str, str) # username, room_code join_requested = pyqtSignal(str, str) # username, room_code
file_selected = pyqtSignal(str) file_selected = pyqtSignal(str)
refresh_requested = pyqtSignal() refresh_requested = pyqtSignal()
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@@ -33,132 +62,239 @@ class LobbyWidget(QWidget):
self.local_file_path = None self.local_file_path = None
self.local_file_name = None self.local_file_name = None
self.local_file_size = 0 self.local_file_size = 0
self.media_folder = "" self.media_folder = ""
self._setup_ui() self._setup_ui()
def _setup_ui(self): def _setup_ui(self):
self.setObjectName("desktopLobby")
self._connecting = False
layout = QVBoxLayout(self) layout = QVBoxLayout(self)
layout.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
# Container for centering
container = QFrame()
container.setObjectName("lobbyCard")
container.setMinimumWidth(420)
container.setMaximumWidth(500)
container_layout = QVBoxLayout(container)
container_layout.setContentsMargins(30, 30, 30, 30)
container_layout.setSpacing(15)
# Brand header = QFrame()
title = QLabel("VideoSync") header.setObjectName("lobbyHeader")
title.setObjectName("brandTitle") header_layout = QHBoxLayout(header)
title.setAlignment(Qt.AlignmentFlag.AlignCenter) header_layout.setContentsMargins(28, 18, 28, 18)
tagline = QLabel("Watch together, anywhere") brand = QLabel("VideoSync")
tagline.setObjectName("brandTagline") brand.setObjectName("lobbyBrand")
tagline.setAlignment(Qt.AlignmentFlag.AlignCenter) header_layout.addWidget(brand)
header_layout.addStretch()
container_layout.addWidget(title) self.connection_label = QLabel("Watch together. Stay in sync.")
container_layout.addWidget(tagline) self.connection_label.setObjectName("lobbyMuted")
container_layout.addSpacing(20) header_layout.addWidget(self.connection_label)
layout.addWidget(header)
# Username body = QHBoxLayout()
body.setSpacing(0)
sidebar = QFrame()
sidebar.setObjectName("lobbySidebar")
sidebar.setFixedWidth(260)
side = QVBoxLayout(sidebar)
side.setContentsMargins(24, 32, 24, 24)
side.setSpacing(12)
title = QLabel("Watch together.")
title.setObjectName("sidebarTitle")
side.addWidget(title)
subtitle = QLabel("One room. Everyone in sync.")
subtitle.setObjectName("lobbyMuted")
side.addWidget(subtitle)
side.addSpacing(12)
self.create_action_btn = QPushButton("+ Create room")
self.create_action_btn.setObjectName("lobbyPrimary")
self.create_action_btn.clicked.connect(self.open_create_dialog)
side.addWidget(self.create_action_btn)
self.join_action_btn = QPushButton("Join by code")
self.join_action_btn.clicked.connect(self.open_join_dialog)
side.addWidget(self.join_action_btn)
side.addSpacing(28)
name_label = QLabel("Your display name")
side.addWidget(name_label)
self.username_input = QLineEdit() self.username_input = QLineEdit()
self.username_input.setPlaceholderText("Enter a display name") self.username_input.setPlaceholderText("Enter your name")
container_layout.addWidget(QLabel("YOUR NAME")) self.username_input.setAccessibleName("Your display name")
container_layout.addWidget(self.username_input) side.addWidget(self.username_input)
name_label.setBuddy(self.username_input)
container_layout.addSpacing(20) hint = QLabel("Remembered on this computer.")
hint.setObjectName("lobbyMuted")
# Actions Layout (Create vs Join) hint.setWordWrap(True)
actions_layout = QHBoxLayout() side.addWidget(hint)
actions_layout.setSpacing(20) side.addStretch()
privacy = QLabel("Your video stays on your computer.\nOnly playback and chat are shared.")
# Create Room Panel privacy.setObjectName("lobbyMuted")
create_panel = QVBoxLayout() privacy.setWordWrap(True)
create_panel.addWidget(QLabel("Create a Room")) side.addWidget(privacy)
self.create_file_btn = QPushButton("Choose Video File") body.addWidget(sidebar)
self.create_file_btn.setObjectName("secondaryBtn")
workspace = QVBoxLayout()
workspace.setContentsMargins(32, 32, 32, 24)
workspace.setSpacing(12)
heading = QHBoxLayout()
title_group = QVBoxLayout()
title_group.setSpacing(8)
title = QLabel("Active sessions")
title.setObjectName("workspaceTitle")
title_group.addWidget(title)
description = QLabel("See who’s watching and join in.")
description.setObjectName("lobbyMuted")
title_group.addWidget(description)
heading.addLayout(title_group)
heading.addStretch()
self.refresh_btn = QPushButton("Refresh")
self.refresh_btn.setObjectName("lobbyQuiet")
self.refresh_btn.clicked.connect(self.refresh_requested.emit)
heading.addWidget(self.refresh_btn)
workspace.addLayout(heading)
workspace.addSpacing(12)
self.sessions_status = QLabel("Checking for active sessions…")
self.sessions_status.setObjectName("lobbyMuted")
self.sessions_status.setWordWrap(True)
workspace.addWidget(self.sessions_status)
self.sessions_list = QListWidget()
self.sessions_list.setObjectName("lobbySessions")
self.sessions_list.setWordWrap(True)
self.sessions_list.setItemDelegate(SessionDelegate(self.sessions_list))
self.sessions_list.setAccessibleName("Active sessions")
workspace.addWidget(self.sessions_list, 1)
bottom = QHBoxLayout()
self.session_count = QLabel("Select a session to get started.")
self.session_count.setObjectName("lobbyMuted")
bottom.addWidget(self.session_count)
bottom.addStretch()
self.session_join_btn = QPushButton("Choose file && join")
self.session_join_btn.setObjectName("lobbyPrimary")
self.session_join_btn.setEnabled(False)
self.session_join_btn.clicked.connect(self.join_selected_session)
bottom.addWidget(self.session_join_btn)
workspace.addLayout(bottom)
self.sessions_list.itemDoubleClicked.connect(lambda _: self.join_selected_session())
self.sessions_list.itemSelectionChanged.connect(self.check_inputs)
body.addLayout(workspace, 1)
layout.addLayout(body, 1)
self.create_dialog = self._make_dialog("Create a room", "Choose a local video, then invite your friends.")
create = self.create_dialog.layout()
self.create_file_btn = QPushButton("Choose video file")
self.create_file_btn.clicked.connect(self.select_file) self.create_file_btn.clicked.connect(self.select_file)
create.addWidget(self.create_file_btn)
self.create_file_info = QLabel("") self.create_file_info = QLabel("")
self.create_file_info.setObjectName("fileInfo") self.create_file_info.setObjectName("lobbyFileInfo")
self.create_file_info.setWordWrap(True) self.create_file_info.setWordWrap(True)
self.create_file_info.hide() self.create_file_info.hide()
create.addWidget(self.create_file_info)
self.create_room_btn = QPushButton("Create Room") self.listed_checkbox = QCheckBox("List room in active sessions")
self.create_room_btn.setObjectName("primaryBtn") self.listed_checkbox.setChecked(True)
self.create_room_btn.setEnabled(False) create.addWidget(self.listed_checkbox)
hint = QLabel("Unlisted rooms can still be joined using their code.")
hint.setObjectName("lobbyMuted")
hint.setWordWrap(True)
create.addWidget(hint)
self.create_room_btn = QPushButton("Create room")
self.create_room_btn.setObjectName("lobbyPrimary")
self.create_room_btn.clicked.connect(self.create_room) self.create_room_btn.clicked.connect(self.create_room)
self._dialog_actions(self.create_dialog, self.create_room_btn)
create_panel.addWidget(self.create_file_btn)
create_panel.addWidget(self.create_file_info) self.join_dialog = self._make_dialog("Join by code", "Enter the room code your friend shared.")
self.listed_checkbox = QCheckBox("List room in active sessions")
self.listed_checkbox.setChecked(True)
self.listed_checkbox.setToolTip("Unlisted rooms can still be joined using their code.")
create_panel.addWidget(self.listed_checkbox)
create_panel.addWidget(self.create_room_btn)
# Join Room Panel
join_panel = QVBoxLayout()
join_panel.addWidget(QLabel("Join a Room"))
self.room_code_input = RoomCodeInput() self.room_code_input = RoomCodeInput()
self.room_code_input.setPlaceholderText("e.g. ABC123") self.room_code_input.setPlaceholderText("e.g. ABC123")
self.join_room_btn = QPushButton("Join Room") self.room_code_input.setAccessibleName("Room code")
self.join_room_btn.setObjectName("secondaryBtn") self.room_code_input.setMaxLength(8)
self.join_room_btn.setEnabled(False) self.room_code_input.returnPressed.connect(self._join_if_valid)
self.join_dialog.layout().addWidget(self.room_code_input)
self.join_room_btn = QPushButton("Join room")
self.join_room_btn.setObjectName("lobbyPrimary")
self.join_room_btn.clicked.connect(self.join_room) self.join_room_btn.clicked.connect(self.join_room)
self._dialog_actions(self.join_dialog, self.join_room_btn)
join_panel.addWidget(self.room_code_input)
join_panel.addWidget(self.join_room_btn)
actions_layout.addLayout(create_panel)
# Divider
divider = QLabel("OR")
divider.setAlignment(Qt.AlignmentFlag.AlignCenter)
divider.setObjectName("divider")
actions_layout.addWidget(divider)
actions_layout.addLayout(join_panel)
container_layout.addLayout(actions_layout)
panels = QHBoxLayout()
panels.addWidget(container)
sessions = QVBoxLayout()
heading = QHBoxLayout()
heading.addWidget(QLabel("ALREADY WATCHING"))
self.refresh_btn = QPushButton("Refresh")
self.refresh_btn.clicked.connect(self.refresh_requested.emit)
heading.addWidget(self.refresh_btn)
sessions.addLayout(heading)
self.sessions_status = QLabel("Checking for active sessions…")
self.sessions_status.setWordWrap(True)
sessions.addWidget(self.sessions_status)
self.sessions_list = QListWidget()
self.sessions_list.setWordWrap(True)
self.sessions_list.setMinimumWidth(280)
sessions.addWidget(self.sessions_list)
self.session_join_btn = QPushButton("Choose file & join")
self.session_join_btn.setEnabled(False)
self.session_join_btn.clicked.connect(self.join_selected_session)
self.sessions_list.itemDoubleClicked.connect(lambda _: self.join_selected_session())
self.sessions_list.itemSelectionChanged.connect(self.check_inputs)
sessions.addWidget(self.session_join_btn)
panels.addLayout(sessions)
layout.addLayout(panels)
# Signals to enable/disable buttons
self.username_input.textChanged.connect(self.check_inputs) self.username_input.textChanged.connect(self.check_inputs)
self.room_code_input.textChanged.connect(self.check_inputs) self.room_code_input.textChanged.connect(self.check_inputs)
self._apply_lobby_style()
self.check_inputs()
def _make_dialog(self, title, description):
dialog = QDialog(self)
dialog.setWindowTitle(title)
dialog.setObjectName("lobbyDialog")
dialog.setModal(True)
dialog.setMinimumWidth(420)
layout = QVBoxLayout(dialog)
layout.setContentsMargins(28, 28, 28, 28)
layout.setSpacing(18)
label = QLabel(title)
label.setObjectName("dialogTitle")
layout.addWidget(label)
text = QLabel(description)
text.setObjectName("lobbyMuted")
text.setWordWrap(True)
layout.addWidget(text)
return dialog
def _dialog_actions(self, dialog, action):
row = QHBoxLayout()
row.addStretch()
cancel = QPushButton("Cancel")
cancel.clicked.connect(dialog.reject)
row.addWidget(cancel)
row.addWidget(action)
dialog.layout().addLayout(row)
def open_create_dialog(self):
if not self.username_input.text().strip():
self.username_input.setFocus()
return
self.create_dialog.show()
self.create_dialog.raise_()
self.create_file_btn.setFocus()
def open_join_dialog(self):
if not self.username_input.text().strip():
self.username_input.setFocus()
return
self.join_dialog.show()
self.join_dialog.raise_()
self.room_code_input.setFocus()
def _join_if_valid(self):
if self.join_room_btn.isEnabled():
self.join_room()
def _apply_lobby_style(self):
self.setStyleSheet("""
QWidget#desktopLobby, QDialog#lobbyDialog { background: #10131b; }
QLabel { color: #edf1fa; font-family: 'Segoe UI'; font-size: 13px; font-weight: normal; }
QFrame#lobbyHeader { background: #191e29; border-bottom: 1px solid #2c3546; }
QFrame#lobbySidebar { background: #191e29; border-right: 1px solid #2c3546; }
QLabel#lobbyBrand { font-size: 20px; font-weight: 600; }
QLabel#sidebarTitle, QLabel#dialogTitle { font-size: 23px; font-weight: 600; }
QLabel#workspaceTitle { font-size: 28px; font-weight: 600; }
QLabel#lobbyMuted { color: #9ca9c0; font-size: 12px; }
QLabel#lobbyFileInfo { background: #29243f; color: #c6bcff; padding: 14px; border-radius: 7px; }
QLineEdit { background: #10131b; color: #edf1fa; border: 1px solid #364157;
border-radius: 7px; padding: 11px; font: 14px 'Segoe UI'; }
QLineEdit:focus { border-color: #9b8bff; }
QPushButton { background: #191e29; color: #edf1fa; border: 1px solid #364157;
border-radius: 7px; padding: 11px 15px; font: 13px 'Segoe UI'; }
QPushButton:hover { background: #242b3b; border-color: #9b8bff; }
QPushButton#lobbyPrimary { background: #9b8bff; color: #141020; border: none; font-weight: 600; }
QPushButton#lobbyPrimary:hover { background: #b1a4ff; }
QPushButton:disabled, QPushButton#lobbyPrimary:disabled { background: #242b38; color: #738099; border-color: #2c3546; }
QPushButton#lobbyQuiet { background: transparent; }
QCheckBox { color: #edf1fa; spacing: 9px; font: 13px 'Segoe UI'; }
QListWidget#lobbySessions { background: #191e29; color: #edf1fa;
border: 1px solid #2c3546; border-radius: 9px; outline: none; font: 14px 'Segoe UI'; }
QListWidget#lobbySessions::item { padding: 20px; border-bottom: 1px solid #2c3546; }
QListWidget#lobbySessions::item:selected { background: #29243f; color: #edf1fa; }
QListWidget#lobbySessions::item:hover { background: #222839; }
QListWidget#lobbySessions:focus { border-color: #9b8bff; }
""")
def _set_local_file(self, file_path: str): def _set_local_file(self, file_path: str):
self.local_file_path = file_path self.local_file_path = file_path
self.local_file_name = os.path.basename(file_path) self.local_file_name = os.path.basename(file_path)
self.local_file_size = os.path.getsize(file_path) self.local_file_size = os.path.getsize(file_path)
self.media_folder = os.path.dirname(file_path) self.media_folder = os.path.dirname(file_path)
self.file_selected.emit(file_path) self.file_selected.emit(file_path)
size_mb = self.local_file_size / (1024 * 1024) size_mb = self.local_file_size / (1024 * 1024)
self.create_file_info.setText(f"{self.local_file_name}\n{size_mb:.1f} MB") self.create_file_info.setText(f"{self.local_file_name}\n{size_mb:.1f} MB")
@@ -167,7 +303,7 @@ class LobbyWidget(QWidget):
def select_file(self): def select_file(self):
file_path, _ = QFileDialog.getOpenFileName( file_path, _ = QFileDialog.getOpenFileName(
self, "Select Video File", self.media_folder, "Video Files (*.mp4 *.mkv *.avi *.mov *.webm);;All Files (*)" self, "Select Video File", self.media_folder, "Video Files (*.mp4 *.mkv *.avi *.mov *.webm);;All Files (*)"
) )
if file_path: if file_path:
self._set_local_file(file_path) self._set_local_file(file_path)
@@ -185,6 +321,7 @@ class LobbyWidget(QWidget):
ext = os.path.splitext(file_path)[1].lower() ext = os.path.splitext(file_path)[1].lower()
if ext in ['.mp4', '.mkv', '.avi', '.mov', '.webm']: if ext in ['.mp4', '.mkv', '.avi', '.mov', '.webm']:
self._set_local_file(file_path) self._set_local_file(file_path)
self.open_create_dialog()
break break
def check_inputs(self): def check_inputs(self):
@@ -192,36 +329,47 @@ class LobbyWidget(QWidget):
has_file = self.local_file_path is not None has_file = self.local_file_path is not None
has_code = len(self.room_code_input.text().strip()) >= 4 has_code = len(self.room_code_input.text().strip()) >= 4
self.create_room_btn.setEnabled(has_name and has_file) self.create_room_btn.setEnabled(has_name and has_file and not self._connecting)
self.join_room_btn.setEnabled(has_name and has_code) self.create_action_btn.setEnabled(has_name and not self._connecting)
self.session_join_btn.setEnabled(has_name and self.sessions_list.currentItem() is not None) self.join_action_btn.setEnabled(has_name and not self._connecting)
self.join_room_btn.setEnabled(has_name and has_code and not self._connecting)
def update_sessions(self, rooms): self.session_join_btn.setEnabled(has_name and self.sessions_list.currentItem() is not None and not self._connecting)
selected = self.sessions_list.currentItem()
code = selected.data(Qt.ItemDataRole.UserRole) if selected else None def update_sessions(self, rooms):
self.sessions_list.clear() selected = self.sessions_list.currentItem()
for room in rooms: code = selected.data(Qt.ItemDataRole.UserRole) if selected else None
seconds = max(0, int(room.get("position", 0))) self.sessions_list.clear()
state = "Playing" if room.get("playing") else "Paused" for room in rooms:
users = room.get("users", []) seconds = max(0, int(room.get("position", 0)))
item = QListWidgetItem(f"{room['fileInfo']['name']}\n" state = "Playing" if room.get("playing") else "Paused"
f"{state} · {seconds // 60}:{seconds % 60:02d} · {len(users)} connected\n" users = room.get("users", [])
f"{', '.join(users)}") item = QListWidgetItem(f"{room['fileInfo']['name']}\n"
item.setData(Qt.ItemDataRole.UserRole, room['code']) f"{state} · {seconds // 60}:{seconds % 60:02d} · {len(users)} connected\n"
self.sessions_list.addItem(item) f"{', '.join(users)}")
if room['code'] == code: item.setSizeHint(QSize(0, 116))
self.sessions_list.setCurrentItem(item) item.setToolTip(item.text())
self.sessions_status.setText("Select a session to join." if rooms else item.setData(Qt.ItemDataRole.UserRole, room['code'])
"No active sessions. Create a room to get started.") self.sessions_list.addItem(item)
self.check_inputs() if room['code'] == code:
self.sessions_list.setCurrentItem(item)
def join_selected_session(self): self.session_count.setText(f"{len(rooms)} room{'s' if len(rooms) != 1 else ''} available")
item = self.sessions_list.currentItem() self.sessions_list.setVisible(bool(rooms))
if item and self.username_input.text().strip(): self.sessions_status.setText("Select a session to join." if rooms else
self.room_code_input.setText(item.data(Qt.ItemDataRole.UserRole)) "No active sessions. Create a room to get started.")
self.join_room() self.check_inputs()
def join_selected_session(self):
item = self.sessions_list.currentItem()
if item and self.username_input.text().strip():
self.room_code_input.setText(item.data(Qt.ItemDataRole.UserRole))
self.join_room()
def create_room(self): def create_room(self):
if not self.create_room_btn.isEnabled():
return
self._connecting = True
self.create_dialog.accept()
self.check_inputs()
username = self.username_input.text().strip() username = self.username_input.text().strip()
self.create_room_btn.setText("Connecting...") self.create_room_btn.setText("Connecting...")
self.create_room_btn.setEnabled(False) self.create_room_btn.setEnabled(False)
@@ -229,6 +377,11 @@ class LobbyWidget(QWidget):
self.create_requested.emit(username, self.local_file_path, self.local_file_name, self.local_file_size) self.create_requested.emit(username, self.local_file_path, self.local_file_name, self.local_file_size)
def join_room(self): def join_room(self):
if not self.join_room_btn.isEnabled():
return
self._connecting = True
self.join_dialog.accept()
self.check_inputs()
username = self.username_input.text().strip() username = self.username_input.text().strip()
room_code = self.room_code_input.text().strip().upper() room_code = self.room_code_input.text().strip().upper()
self.join_room_btn.setText("Connecting...") self.join_room_btn.setText("Connecting...")
@@ -237,8 +390,9 @@ class LobbyWidget(QWidget):
self.join_requested.emit(username, room_code) self.join_requested.emit(username, room_code)
def reset_ui(self): def reset_ui(self):
self.create_room_btn.setText("Create Room") self._connecting = False
self.join_room_btn.setText("Join Room") self.create_room_btn.setText("Create room")
self.join_room_btn.setText("Join room")
self.check_inputs() self.check_inputs()
def clear_file(self): def clear_file(self):
+2
View File
@@ -109,6 +109,8 @@ class VlcSyncApp(QMainWindow):
self.lobby_widget.update_sessions(rooms) self.lobby_widget.update_sessions(rooms)
except (ValueError, KeyError, TypeError, OverflowError): except (ValueError, KeyError, TypeError, OverflowError):
self.lobby_widget.sessions_list.clear() self.lobby_widget.sessions_list.clear()
self.lobby_widget.sessions_list.hide()
self.lobby_widget.session_count.setText("Join by code is still available.")
self.lobby_widget.check_inputs() self.lobby_widget.check_inputs()
self.lobby_widget.sessions_status.setText("Couldn’t check for sessions. You can retry or join by code.") self.lobby_widget.sessions_status.setText("Couldn’t check for sessions. You can retry or join by code.")
finally: finally:
+29
View File
@@ -1,4 +1,5 @@
from lobby_widget import LobbyWidget from lobby_widget import LobbyWidget
from PyQt6.QtCore import Qt
def test_session_selection_uses_existing_join_flow(qtbot): def test_session_selection_uses_existing_join_flow(qtbot):
@@ -21,3 +22,31 @@ def test_session_selection_uses_existing_join_flow(qtbot):
lobby.update_sessions([]) lobby.update_sessions([])
assert not lobby.session_join_btn.isEnabled() assert not lobby.session_join_btn.isEnabled()
assert "No active sessions" in lobby.sessions_status.text() assert "No active sessions" in lobby.sessions_status.text()
def test_workspace_dialogs_and_connection_lock(qtbot, tmp_path):
lobby = LobbyWidget()
qtbot.addWidget(lobby)
lobby.show()
assert not lobby.create_action_btn.isEnabled()
lobby.username_input.setText("Alex")
qtbot.mouseClick(lobby.create_action_btn, Qt.MouseButton.LeftButton)
assert lobby.create_dialog.isVisible()
assert not lobby.create_room_btn.isEnabled()
video = tmp_path / "movie.mkv"
video.write_bytes(b"local video")
lobby._set_local_file(str(video))
lobby.listed_checkbox.setChecked(False)
with qtbot.waitSignal(lobby.create_requested) as signal:
qtbot.mouseClick(lobby.create_room_btn, Qt.MouseButton.LeftButton)
assert signal.args == ["Alex", str(video), "movie.mkv", 11]
assert not lobby.create_dialog.isVisible()
assert not lobby.create_action_btn.isEnabled()
assert not lobby.join_action_btn.isEnabled()
lobby.reset_ui()
qtbot.mouseClick(lobby.join_action_btn, Qt.MouseButton.LeftButton)
lobby.room_code_input.setText("abc123")
with qtbot.waitSignal(lobby.join_requested) as signal:
qtbot.keyClick(lobby.room_code_input, Qt.Key.Key_Return)
assert signal.args == ["Alex", "ABC123"]
assert not lobby.join_dialog.isVisible()