pretty much ready to 'ship'.3
This commit is contained in:
@@ -2,7 +2,7 @@ export const branding = {
|
|||||||
app: {
|
app: {
|
||||||
name: 'HSO Jackbox Game Picker',
|
name: 'HSO Jackbox Game Picker',
|
||||||
shortName: 'HSO JGP',
|
shortName: 'HSO JGP',
|
||||||
version: '0.3.1',
|
version: '0.3.2 - Safari Walkabout Edition',
|
||||||
description: 'Spicing up Hyper Spaceout game nights!',
|
description: 'Spicing up Hyper Spaceout game nights!',
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { useAuth } from '../context/AuthContext';
|
import { useAuth } from '../context/AuthContext';
|
||||||
import { useToast } from '../components/Toast';
|
import { useToast } from '../components/Toast';
|
||||||
import api from '../api/axios';
|
import api from '../api/axios';
|
||||||
@@ -17,9 +17,32 @@ function History() {
|
|||||||
const [showAllSessions, setShowAllSessions] = useState(false);
|
const [showAllSessions, setShowAllSessions] = useState(false);
|
||||||
const [deletingSession, setDeletingSession] = useState(null);
|
const [deletingSession, setDeletingSession] = useState(null);
|
||||||
|
|
||||||
|
const loadSessions = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
const response = await api.get('/sessions');
|
||||||
|
setSessions(response.data);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to load sessions', err);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const refreshSessionGames = useCallback(async (sessionId, silent = false) => {
|
||||||
|
try {
|
||||||
|
const response = await api.get(`/sessions/${sessionId}/games`);
|
||||||
|
// Reverse chronological order (most recent first)
|
||||||
|
setSessionGames(response.data.reverse());
|
||||||
|
} catch (err) {
|
||||||
|
if (!silent) {
|
||||||
|
console.error('Failed to load session games', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadSessions();
|
loadSessions();
|
||||||
}, []);
|
}, [loadSessions]);
|
||||||
|
|
||||||
// Auto-select active session if navigating from picker
|
// Auto-select active session if navigating from picker
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -29,7 +52,7 @@ function History() {
|
|||||||
loadSessionGames(activeSession.id);
|
loadSessionGames(activeSession.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [sessions]);
|
}, [sessions, selectedSession]);
|
||||||
|
|
||||||
// Poll for session list updates (to detect when sessions end/start)
|
// Poll for session list updates (to detect when sessions end/start)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -38,7 +61,7 @@ function History() {
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, []);
|
}, [loadSessions]);
|
||||||
|
|
||||||
// Poll for updates on active session games
|
// Poll for updates on active session games
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -49,22 +72,11 @@ function History() {
|
|||||||
|
|
||||||
// Refresh games every 3 seconds for active session
|
// Refresh games every 3 seconds for active session
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
loadSessionGames(selectedSession, true); // silent refresh
|
refreshSessionGames(selectedSession, true); // silent refresh
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [selectedSession, sessions]);
|
}, [selectedSession, sessions, refreshSessionGames]);
|
||||||
|
|
||||||
const loadSessions = async () => {
|
|
||||||
try {
|
|
||||||
const response = await api.get('/sessions');
|
|
||||||
setSessions(response.data);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to load sessions', err);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleExport = async (sessionId, format) => {
|
const handleExport = async (sessionId, format) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState, useCallback } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useAuth } from '../context/AuthContext';
|
import { useAuth } from '../context/AuthContext';
|
||||||
import api from '../api/axios';
|
import api from '../api/axios';
|
||||||
@@ -11,21 +11,19 @@ function Home() {
|
|||||||
const [sessionGames, setSessionGames] = useState([]);
|
const [sessionGames, setSessionGames] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
const loadSessionGames = useCallback(async (sessionId, silent = false) => {
|
||||||
loadActiveSession();
|
try {
|
||||||
|
const gamesResponse = await api.get(`/sessions/${sessionId}/games`);
|
||||||
|
// Reverse chronological order (most recent first)
|
||||||
|
setSessionGames(gamesResponse.data.reverse());
|
||||||
|
} catch (error) {
|
||||||
|
if (!silent) {
|
||||||
|
console.error('Failed to load session games', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Auto-refresh for active session status and games
|
const loadActiveSession = useCallback(async () => {
|
||||||
useEffect(() => {
|
|
||||||
// Poll for session status changes every 3 seconds
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
loadActiveSession();
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const loadActiveSession = async () => {
|
|
||||||
try {
|
try {
|
||||||
const response = await api.get('/sessions/active');
|
const response = await api.get('/sessions/active');
|
||||||
|
|
||||||
@@ -42,19 +40,21 @@ function Home() {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
}, [loadSessionGames]);
|
||||||
|
|
||||||
const loadSessionGames = async (sessionId, silent = false) => {
|
useEffect(() => {
|
||||||
try {
|
loadActiveSession();
|
||||||
const gamesResponse = await api.get(`/sessions/${sessionId}/games`);
|
}, [loadActiveSession]);
|
||||||
// Reverse chronological order (most recent first)
|
|
||||||
setSessionGames(gamesResponse.data.reverse());
|
// Auto-refresh for active session status and games
|
||||||
} catch (error) {
|
useEffect(() => {
|
||||||
if (!silent) {
|
// Poll for session status changes every 3 seconds
|
||||||
console.error('Failed to load session games', error);
|
const interval = setInterval(() => {
|
||||||
}
|
loadActiveSession();
|
||||||
}
|
}, 3000);
|
||||||
};
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [loadActiveSession]);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useAuth } from '../context/AuthContext';
|
import { useAuth } from '../context/AuthContext';
|
||||||
import api from '../api/axios';
|
import api from '../api/axios';
|
||||||
@@ -42,29 +42,31 @@ function Picker() {
|
|||||||
// Exclude previously played games
|
// Exclude previously played games
|
||||||
const [excludePlayedGames, setExcludePlayedGames] = useState(false);
|
const [excludePlayedGames, setExcludePlayedGames] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
const checkActiveSession = useCallback(async () => {
|
||||||
// Wait for auth to finish loading before checking authentication
|
try {
|
||||||
if (authLoading) return;
|
const sessionResponse = await api.get('/sessions/active');
|
||||||
|
const session = sessionResponse.data?.session !== undefined
|
||||||
if (!isAuthenticated) {
|
? sessionResponse.data.session
|
||||||
navigate('/login');
|
: sessionResponse.data;
|
||||||
return;
|
|
||||||
|
// Check if session status changed
|
||||||
|
setActiveSession(prevSession => {
|
||||||
|
// If we had a session but now don't, mark it as ended
|
||||||
|
if (prevSession && (!session || !session.id)) {
|
||||||
|
setSessionEnded(true);
|
||||||
|
return null;
|
||||||
|
} else if (session && session.id) {
|
||||||
|
setSessionEnded(false);
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
return prevSession;
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to check session status', err);
|
||||||
}
|
}
|
||||||
loadData();
|
}, []);
|
||||||
}, [isAuthenticated, authLoading, navigate]);
|
|
||||||
|
|
||||||
// Poll for active session status changes
|
const loadData = useCallback(async () => {
|
||||||
useEffect(() => {
|
|
||||||
if (!isAuthenticated || authLoading) return;
|
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
checkActiveSession();
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, [isAuthenticated, authLoading]);
|
|
||||||
|
|
||||||
const loadData = async () => {
|
|
||||||
try {
|
try {
|
||||||
// Load active session
|
// Load active session
|
||||||
const sessionResponse = await api.get('/sessions/active');
|
const sessionResponse = await api.get('/sessions/active');
|
||||||
@@ -85,27 +87,29 @@ function Picker() {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const checkActiveSession = async () => {
|
useEffect(() => {
|
||||||
try {
|
// Wait for auth to finish loading before checking authentication
|
||||||
const sessionResponse = await api.get('/sessions/active');
|
if (authLoading) return;
|
||||||
const session = sessionResponse.data?.session !== undefined
|
|
||||||
? sessionResponse.data.session
|
if (!isAuthenticated) {
|
||||||
: sessionResponse.data;
|
navigate('/login');
|
||||||
|
return;
|
||||||
// If we had a session but now don't, mark it as ended
|
|
||||||
if (activeSession && (!session || !session.id)) {
|
|
||||||
setSessionEnded(true);
|
|
||||||
setActiveSession(null);
|
|
||||||
} else if (session && session.id) {
|
|
||||||
setActiveSession(session);
|
|
||||||
setSessionEnded(false);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to check session status', err);
|
|
||||||
}
|
}
|
||||||
};
|
loadData();
|
||||||
|
}, [isAuthenticated, authLoading, navigate, loadData]);
|
||||||
|
|
||||||
|
// Poll for active session status changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isAuthenticated || authLoading) return;
|
||||||
|
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
checkActiveSession();
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [isAuthenticated, authLoading, checkActiveSession]);
|
||||||
|
|
||||||
const handleCreateSession = async () => {
|
const handleCreateSession = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -727,11 +731,7 @@ function SessionInfo({ sessionId, onGamesUpdate }) {
|
|||||||
const [confirmingRemove, setConfirmingRemove] = useState(null);
|
const [confirmingRemove, setConfirmingRemove] = useState(null);
|
||||||
const [showPopularity, setShowPopularity] = useState(true);
|
const [showPopularity, setShowPopularity] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
const loadGames = useCallback(async () => {
|
||||||
loadGames();
|
|
||||||
}, [sessionId, onGamesUpdate]);
|
|
||||||
|
|
||||||
const loadGames = async () => {
|
|
||||||
try {
|
try {
|
||||||
const response = await api.get(`/sessions/${sessionId}/games`);
|
const response = await api.get(`/sessions/${sessionId}/games`);
|
||||||
// Reverse chronological order (most recent first)
|
// Reverse chronological order (most recent first)
|
||||||
@@ -741,7 +741,20 @@ function SessionInfo({ sessionId, onGamesUpdate }) {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
}, [sessionId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadGames();
|
||||||
|
}, [sessionId, onGamesUpdate, loadGames]);
|
||||||
|
|
||||||
|
// Auto-refresh games list every 3 seconds
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
loadGames();
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [loadGames]);
|
||||||
|
|
||||||
const handleUpdateStatus = async (gameId, newStatus) => {
|
const handleUpdateStatus = async (gameId, newStatus) => {
|
||||||
try {
|
try {
|
||||||
@@ -815,8 +828,8 @@ function SessionInfo({ sessionId, onGamesUpdate }) {
|
|||||||
<p className="text-gray-500 dark:text-gray-400">No games played yet. Pick a game to get started!</p>
|
<p className="text-gray-500 dark:text-gray-400">No games played yet. Pick a game to get started!</p>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-2 max-h-96 overflow-y-auto">
|
<div className="space-y-2 max-h-96 overflow-y-auto">
|
||||||
{games.map((game) => {
|
{games.map((game, index) => {
|
||||||
const index = games.length - games.indexOf(game);
|
const displayNumber = games.length - index;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={game.id}
|
key={game.id}
|
||||||
@@ -836,7 +849,7 @@ function SessionInfo({ sessionId, onGamesUpdate }) {
|
|||||||
? 'text-gray-500 dark:text-gray-500 line-through'
|
? 'text-gray-500 dark:text-gray-500 line-through'
|
||||||
: 'text-gray-700 dark:text-gray-200'
|
: 'text-gray-700 dark:text-gray-200'
|
||||||
}`}>
|
}`}>
|
||||||
{index + 1}. {game.title}
|
{displayNumber}. {game.title}
|
||||||
</span>
|
</span>
|
||||||
{getStatusBadge(game.status)}
|
{getStatusBadge(game.status)}
|
||||||
{game.manually_added === 1 && (
|
{game.manually_added === 1 && (
|
||||||
|
|||||||
Reference in New Issue
Block a user