122 lines
3.8 KiB
TypeScript
122 lines
3.8 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react';
|
|
import PartySocket from 'partysocket';
|
|
import { GameState } from '../types';
|
|
import { useAuth, useUser } from '@clerk/clerk-react';
|
|
|
|
const PARTY_HOST = import.meta.env.DEV ? 'localhost:1999' : 'bozo-clicker.your-username.partykit.dev';
|
|
|
|
export function usePartyKit() {
|
|
const { getToken, isLoaded, isSignedIn } = useAuth();
|
|
const { user } = useUser();
|
|
const [gameState, setGameState] = useState<GameState | null>(null);
|
|
const [socket, setSocket] = useState<PartySocket | null>(null);
|
|
// Generate a persistent guest ID if the user is not signed in
|
|
const [guestId] = useState(() => {
|
|
let storedGuestId = localStorage.getItem('bozo_guest_id');
|
|
if (!storedGuestId) {
|
|
storedGuestId = `guest_${Math.random().toString(36).substring(2, 15)}`;
|
|
localStorage.setItem('bozo_guest_id', storedGuestId);
|
|
}
|
|
return storedGuestId;
|
|
});
|
|
const [guestName] = useState(() => {
|
|
let storedGuestName = localStorage.getItem('bozo_guest_name');
|
|
if (!storedGuestName) {
|
|
storedGuestName = `Guest${Math.floor(Math.random() * 10000)}`;
|
|
localStorage.setItem('bozo_guest_name', storedGuestName);
|
|
}
|
|
return storedGuestName;
|
|
});
|
|
|
|
useEffect(() => {
|
|
// Always attempt to connect the socket
|
|
const ws = new PartySocket({
|
|
host: PARTY_HOST,
|
|
room: 'bozo-clicker'
|
|
});
|
|
|
|
ws.onopen = async () => {
|
|
if (isLoaded && isSignedIn && user) {
|
|
const token = await getToken();
|
|
ws.send(JSON.stringify({
|
|
type: 'user-join',
|
|
userId: user.id,
|
|
userName: user.username || user.fullName || user.emailAddresses[0].emailAddress,
|
|
token
|
|
}));
|
|
} else {
|
|
// If not signed in, send a generic user-join message for guest
|
|
ws.send(JSON.stringify({
|
|
type: 'user-join',
|
|
userId: guestId,
|
|
userName: guestName
|
|
}));
|
|
}
|
|
};
|
|
|
|
ws.onmessage = (event) => {
|
|
const data = JSON.parse(event.data);
|
|
if (data.type === 'game-state') {
|
|
setGameState(data.state);
|
|
}
|
|
};
|
|
|
|
setSocket(ws);
|
|
|
|
return () => {
|
|
ws.close();
|
|
};
|
|
}, [isLoaded, guestId, guestName]); // Removed isSignedIn, user, getToken from dependencies
|
|
|
|
const sendClick = useCallback(async () => {
|
|
if (socket && isSignedIn && user) {
|
|
const token = await getToken();
|
|
socket.send(JSON.stringify({
|
|
type: 'click',
|
|
userId: user.id,
|
|
userName: user.username || user.fullName || user.emailAddresses[0].emailAddress,
|
|
token
|
|
}));
|
|
}
|
|
}, [socket, isSignedIn, user, getToken]);
|
|
|
|
const purchaseUpgrade = useCallback(async (upgradeId: string) => {
|
|
if (socket && isSignedIn && user) {
|
|
const token = await getToken();
|
|
socket.send(JSON.stringify({
|
|
type: 'purchase-upgrade',
|
|
userId: user.id,
|
|
upgradeId,
|
|
token
|
|
}));
|
|
}
|
|
}, [socket, isSignedIn, user, getToken]);
|
|
|
|
const sendMascotClickBonus = useCallback(async (multiplierBonus: number) => {
|
|
if (socket && isSignedIn && user) {
|
|
const token = await getToken();
|
|
socket.send(JSON.stringify({
|
|
type: 'apply-multiplier-bonus',
|
|
userId: user.id,
|
|
userName: user.username || user.fullName || user.emailAddresses[0].emailAddress,
|
|
token,
|
|
multiplierBonus
|
|
}));
|
|
}
|
|
}, [socket, isSignedIn, user, getToken]);
|
|
|
|
// Determine the userId and userName to expose based on sign-in status
|
|
// Only expose Clerk user ID/name if signed in, otherwise undefined
|
|
const currentUserId = isSignedIn && user ? user.id : undefined;
|
|
const currentUserName = isSignedIn && user ? (user.username || user.fullName || user.emailAddresses[0].emailAddress) : undefined;
|
|
|
|
return {
|
|
gameState,
|
|
sendClick,
|
|
purchaseUpgrade,
|
|
sendMascotClickBonus,
|
|
userId: currentUserId,
|
|
userName: currentUserName
|
|
};
|
|
}
|