Added matches scrollable to home page #96
@@ -23,6 +23,11 @@ const gObserverTarget = ref(null);
|
||||
const gObserver = ref(null);
|
||||
const gLoading = ref(false);
|
||||
const gPage = ref(1);
|
||||
const openMatches = ref([])
|
||||
const mObserverTarget = ref(null);
|
||||
const mObserver = ref(null);
|
||||
const mLoading = ref(false);
|
||||
const mPage = ref(1);
|
||||
const showHostModal = ref(false)
|
||||
const showJoinModal = ref(false)
|
||||
const selectedGame = ref(null)
|
||||
@@ -38,6 +43,21 @@ const lbObserverTarget = ref(null)
|
||||
const lbObserver = ref(null)
|
||||
const lbMorePages = ref(true)
|
||||
const publicStats = ref(null)
|
||||
const isLoggedIn = useAuthStore().isLoggedIn
|
||||
|
||||
const setupMatchesObserver = () => {
|
||||
if (mObserver.value) mObserver.value.disconnect();
|
||||
|
||||
mObserver.value = new IntersectionObserver((entries) => {
|
||||
if (entries[0].isIntersecting && !mLoading.value && openMatches.value.length > 0) {
|
||||
getPendingMatches();
|
||||
}
|
||||
}, { threshold: 0.1 });
|
||||
|
||||
if (mObserverTarget.value) {
|
||||
mObserver.value.observe(mObserverTarget.value);
|
||||
}
|
||||
}
|
||||
|
||||
const setupGamesObserver = () => {
|
||||
if (gObserver.value) gObserver.value.disconnect();
|
||||
@@ -67,14 +87,30 @@ const getPendingGames = async (mode = selectedMode.value) => {
|
||||
|
||||
const newGames = response.data.data;
|
||||
openGames.value = [...openGames.value, ...newGames];
|
||||
gPage.value++;
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch games:', error);
|
||||
} finally {
|
||||
gLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const getPendingMatches = async (mode = selectedMode.value) => {
|
||||
if (mLoading.value) return;
|
||||
mLoading.value = true;
|
||||
|
||||
try {
|
||||
const response = await axios.get(`${API_BASE_URL}/matches/open`, {
|
||||
params: {
|
||||
type: mode,
|
||||
page: mPage.value
|
||||
}
|
||||
});
|
||||
|
||||
const newMatches = response.data.data;
|
||||
openMatches.value = [...openMatches.value, ...newMatches];
|
||||
} finally {
|
||||
mLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const fetchPublicStats = async () => {
|
||||
try {
|
||||
const response = await apiStore.getPublicStats()
|
||||
@@ -145,81 +181,77 @@ const setupLeaderboardObserver = () => {
|
||||
|
||||
const clickMode = async (mode) => {
|
||||
if (selectedMode.value === mode) return;
|
||||
openGames.value = [];
|
||||
selectedMode.value = mode;
|
||||
openGames.value = [];
|
||||
gPage.value = 1;
|
||||
await getPendingGames();
|
||||
openMatches.value = [];
|
||||
mPage.value = 1;
|
||||
await Promise.all([getPendingGames(), getPendingMatches()]);
|
||||
setupGamesObserver();
|
||||
setupMatchesObserver();
|
||||
}
|
||||
|
||||
const startSingle = () => {
|
||||
router.push({ name: 'bisca' + selectedMode.value });
|
||||
}
|
||||
|
||||
const JoinGameModal = (game) => {
|
||||
selectedGame.value = game
|
||||
isReady.value = false
|
||||
showJoinModal.value = true
|
||||
}
|
||||
|
||||
const hostGameModal = () => {
|
||||
showHostModal.value = true
|
||||
}
|
||||
|
||||
const toggleReady = () => {
|
||||
isReady.value = !isReady.value
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchPublicStats();
|
||||
await getPendingGames();
|
||||
await nextTick();
|
||||
setupGamesObserver();
|
||||
if (isLoggedIn) {
|
||||
await Promise.all([getPendingGames(), getPendingMatches()]);
|
||||
await nextTick();
|
||||
setupGamesObserver();
|
||||
setupMatchesObserver();
|
||||
}
|
||||
})
|
||||
|
||||
const hostGame = async () => {
|
||||
showHostModal.value = true
|
||||
showHostModal.value = true
|
||||
|
||||
try {
|
||||
const response = await axios.post(`${API_BASE_URL}/games/host`, {
|
||||
type: selectedMode.value
|
||||
})
|
||||
try {
|
||||
const response = await axios.post(`${API_BASE_URL}/games/host`, {
|
||||
type: selectedMode.value
|
||||
})
|
||||
|
||||
const gameId = response.data.id
|
||||
const gameId = response.data.id
|
||||
|
||||
console.log('Game hosted:', gameId)
|
||||
console.log('Game hosted:', gameId)
|
||||
|
||||
router.push({
|
||||
name: 'multiplayer-game',
|
||||
params: { id: gameId },
|
||||
query: { type: selectedMode.value }
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to host game:', error)
|
||||
console.error('Error details:', error.response?.data)
|
||||
showHostModal.value = false
|
||||
alert('Failed to host game: ' + (error.response?.data?.message || error.message))
|
||||
}
|
||||
router.push({
|
||||
name: 'multiplayer-game',
|
||||
params: { id: gameId },
|
||||
query: { type: selectedMode.value }
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to host game:', error)
|
||||
console.error('Error details:', error.response?.data)
|
||||
showHostModal.value = false
|
||||
alert('Failed to host game: ' + (error.response?.data?.message || error.message))
|
||||
}
|
||||
}
|
||||
|
||||
const joinGame = async (game) => {
|
||||
selectedGame.value = game
|
||||
selectedGame.value = game
|
||||
|
||||
try {
|
||||
await axios.post(`${API_BASE_URL}/games/${game.id}/join`)
|
||||
try {
|
||||
await axios.post(`${API_BASE_URL}/games/${game.id}/join`)
|
||||
|
||||
console.log('Joined game:', game.id)
|
||||
console.log('Joined game:', game.id)
|
||||
|
||||
router.push({
|
||||
name: 'multiplayer-game',
|
||||
params: { id: game.id },
|
||||
query: { type: selectedMode.value }
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to join game:', error)
|
||||
console.error('Error details:', error.response?.data)
|
||||
alert('Failed to join game: ' + (error.response?.data?.message || error.message))
|
||||
}
|
||||
router.push({
|
||||
name: 'multiplayer-game',
|
||||
params: { id: game.id },
|
||||
query: { type: selectedMode.value }
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to join game:', error)
|
||||
console.error('Error details:', error.response?.data)
|
||||
alert('Failed to join game: ' + (error.response?.data?.message || error.message))
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -227,7 +259,7 @@ const joinGame = async (game) => {
|
||||
<template>
|
||||
<div class="min-h-screen bg-cover bg-center transition-all duration-700">
|
||||
|
||||
<div class="flex flex-col justify-center items-center gap-5 pt-10 px-4">
|
||||
<div class="flex flex-col justify-center items-center gap-5 pt-10 pb-10 px-4">
|
||||
|
||||
<Card v-if="publicStats" class="w-full max-w-2xl border-[#a855f7] bg-[#f8f0ff] shadow-sm">
|
||||
<CardContent class="p-4">
|
||||
@@ -293,50 +325,95 @@ const joinGame = async (game) => {
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card class="w-full max-w-2xl">
|
||||
<Card class="w-full max-w-2xl flex flex-col">
|
||||
<CardHeader>
|
||||
<CardTitle class="text-3xl font-bold text-center">
|
||||
Multi Player
|
||||
</CardTitle>
|
||||
<CardDescription class="text-center">
|
||||
Go one on one with another player!
|
||||
</CardDescription>
|
||||
<CardTitle class="text-3xl font-bold text-center">Multi Player</CardTitle>
|
||||
<CardDescription class="text-center">Go one on one with another player!</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-6">
|
||||
<label class="text-sm font-medium">Open games (oldest first)</label>
|
||||
<div class="rounded-lg border bg-card text-card-foreground shadow-sm">
|
||||
<div v-if="openGames.length === 0" class="p-6 text-center text-sm text-muted-foreground">
|
||||
No open games yet. Try hosting one!
|
||||
|
||||
<CardContent class="flex-1 flex flex-col space-y-6">
|
||||
<div class="flex flex-col flex-1">
|
||||
<div class="flex justify-between items-end mb-2">
|
||||
<label class="text-sm font-bold uppercase tracking-wider text-muted-foreground">Open
|
||||
Matches</label>
|
||||
<Button v-if="isLoggedIn" @click="showHostModal()" size="sm" variant="outline"
|
||||
class="border-purple-500 text-purple-500 hover:bg-purple-500 hover:text-white transition-all duration-300 font-bold">
|
||||
Host
|
||||
</Button>
|
||||
</div>
|
||||
<div v-else class="max-h-[350px] overflow-y-auto custom-scrollbar p-1">
|
||||
|
||||
<div v-for="(game, index) in openGames" :key="game.id" @click="joinGame(game)"
|
||||
class="flex items-center justify-between p-3 mb-2 border border-transparent rounded-lg transition-all hover:bg-muted/50 hover:border-purple-500">
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="flex h-8 w-8 items-center justify-center rounded-full text-xs font-bold bg-green-100 text-green-700">
|
||||
{{ index + 1 }}
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-medium text-sm">Started: {{ game.began_at }}</div>
|
||||
<div class="text-xs text-muted-foreground">host: {{
|
||||
game.player1?.nickname || 'Anonymous' }}</div>
|
||||
<div class="rounded-lg border bg-card text-card-foreground shadow-sm">
|
||||
<div v-if="openGames.length === 0"
|
||||
class="p-6 text-center text-sm text-muted-foreground">
|
||||
No open macthes yet. Try hosting one!
|
||||
</div>
|
||||
<div v-else class="max-h-[350px] overflow-y-auto custom-scrollbar p-1">
|
||||
<div v-for="(match, index) in openMatches" :key="match.id"
|
||||
class="flex items-center justify-between p-3 mb-2 border border-transparent rounded-lg cursor-pointer transition-all hover:bg-muted/50 hover:border-purple-500">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="flex h-8 w-8 items-center justify-center rounded-full text-xs font-bold bg-red-100 text-red-700">
|
||||
{{ index + 1 }}
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-medium text-sm">Match ID: {{ match.id }}</div>
|
||||
<div class="text-[10px] text-muted-foreground">{{ new
|
||||
Date(game.began_at).toLocaleDateString() }} • {{ new
|
||||
Date(game.began_at).toLocaleTimeString([], {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit' }) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ref="gObserverTarget" class="h-10 flex items-center justify-center">
|
||||
<span v-if="gLoading" class="text-xs text-purple-500 animate-pulse">Loading
|
||||
more...</span>
|
||||
<div ref="mObserverTarget" class="h-10 flex items-center justify-center">
|
||||
<span v-if="mLoading" class="text-xs text-purple-500 animate-pulse">Loading
|
||||
matches...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<Button @click="hostGame()" size="lg" variant="secondary"
|
||||
class="hover:bg-purple-500 hover:text-slate-200">
|
||||
Host Game
|
||||
</Button>
|
||||
|
||||
<div class="flex flex-col flex-1">
|
||||
<div class="flex justify-between items-end mb-2">
|
||||
<label class="text-sm font-medium uppercase tracking-wider text-muted-foreground">Open
|
||||
Games</label>
|
||||
<Button v-if="isLoggedIn" @click="hostGame()" size="sm" variant="outline"
|
||||
class="border-purple-500 text-purple-500 hover:bg-purple-500 hover:text-white transition-all duration-300 font-bold">
|
||||
Host
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border bg-card text-card-foreground shadow-sm">
|
||||
<div v-if="openGames.length === 0"
|
||||
class="p-6 text-center text-sm text-muted-foreground">
|
||||
No open games yet. Try hosting one!
|
||||
</div>
|
||||
<div v-else class="max-h-[350px] overflow-y-auto custom-scrollbar p-1">
|
||||
<div v-for="(game, index) in openGames" :key="game.id" @click="joinGame(game)"
|
||||
class="flex items-center justify-between p-3 mb-2 border border-transparent rounded-lg cursor-pointer transition-all hover:bg-muted/50 hover:border-purple-500">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="flex h-8 w-8 items-center justify-center rounded-full text-xs font-bold bg-green-100 text-green-700">
|
||||
{{ index + 1 }}
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-medium text-sm">Host: {{ game.player1?.nickname ||
|
||||
'Anonymous' }}</div>
|
||||
<div class="text-[10px] text-muted-foreground">{{ new
|
||||
Date(game.began_at).toLocaleDateString() }} • {{ new
|
||||
Date(game.began_at).toLocaleTimeString([], {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit' }) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="gObserverTarget" class="h-10 flex items-center justify-center">
|
||||
<span v-if="gLoading" class="text-xs text-purple-500 animate-pulse">Loading
|
||||
games...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user