import { reactive, computed } from 'vue'; import mqtt from 'mqtt'; import config from '@/config.js'; import sessionConfig from '@/quizz/vulture-session-2026-01/session-configuration.json'; // Reactive state const state = reactive({ currentQuestionIndex: 0, questions: [], isMediaHidden: true, packTitle: '', timer: 0 // Timer in seconds }); // MQTT Client let client = null; let timerInterval = null; let initialized = false; // Initialize Store function init() { if (initialized) { console.log('QuizStore: Already initialized'); return; } initialized = true; // Load local config immediately state.questions = sessionConfig.Questions || []; state.packTitle = sessionConfig.PackTitle || ''; // Connect MQTT client = mqtt.connect(config.mqttBrokerUrl); client.on('connect', () => { console.log('QuizStore: MQTT Connected'); client.subscribe('game/quiz/control'); client.subscribe('/display/control'); }); client.on('message', (topic, message) => { const msgStr = message.toString(); console.log('QuizStore: MQTT Message Received', topic, msgStr); if (topic === 'game/quiz/control') { try { const payload = JSON.parse(msgStr); handleRemoteCommand(payload); } catch (e) { console.error('QuizStore: JSON Parse Error', e); } } else if (topic === '/display/control') { // Handle raw string commands from MqttButtons if (msgStr === 'next') { _nextQuestion(true); } else if (msgStr === 'previous') { _prevQuestion(true); } else if (msgStr === 'play') { // Start timer for picture questions const currentQ = state.questions[state.currentQuestionIndex]; if (currentQ?.Type === 'picture') { const playTime = currentQ.Settings?.PlayTime; if (playTime && playTime > 0) { console.log('QuizStore: Starting timer for picture', playTime); actions.startTimer(playTime); } } } else if (msgStr === 'pause') { stopTimer(); } } }); } function handleRemoteCommand(cmd) { if (cmd.action === 'next') { _nextQuestion(false); } else if (cmd.action === 'prev') { _prevQuestion(false); } else if (cmd.action === 'setIndex') { state.currentQuestionIndex = cmd.index; } } // Internal actions (boolean publish determines if we send MQTT) function _nextQuestion(publish = true) { if (state.currentQuestionIndex < state.questions.length - 1) { state.currentQuestionIndex++; if (publish && client) { client.publish('game/quiz/control', JSON.stringify({ action: 'setIndex', index: state.currentQuestionIndex })); } } } function _prevQuestion(publish = true) { if (state.currentQuestionIndex > 0) { state.currentQuestionIndex--; if (publish && client) { client.publish('game/quiz/control', JSON.stringify({ action: 'setIndex', index: state.currentQuestionIndex })); } } } // Public Actions const actions = { init, nextQuestion: () => _nextQuestion(true), prevQuestion: () => _prevQuestion(true), setQuestion: (index) => { if (index >= 0 && index < state.questions.length) { state.currentQuestionIndex = index; if (client) { client.publish('game/quiz/control', JSON.stringify({ action: 'setIndex', index: index })); } } }, startTimer: (seconds) => { stopTimer(); state.timer = seconds; publishTimer(); timerInterval = setInterval(() => { if (state.timer > 0) { state.timer--; publishTimer(); } else { stopTimer(); // Auto-hide by publishing pause if (client) { client.publish('/display/control', 'pause'); } } }, 1000); }, stopTimer }; function stopTimer() { if (timerInterval) { clearInterval(timerInterval); timerInterval = null; } state.timer = 0; publishTimer(); } function publishTimer() { if (client) { client.publish('game/timer', JSON.stringify({ time: state.timer })); } } // Getters const getters = { currentQuestion: computed(() => state.questions[state.currentQuestionIndex]), isFirstQuestion: computed(() => state.currentQuestionIndex === 0), isLastQuestion: computed(() => state.currentQuestionIndex === state.questions.length - 1), totalQuestions: computed(() => state.questions.length), packTitle: computed(() => state.packTitle), currentQuestionIndex: computed(() => state.currentQuestionIndex), timer: computed(() => state.timer) }; export default { state, actions, getters };