diff --git a/src/main/java/fr/titionfire/ffsaf/ws/CompetitionWS.java b/src/main/java/fr/titionfire/ffsaf/ws/CompetitionWS.java index 7978fbd..56d8f42 100644 --- a/src/main/java/fr/titionfire/ffsaf/ws/CompetitionWS.java +++ b/src/main/java/fr/titionfire/ffsaf/ws/CompetitionWS.java @@ -51,6 +51,9 @@ public class CompetitionWS { @Inject RTeam rTeam; + @Inject + RState rState; + @Inject SecurityCtx securityCtx; @@ -95,6 +98,7 @@ public class CompetitionWS { getWSReceiverMethods(RRegister.class, rRegister); getWSReceiverMethods(RCard.class, rCard); getWSReceiverMethods(RTeam.class, rTeam); + getWSReceiverMethods(RState.class, rState); executor = notifyExecutor; } @@ -141,6 +145,7 @@ public class CompetitionWS { LOGGER.debugf("Active connections: %d", connection.getOpenConnections().size()); waitingResponse.remove(connection); + rState.removeConnection(connection); } private MessageOut makeReply(MessageIn message, Object data) { @@ -230,6 +235,30 @@ public class CompetitionWS { }); } + public static void sendNotifyState(WebSocketConnection connection, String code, Object data) { + String uuid = connection.pathParam("uuid"); + + List> queue = new ArrayList<>(); + queue.add(Uni.createFrom().voidItem()); // For avoid empty queue + + connection.getOpenConnections().forEach(c -> { + Boolean s = c.userData().get(UserData.TypedKey.forBoolean("needState")); + if (uuid.equals(c.pathParam("uuid")) && s != null && s) { + queue.add(c.sendText(new MessageOut(UUID.randomUUID(), code, MessageType.NOTIFY, data))); + } + }); + + Uni.join().all(queue) + .andCollectFailures() + .runSubscriptionOn(executor) + .subscribeAsCompletionStage() + .whenComplete((v, t) -> { + if (t != null) { + LOGGER.error("Error sending ws_out message", t); + } + }); + } + @OnError Uni error(WebSocketConnection connection, ForbiddenException t) { return connection.close(CloseReason.INTERNAL_SERVER_ERROR); diff --git a/src/main/java/fr/titionfire/ffsaf/ws/recv/RCard.java b/src/main/java/fr/titionfire/ffsaf/ws/recv/RCard.java index c061c71..657b6db 100644 --- a/src/main/java/fr/titionfire/ffsaf/ws/recv/RCard.java +++ b/src/main/java/fr/titionfire/ffsaf/ws/recv/RCard.java @@ -58,6 +58,8 @@ public class RCard { @WSReceiver(code = "getCardForMatch", permission = PermLevel.VIEW) public Uni> getCardForMatch(WebSocketConnection connection, Long matchId) { + if (matchId == null) + return Uni.createFrom().nullItem(); return getById(matchId, connection).chain(matchModel -> cardService.getForMatch(matchModel)); } diff --git a/src/main/java/fr/titionfire/ffsaf/ws/recv/RMatch.java b/src/main/java/fr/titionfire/ffsaf/ws/recv/RMatch.java index a902ebe..aa99262 100644 --- a/src/main/java/fr/titionfire/ffsaf/ws/recv/RMatch.java +++ b/src/main/java/fr/titionfire/ffsaf/ws/recv/RMatch.java @@ -51,6 +51,9 @@ public class RMatch { @Inject TradService trad; + @Inject + RState rState; + private Uni getById(long id, WebSocketConnection connection) { return matchRepository.findById(id) .invoke(Unchecked.consumer(o -> { @@ -195,6 +198,7 @@ public class RMatch { return Panache.withTransaction(() -> matchRepository.persist(mm)); }) .invoke(mm -> toSend.add(MatchEntity.fromModel(mm))) + .invoke(mm -> rState.setMatchEnd(connection, matchEnd)) .chain(mm -> updateEndAndTree(mm, toSend)) .invoke(__ -> SSMatch.sendMatch(connection, toSend)) .replaceWithVoid(); diff --git a/src/main/java/fr/titionfire/ffsaf/ws/recv/RState.java b/src/main/java/fr/titionfire/ffsaf/ws/recv/RState.java new file mode 100644 index 0000000..047dd94 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/recv/RState.java @@ -0,0 +1,149 @@ +package fr.titionfire.ffsaf.ws.recv; + +import fr.titionfire.ffsaf.ws.PermLevel; +import fr.titionfire.ffsaf.ws.send.SSState; +import io.quarkus.runtime.annotations.RegisterForReflection; +import io.quarkus.websockets.next.UserData; +import io.quarkus.websockets.next.WebSocketConnection; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; +import lombok.Data; + +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +@ApplicationScoped +@RegisterForReflection +public class RState { + + private static final HashMap tableStates = new HashMap<>(); + + @WSReceiver(code = "subscribeToState", permission = PermLevel.VIEW) + public Uni> sendCurrentScore(WebSocketConnection connection, Boolean subscribe) { + connection.userData().put(UserData.TypedKey.forBoolean("needState"), subscribe); + + if (subscribe) { + String uuid = connection.pathParam("uuid"); + return Uni.createFrom().item(() -> + tableStates.values().stream().filter(s -> s.getCompetitionUuid().equals(uuid)).toList() + ); + } + return Uni.createFrom().nullItem(); + } + + @WSReceiver(code = "sendState", permission = PermLevel.TABLE) + public Uni sendState(WebSocketConnection connection, TableState tableState) { + tableState.setCompetitionUuid(connection.pathParam("uuid")); + + if (tableStates.containsKey(connection)) + tableState.setId(tableStates.get(connection).getId()); + if (tableState.getChronoState().isRunning() && tableState.getChronoState().state == 0) + tableState.setState(MatchState.IN_PROGRESS); + tableStates.put(connection, tableState); + SSState.sendStateFull(connection, tableState); + return Uni.createFrom().voidItem(); + } + + @WSReceiver(code = "sendSelectCategory", permission = PermLevel.TABLE) + public Uni sendSelectCategory(WebSocketConnection connection, Long catId) { + TableState tableState = tableStates.get(connection); + if (tableState != null) { + tableState.setSelectedCategory(catId); + tableState.setState(MatchState.NOT_STARTED); + SSState.sendStateFull(connection, tableState); + } + return Uni.createFrom().voidItem(); + } + + @WSReceiver(code = "sendSelectMatch", permission = PermLevel.TABLE) + public Uni sendSelectMatch(WebSocketConnection connection, Long matchId) { + TableState tableState = tableStates.get(connection); + if (tableState != null) { + tableState.setSelectedMatch(matchId); + tableState.setState(MatchState.NOT_STARTED); + SSState.sendStateFull(connection, tableState); + } + return Uni.createFrom().voidItem(); + } + + @WSReceiver(code = "sendCurentChrono", permission = PermLevel.TABLE) + public Uni sendCurentChrono(WebSocketConnection connection, ChronoState chronoState) { + TableState tableState = tableStates.get(connection); + if (tableState != null) { + tableState.setChronoState(chronoState); + if (chronoState.isRunning()) + tableState.setState(MatchState.IN_PROGRESS); + SSState.sendStateFull(connection, tableState); + } + return Uni.createFrom().voidItem(); + } + + @WSReceiver(code = "sendLicenceName", permission = PermLevel.TABLE) + public Uni sendCurrentScore(WebSocketConnection connection, String name) { + TableState tableState = tableStates.get(connection); + if (tableState != null) { + tableState.setLiceName(name); + SSState.sendStateFull(connection, tableState); + } + return Uni.createFrom().voidItem(); + } + + @WSReceiver(code = "sendCurrentScore", permission = PermLevel.TABLE) + public Uni sendCurrentScore(WebSocketConnection connection, ScoreState scoreState) { + TableState tableState = tableStates.get(connection); + if (tableState != null) { + tableState.setScoreState(scoreState); + SSState.sendStateFull(connection, tableState); + } + return Uni.createFrom().voidItem(); + } + + public void removeConnection(WebSocketConnection connection) { + if (tableStates.containsKey(connection)) { + SSState.sendRmStateFull(connection, tableStates.get(connection).getId()); + tableStates.remove(connection); + } + } + + public void setMatchEnd(WebSocketConnection connection, RMatch.MatchEnd matchEnd) { + if (tableStates.containsKey(connection)) { + TableState tableState = tableStates.get(connection); + if (matchEnd.end()) + tableState.setState(MatchState.ENDED); + else + tableState.setState(MatchState.IN_PROGRESS); + SSState.sendStateFull(connection, tableState); + } + } + + @RegisterForReflection + public record ChronoState(long time, long startTime, long configTime, long configPause, int state) { + public boolean isRunning() { + return startTime != 0 || state != 0; + } + } + + @RegisterForReflection + public record ScoreState(int scoreRouge, int scoreBleu) { + } + + @Data + @RegisterForReflection + public static class TableState { + UUID id = UUID.randomUUID(); + String competitionUuid; + Long selectedCategory; + Long selectedMatch; + ChronoState chronoState; + ScoreState scoreState; + String liceName = "???"; + MatchState state = MatchState.NOT_STARTED; + } + + public enum MatchState { + NOT_STARTED, + IN_PROGRESS, + ENDED + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/send/SSState.java b/src/main/java/fr/titionfire/ffsaf/ws/send/SSState.java new file mode 100644 index 0000000..6c6c826 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/send/SSState.java @@ -0,0 +1,19 @@ +package fr.titionfire.ffsaf.ws.send; + +import fr.titionfire.ffsaf.ws.CompetitionWS; +import fr.titionfire.ffsaf.ws.recv.RState; +import io.quarkus.websockets.next.WebSocketConnection; + +import java.util.UUID; + +public class SSState { + + public static void sendStateFull(WebSocketConnection connection, RState.TableState state) { + CompetitionWS.sendNotifyState(connection, "sendStateFull", state); + } + + public static void sendRmStateFull(WebSocketConnection connection, UUID id) { + CompetitionWS.sendNotifyState(connection, "rmStateFull", id); + } + +} diff --git a/src/main/webapp/public/locales/en/cm.json b/src/main/webapp/public/locales/en/cm.json index dbb547d..a805338 100644 --- a/src/main/webapp/public/locales/en/cm.json +++ b/src/main/webapp/public/locales/en/cm.json @@ -5,7 +5,6 @@ "actuel": "Current", "administration": "Administration", "adresseDuServeur": "Server address", - "advertisement": "", "ajouter": "Add", "ajouterDesCombattants": "Add fighters", "ajouterUn": "Add one", @@ -40,6 +39,7 @@ "config.obs.motDePasseDuServeur": "Server password", "config.obs.warn1": "/! The password will be stored in plain text; it is recommended to use it only on OBS WebSocket and to change it between each competition", "config.obs.ws": "ws://", + "configurationDuNomDeLaZone": "Zone name configuration", "configurationObs": "OBS Configuration", "confirm1": "This match already has results; are you sure you want to delete it?", "confirm2.msg": "Do you really want to change the tournament tree size or the loser matches? This will modify existing matches (including possible deletions)!", @@ -64,6 +64,7 @@ "err3": "At least one type (pool or tournament) must be selected.", "erreurLorsDeLaCopieDansLePresse": "Error while copying to clipboard: ", "erreurLorsDeLaCréationDesMatchs": "Error while creating matches: ", + "etatDesTablesDeMarque": "State of marque tables", "exporter": "Export", "fermer": "Close", "finalesUniquement": "Finals only", @@ -80,6 +81,7 @@ "neRienConserver": "Keep nothing", "no": "No.", "nom": "Name", + "nomDeLaZone": "Area name", "nomDeLéquipe": "team name", "nomDesZonesDeCombat": "Combat zone names <1>(separated by ';')", "nouvelle...": "New...", @@ -146,7 +148,7 @@ "ttm.admin.obs": "Short click: Download resources. Long click: Create OBS configuration", "ttm.admin.scripte": "Copy integration script", "ttm.table.inverserLaPosition": "Reverse fighter positions on this screen", - "ttm.table.obs": "Short click: Load configuration and connect. Long click: Ring configuration", + "ttm.table.obs": "Short click: Load configuration and connect.", "ttm.table.pub_aff": "Open public display", "ttm.table.pub_score": "Show scores on public display", "type": "Type", diff --git a/src/main/webapp/public/locales/fr/cm.json b/src/main/webapp/public/locales/fr/cm.json index 0e3ff49..a0f1e20 100644 --- a/src/main/webapp/public/locales/fr/cm.json +++ b/src/main/webapp/public/locales/fr/cm.json @@ -5,7 +5,6 @@ "actuel": "Actuel", "administration": "Administration", "adresseDuServeur": "Adresse du serveur", - "advertisement": "Advertisement", "ajouter": "Ajouter", "ajouterDesCombattants": "Ajouter des combattants", "ajouterUn": "Ajouter un ", @@ -40,6 +39,7 @@ "config.obs.motDePasseDuServeur": "Mot de passe du serveur", "config.obs.warn1": "/! Le mot de passe va être stoker en claire, il est recommandé de ne l'utiliser que sur obs websocket et d'en changer entre chaque compétition", "config.obs.ws": "ws://", + "configurationDuNomDeLaZone": "Configuration du nom de la zone", "configurationObs": "Configuration OBS", "confirm1": "Ce match a déjà des résultats, êtes-vous sûr de vouloir le supprimer ?", "confirm2.msg": "Voulez-vous vraiment changer la taille de l'arbre du tournoi ou les matchs pour les perdants ? Cela va modifier les matchs existants (incluant des possibles suppressions)!", @@ -64,6 +64,7 @@ "err3": "Au moins un type (poule ou tournoi) doit être sélectionné.", "erreurLorsDeLaCopieDansLePresse": "Erreur lors de la copie dans le presse-papier : ", "erreurLorsDeLaCréationDesMatchs": "Erreur lors de la création des matchs: ", + "etatDesTablesDeMarque": "Etat des tables de marque", "exporter": "Exporter", "fermer": "Fermer", "finalesUniquement": "Finales uniquement", @@ -80,6 +81,7 @@ "neRienConserver": "Ne rien conserver", "no": "N°", "nom": "Nom", + "nomDeLaZone": "Nom de la zone", "nomDeLéquipe": "Nom de l'équipe", "nomDesZonesDeCombat": "Nom des zones de combat <1>(séparée par des ';')", "nouvelle...": "Nouvelle...", @@ -146,7 +148,7 @@ "ttm.admin.obs": "Clique court : Télécharger les ressources. Clique long : Créer la configuration obs", "ttm.admin.scripte": "Copier le scripte d'intégration", "ttm.table.inverserLaPosition": "Inverser la position des combattants sur cette écran", - "ttm.table.obs": "Clique court : Charger la configuration et se connecter. Clique long : Configuration de la lice", + "ttm.table.obs": "Clique court : Charger la configuration et se connecter.", "ttm.table.pub_aff": "Ouvrir l'affichage public", "ttm.table.pub_score": "Afficher les scores sur l'affichage public", "type": "Type", diff --git a/src/main/webapp/src/hooks/useWS.jsx b/src/main/webapp/src/hooks/useWS.jsx index 79fce96..3fdeada 100644 --- a/src/main/webapp/src/hooks/useWS.jsx +++ b/src/main/webapp/src/hooks/useWS.jsx @@ -47,6 +47,7 @@ export function WSProvider({url, onmessage, children}) { const [welcomeData, setWelcomeData] = useState({name: "", perm: "", show_blason: true, show_flag: false}) const [state, dispatch] = useReducer(reducer, {listener: []}) const ws = useRef(null) + const tableState = useRef({}) const listenersRef = useRef([]) const callbackRef = useRef({}) const isReadyRef = useRef(isReady) @@ -216,14 +217,14 @@ export function WSProvider({url, onmessage, children}) { } - const ret = {isReady, dispatch, send, wait_length: callbackRef, welcomeData} + const ret = {isReady, dispatch, send, wait_length: callbackRef, welcomeData, tableState} return {children} } export function useWS() { - const {isReady, dispatch, send, wait_length, welcomeData} = useContext(WebsocketContext) + const {isReady, dispatch, send, wait_length, welcomeData, tableState} = useContext(WebsocketContext) return { dispatch, isReady, @@ -247,6 +248,10 @@ export function useWS() { send(uuidv4(), "error", "ERROR", data) }, send, + setState: (newState) => { + tableState.current = {...tableState.current, ...newState} + }, + tableState } } diff --git a/src/main/webapp/src/pages/competition/editor/CMAdmin.jsx b/src/main/webapp/src/pages/competition/editor/CMAdmin.jsx index a2e301b..92ecf3c 100644 --- a/src/main/webapp/src/pages/competition/editor/CMAdmin.jsx +++ b/src/main/webapp/src/pages/competition/editor/CMAdmin.jsx @@ -11,10 +11,12 @@ import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {SimpleIconsOBS} from "../../../assets/SimpleIconsOBS.ts"; import JSZip from "jszip"; import {detectOptimalBackground} from "../../../components/SmartLogoBackground.jsx"; -import {faGlobe} from "@fortawesome/free-solid-svg-icons"; +import {faGlobe, faTableCellsLarge} from "@fortawesome/free-solid-svg-icons"; import {Trans, useTranslation} from "react-i18next"; import i18n from "i18next"; import {getToastMessage} from "../../../utils/Tools.js"; +import {copyStyles} from "../../../utils/copyStyles.js"; +import {StateWindow} from "./StateWindow.jsx"; const vite_url = import.meta.env.VITE_URL; @@ -161,12 +163,18 @@ async function downloadResourcesAsZip(resourceList) { progressText.textContent = i18n.t('téléchargementTerminé!'); } +const windowName = "FFSAFTableStateWindow"; + function Menu({menuActions, compUuid}) { const e = document.getElementById("actionMenu") const longPress = useRef({time: null, timer: null, button: null}); const obsModal = useRef(null); const {t} = useTranslation("cm"); + const [showStateWin, setShowStateWin] = useState(false) + const externalWindow = useRef(null) + const containerEl = useRef(document.createElement("div")) + for (const x of tto) x.dispose(); const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip2"]') @@ -178,6 +186,32 @@ function Menu({menuActions, compUuid}) { } } + useEffect(() => { + if (sessionStorage.getItem(windowName + "_open") === "true") { + handleStateWin(); + } + }, []); + + const handleStateWin = __ => { + if (showStateWin === false || !externalWindow.current || externalWindow.current.closed) { + externalWindow.current = window.open("", windowName, "width=800,height=600,left=200,top=200") + externalWindow.current.document.body.innerHTML = "" + externalWindow.current.document.body.appendChild(containerEl.current) + copyStyles(document, externalWindow.current.document) + + externalWindow.current.addEventListener("beforeunload", () => { + setShowStateWin(false); + externalWindow.current.close(); + externalWindow.current = null; + sessionStorage.removeItem(windowName + "_open"); + }); + setShowStateWin(true); + sessionStorage.setItem(windowName + "_open", "true"); + } else { + externalWindow.current.focus(); + } + } + const longPressDown = (button) => { longPress.current.button = button; longPress.current.time = new Date(); @@ -251,7 +285,12 @@ function Menu({menuActions, compUuid}) { onClick={() => copyScriptToClipboard()} data-bs-toggle="tooltip2" data-bs-placement="top" data-bs-title={t('ttm.admin.scripte')}/> + , document.getElementById("actionMenu"))} + {externalWindow.current && createPortal(, containerEl.current)} @@ -181,5 +187,19 @@ export function ChronoPanel() { + + } + +function SendChrono({chrono, config, chronoState}) { + const {sendNotify, setState} = useWS(); + + useEffect(() => { + setState({chronoState: {...chrono, configTime: config.time, configPause: config.pause, state: chronoState}}); + sendNotify("sendCurentChrono", {...chrono, configTime: config.time, configPause: config.pause, state: chronoState}); + }, [chrono]); + + return <> + +} diff --git a/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx b/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx index 328cc77..1f1055e 100644 --- a/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx +++ b/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx @@ -191,12 +191,13 @@ function MatchList({matches, cat, menuActions}) { const [lice, setLice] = useState(localStorage.getItem("cm_lice") || "1") const publicAffDispatch = usePubAffDispatch(); const {t} = useTranslation("cm"); - const {cards, getHeightCardForCombInMatch} = useCards(); + const {cards_v, getHeightCardForCombInMatch} = useCards(); + const {sendNotify, setState} = useWS(); const liceName = (cat.liceName || "N/A").split(";"); const marches2 = matches.filter(m => m.categorie_ord !== -42) .sort((a, b) => a.categorie_ord - b.categorie_ord) - .map(m => ({...m, ...win_end(m, Object.values(cards))})) + .map(m => ({...m, ...win_end(m, cards_v), end: m.end})) const firstIndex = marches2.findLastIndex(m => m.poule === '-') + 1; const isActiveMatch = (index) => { @@ -221,10 +222,6 @@ function MatchList({matches, cat, menuActions}) { }); } }, [match]); - //useEffect(() => { - // if (activeMatch !== null) - // setActiveMatch(null); - //}, [cat]) useEffect(() => { if (match && match.poule !== lice) @@ -240,6 +237,11 @@ function MatchList({matches, cat, menuActions}) { setActiveMatch(marches2.find((m, index) => !m.end && isActiveMatch(index))?.id); }, [matches]) + useEffect(() => { + setState({selectedMatch: activeMatch}); + sendNotify("sendSelectMatch", activeMatch); + }, [activeMatch]); + const GetCard = ({combId, match, cat}) => { const c = getHeightCardForCombInMatch(combId, match) @@ -329,6 +331,7 @@ function BuildTree({treeData, matches, menuActions}) { const {getComb} = useCombs() const publicAffDispatch = usePubAffDispatch(); const {cards_v} = useCards(); + const {sendNotify, setState} = useWS(); const match = matches.find(m => m.id === currentMatch?.matchSelect) useEffect(() => { @@ -390,6 +393,8 @@ function BuildTree({treeData, matches, menuActions}) { const onMatchClick = (rect, matchId, __) => { setCurrentMatch({matchSelect: matchId, matchNext: new TreeNode(matchId).nextMatchTree(trees.reverse())}); + setState({selectedMatch: matchId}); + sendNotify("sendSelectMatch", matchId); } const onClickVoid = () => { diff --git a/src/main/webapp/src/pages/competition/editor/CMTPoint.jsx b/src/main/webapp/src/pages/competition/editor/CMTPoint.jsx index 8dbb05d..18a1752 100644 --- a/src/main/webapp/src/pages/competition/editor/CMTPoint.jsx +++ b/src/main/webapp/src/pages/competition/editor/CMTPoint.jsx @@ -1,8 +1,9 @@ -import {useEffect, useState} from "react"; +import React, {useEffect, useState} from "react"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {faChevronDown, faChevronUp} from "@fortawesome/free-solid-svg-icons"; import {usePubAffDispatch} from "../../../hooks/useExternalWindow.jsx"; import {useTranslation} from "react-i18next"; +import {useWS} from "../../../hooks/useWS.jsx"; export function PointPanel({menuActions}) { const [revers, setRevers] = useState(false) @@ -49,5 +50,18 @@ export function PointPanel({menuActions}) { + } + +function SendScore({scoreRouge, scoreBleu}) { + const {sendNotify, setState} = useWS(); + + useEffect(() => { + setState({scoreState: {scoreRouge, scoreBleu}}); + sendNotify("sendCurrentScore", {scoreRouge, scoreBleu}); + }, [scoreRouge, scoreBleu]); + + return <> + +} diff --git a/src/main/webapp/src/pages/competition/editor/CMTable.jsx b/src/main/webapp/src/pages/competition/editor/CMTable.jsx index 9e2dc71..63bd969 100644 --- a/src/main/webapp/src/pages/competition/editor/CMTable.jsx +++ b/src/main/webapp/src/pages/competition/editor/CMTable.jsx @@ -59,6 +59,7 @@ export function CMTable() { + @@ -73,6 +74,7 @@ function Menu({menuActions}) { const publicAffDispatch = usePubAffDispatch() const [showPubAff, setShowPubAff] = useState(false) const [showScore, setShowScore] = useState(true) + const [zone, setZone] = useState(sessionStorage.getItem("liceName") || "???") const {connected, connect, disconnect} = useOBS(); const longPress = useRef({time: null, timer: null, button: null}); const obsModal = useRef(null); @@ -124,7 +126,7 @@ function Menu({menuActions}) { const longTimeAction = (button) => { if (button === "obs") { - obsModal.current.click(); + // obsModal.current.click(); } } @@ -169,12 +171,19 @@ function Menu({menuActions}) { } } - const handleOBSSubmit = (e) => { + const handleLiceSubmit = (e) => { e.preventDefault(); const form = e.target; const prefix = form[0].value; - sessionStorage.setItem("obs_prefix", prefix); + if (prefix === "") { + sessionStorage.removeItem("liceName"); + setZone("???"); + return; + } + + sessionStorage.setItem("liceName", prefix); + setZone(prefix); } if (!e) @@ -182,6 +191,8 @@ function Menu({menuActions}) { return <> {createPortal( <> +
+ obsModal.current.click()} style={{cursor: "pointer"}}>Zone {zone}
, document.getElementById("actionMenu"))} {externalWindow.current && createPortal(, containerEl.current)} - -