263 lines
10 KiB
Python
263 lines
10 KiB
Python
import webbrowser
|
|
from sys import platform
|
|
from os import environ
|
|
|
|
from PySide6 import QtGui
|
|
from PySide6.QtCore import Qt
|
|
from PySide6.QtUiTools import QUiLoader
|
|
from PySide6.QtWidgets import QMainWindow, QSizePolicy
|
|
from PySide6.QtCore import QThread, Signal
|
|
|
|
from config.config_manager import ConfigManager
|
|
from config.constants import PlayerServerInfo, Urls
|
|
from tools.http_client import ApiError
|
|
from ui.error_dialog import show_qt_error
|
|
from ui.hazard_stripes import HazardButton
|
|
from controllers.audio_controller import AudioController
|
|
from controllers.glow_animator import GlowAnimator
|
|
from controllers.window_dragger import WindowDragger
|
|
from discord import discord_oauth
|
|
from fivemserver.whitelistmanager import WhiteList
|
|
from fivemserver.fivemlauncher import FiveMLauncher
|
|
from fivemserver.queuemanager import QueueManager
|
|
from fivemserver.get_server_token import GetServerTokenForDiscord
|
|
from fake_patch_notes import patch_note
|
|
|
|
# For Linux Wayland to authorize moving window
|
|
if platform.startswith('linux'):
|
|
environ["QT_QPA_PLATFORM"] = "xcb"
|
|
|
|
class MainWindow(QMainWindow):
|
|
#update = Signal(str) # Reçoit les callbacks de QueueManager
|
|
|
|
def __init__(self, bundle_dir: str, config_manager: ConfigManager):
|
|
super().__init__()
|
|
|
|
self.config = config_manager
|
|
self.stored_user_id = self.config.get_discord_user()
|
|
self.queue_thread = None
|
|
|
|
# UI
|
|
self.ui = QUiLoader().load(f"{bundle_dir}/ui/mainwindow_vertical_pager.ui", self)
|
|
self.setCentralWidget(self.ui.centralWidget())
|
|
self.setWindowFlags(Qt.WindowType.FramelessWindowHint | Qt.WindowType.Window)
|
|
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
|
|
|
# Par défaut on affiche la page normal pour la connexion au serveur
|
|
self.ui.stackedWidget.setCurrentIndex(0)
|
|
# On cache par défaut les infos liste d'attente
|
|
self.ui.queue_lbl.hide()
|
|
self.ui.queue_position.hide()
|
|
|
|
|
|
# Si l'id discord = "" ou des espace, alors on affiche la page comme quoi faut être connecté à discord.
|
|
if self.stored_user_id == "" or self.stored_user_id.isspace():
|
|
self.ui.stackedWidget.setCurrentIndex(1)
|
|
else:
|
|
try:
|
|
# on vérifie si le joueur est whitelisté
|
|
WhiteList.check_whitelist(Urls.API_URL.value, self.stored_user_id)
|
|
except ApiError as exc:
|
|
show_qt_error(self, "La Tanière", f"Impossible de vérifier la whitelist.\n\n{exc}")
|
|
PlayerServerInfo.is_whitelist = False
|
|
PlayerServerInfo.is_staff = False
|
|
|
|
# si on est whitelisté, on démarre la file d'attente
|
|
if PlayerServerInfo.is_whitelist:
|
|
self.start_queue()
|
|
self.ui.queue_lbl.show()
|
|
self.ui.queue_position.show()
|
|
else:
|
|
self.ui.stackedWidget.setCurrentIndex(2)
|
|
|
|
# Test bouton en contruction
|
|
en_chantier = False
|
|
# on set la css du bouton en fonction de la valeur de la variable en_chantier
|
|
self.set_en_chantier(en_chantier)
|
|
if en_chantier:
|
|
old_btn = self.ui.connexion_btn
|
|
parent_layout = self.ui.verticalLayout_6 # layout direct du bouton dans le .ui
|
|
|
|
index = parent_layout.indexOf(old_btn)
|
|
|
|
new_btn = HazardButton(old_btn.parentWidget())
|
|
new_btn.setObjectName("connexion_btn")
|
|
new_btn.setText("EN MAINTENANCE")
|
|
new_btn.setIcon(old_btn.icon())
|
|
new_btn.setIconSize(old_btn.iconSize())
|
|
new_btn.setMinimumSize(old_btn.minimumSize())
|
|
new_btn.set_hazard(True)
|
|
|
|
parent_layout.takeAt(index)
|
|
old_btn.deleteLater()
|
|
parent_layout.insertWidget(index, new_btn)
|
|
|
|
self.ui.connexion_btn = new_btn
|
|
self.ui.connexion_btn.clicked.connect(self._on_connexion)
|
|
|
|
# centrage vertical du bouton connexion
|
|
if not PlayerServerInfo.is_staff:
|
|
self.ui.staff_btn.hide()
|
|
layout = self.ui.verticalLayout_6
|
|
# Trouver et modifier le spacer item
|
|
for i in range(layout.count()):
|
|
item = layout.itemAt(i)
|
|
if item.spacerItem(): # C'est un spacer
|
|
item.spacerItem().changeSize(20, 15, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
|
layout.invalidate() # Forcer le recalcul du layout
|
|
break
|
|
|
|
self.ui.info_text.setMarkdown(patch_note)
|
|
|
|
# Sous-systèmes
|
|
self._audio = AudioController(self.config, self.ui.audio_volume_adjust, self.ui.mute_btn)
|
|
self._glow = GlowAnimator(self.ui.connexion_btn)
|
|
self._dragger = WindowDragger(self)
|
|
|
|
self._connect_signals()
|
|
self._center_window()
|
|
|
|
self.show()
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
# Setup
|
|
# ------------------------------------------------------------------
|
|
|
|
def _connect_signals(self) -> None:
|
|
self.ui.close_btn.clicked.connect(self.close)
|
|
self.ui.minimize_btn.clicked.connect(self.showMinimized)
|
|
self.ui.connexion_btn.clicked.connect(self._on_connexion)
|
|
self.ui.discord_btn.clicked.connect(self._on_discord)
|
|
self.ui.intranet_btn.clicked.connect(self._on_intranet)
|
|
self.ui.discord_auth_btn.clicked.connect(self._on_discord_auth_btn)
|
|
self.ui.no_whitelist_btn.clicked.connect(self.close)
|
|
|
|
def _center_window(self) -> None:
|
|
self.adjustSize()
|
|
screen = (
|
|
QtGui.QGuiApplication.screenAt(QtGui.QCursor.pos())
|
|
or QtGui.QGuiApplication.primaryScreen()
|
|
)
|
|
rect = self.frameGeometry()
|
|
rect.moveCenter(screen.availableGeometry().center())
|
|
self.move(rect.topLeft())
|
|
|
|
# ------------------------------------------------------------------
|
|
# Button handlers
|
|
# ------------------------------------------------------------------
|
|
|
|
def _on_connexion(self) -> None:
|
|
try:
|
|
session_id = GetServerTokenForDiscord.authenticate(Urls.API_URL.value)
|
|
GetServerTokenForDiscord.register_discord_user(self.stored_user_id, session_id)
|
|
FiveMLauncher.launch()
|
|
except ApiError as exc:
|
|
show_qt_error(self, "Connexion impossible", f"Erreur lors de la connexion.\n\n{exc}")
|
|
|
|
@staticmethod
|
|
def _on_discord() -> None:
|
|
webbrowser.open(Urls.DISCORD.value)
|
|
|
|
def _on_intranet(self) -> None:
|
|
webbrowser.open(Urls.INTRANET.value)
|
|
self._glow.start()
|
|
|
|
def _on_discord_auth_btn(self) -> None:
|
|
try:
|
|
test = discord_oauth.get_discord_user_id()
|
|
self.config.set_discord_user(test[0])
|
|
PlayerServerInfo.session_id = test[1]
|
|
self.config.save()
|
|
self.ui.stackedWidget.setCurrentIndex(0)
|
|
except ApiError as exc:
|
|
show_qt_error(self, "Connexion Discord", f"Impossible de récupérer ton compte Discord.\n\n{exc}")
|
|
# ------------------------------------------------------------------
|
|
# Mouse events → délégués au WindowDragger
|
|
# ------------------------------------------------------------------
|
|
|
|
def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
|
|
self._dragger.mouse_press(event)
|
|
super().mousePressEvent(event)
|
|
|
|
def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
|
|
self._dragger.mouse_move(event)
|
|
super().mouseMoveEvent(event)
|
|
|
|
def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> None:
|
|
self._dragger.mouse_release(event)
|
|
super().mouseReleaseEvent(event)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Close
|
|
# ------------------------------------------------------------------
|
|
|
|
def closeEvent(self, event) -> None:
|
|
if self.queue_thread and self.queue_thread.isRunning():
|
|
self.queue_thread.stop()
|
|
self.queue_thread.wait() # Attend que le thread se termine proprement
|
|
self.config.save()
|
|
super().closeEvent(event)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Change ui on runtime
|
|
# ------------------------------------------------------------------
|
|
def set_en_chantier(self, valeur: bool):
|
|
self.en_chantier = valeur # ta variable Python
|
|
self.ui.connexion_btn.setProperty("en_chantier", valeur) # propriété Qt
|
|
self.ui.connexion_btn.style().unpolish(self.ui.connexion_btn)
|
|
self.ui.connexion_btn.style().polish(self.ui.connexion_btn)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Queue managment
|
|
# ------------------------------------------------------------------
|
|
def start_queue(self):
|
|
self.queue_thread = QueueThread(self.stored_user_id)
|
|
self.queue_thread = QueueThread(self.stored_user_id, parent=self) # ← parent=self
|
|
self.queue_thread.update.connect(self.handle_update)
|
|
self.queue_thread.start()
|
|
|
|
# 🧪 TEMP - Simule une position en queue pour tester l'UI
|
|
self.handle_update("position:3:10")
|
|
|
|
def handle_update(self, message: str):
|
|
# print(f"[handle_update] reçu: {message}") # ← Debug
|
|
if message == "ok":
|
|
self.ui.queue_lbl.setVisible(False)
|
|
self.ui.queue_position.setVisible(False)
|
|
#self.ui.connexion_btn.setEnabled(True)
|
|
#self.ui.connexion_btn.setText("Lancer FiveM")
|
|
#self.launch_fivem()
|
|
|
|
elif message == "ready":
|
|
self.ui.queue_lbl.setVisible(True)
|
|
self.ui.queue_position.setVisible(False)
|
|
self.ui.queue_lbl.setText("🚀 C'est votre tour !")
|
|
self.launch_fivem()
|
|
|
|
elif message.startswith("position:"):
|
|
_, pos, total = message.split(":")
|
|
self.ui.queue_lbl.setVisible(True)
|
|
self.ui.queue_position.setVisible(True)
|
|
#self.ui.queue_position.setText(f"{pos} / {total}") <- si on veux le total de slots
|
|
self.ui.queue_position.setText(f"{pos}")
|
|
|
|
def launch_fivem(self):
|
|
pass
|
|
|
|
class QueueThread(QThread):
|
|
update = Signal(str)
|
|
|
|
def __init__(self, user_id: str, parent=None): # ← parent=None
|
|
super().__init__(parent) # ← passé à QThread
|
|
self.manager = QueueManager(
|
|
user_id=user_id,
|
|
on_update=self.update.emit
|
|
)
|
|
|
|
def run(self):
|
|
self.manager.start()
|
|
|
|
def stop(self):
|
|
self.manager.stop()
|