clerk auth fixed
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -23,3 +23,4 @@ dist-ssr
|
|||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
.env
|
.env
|
||||||
|
.partykit/
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
48
package-lock.json
generated
48
package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"name": "vite-react-typescript-starter",
|
"name": "vite-react-typescript-starter",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@clerk/backend": "^2.6.2",
|
||||||
"@clerk/clerk-react": "^5.38.1",
|
"@clerk/clerk-react": "^5.38.1",
|
||||||
"lucide-react": "^0.344.0",
|
"lucide-react": "^0.344.0",
|
||||||
"partykit": "^0.0.115",
|
"partykit": "^0.0.115",
|
||||||
@@ -348,6 +349,31 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@clerk/backend": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-2.6.2.tgz",
|
||||||
|
"integrity": "sha512-IUTjLmA1QkqoJnB97S8Ay/oeFR1QtBxxzi9V2J8zncGdUUpAHRp9PfbUwe203VEZuoDD8n6PGfK4oiiq5CoKhQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@clerk/shared": "^3.17.0",
|
||||||
|
"@clerk/types": "^4.72.0",
|
||||||
|
"cookie": "1.0.2",
|
||||||
|
"standardwebhooks": "^1.0.0",
|
||||||
|
"tslib": "2.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.17.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@clerk/backend/node_modules/cookie": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@clerk/clerk-react": {
|
"node_modules/@clerk/clerk-react": {
|
||||||
"version": "5.38.1",
|
"version": "5.38.1",
|
||||||
"resolved": "https://registry.npmjs.org/@clerk/clerk-react/-/clerk-react-5.38.1.tgz",
|
"resolved": "https://registry.npmjs.org/@clerk/clerk-react/-/clerk-react-5.38.1.tgz",
|
||||||
@@ -1358,6 +1384,12 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@stablelib/base64": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/babel__core": {
|
"node_modules/@types/babel__core": {
|
||||||
"version": "7.20.5",
|
"version": "7.20.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||||
@@ -2568,6 +2600,12 @@
|
|||||||
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
|
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-sha256": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==",
|
||||||
|
"license": "Unlicense"
|
||||||
|
},
|
||||||
"node_modules/fastq": {
|
"node_modules/fastq": {
|
||||||
"version": "1.17.1",
|
"version": "1.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
||||||
@@ -4041,6 +4079,16 @@
|
|||||||
"get-source": "^2.0.12"
|
"get-source": "^2.0.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/standardwebhooks": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@stablelib/base64": "^1.0.0",
|
||||||
|
"fast-sha256": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/std-env": {
|
"node_modules/std-env": {
|
||||||
"version": "3.9.0",
|
"version": "3.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@clerk/backend": "^2.6.2",
|
||||||
"@clerk/clerk-react": "^5.38.1",
|
"@clerk/clerk-react": "^5.38.1",
|
||||||
"lucide-react": "^0.344.0",
|
"lucide-react": "^0.344.0",
|
||||||
"partykit": "^0.0.115",
|
"partykit": "^0.0.115",
|
||||||
|
|||||||
122
party/index.ts
122
party/index.ts
@@ -1,5 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
import type * as Party from 'partykit/server';
|
import type * as Party from 'partykit/server';
|
||||||
|
import { createClerkClient, verifyToken } from '@clerk/backend';
|
||||||
|
|
||||||
interface GameState {
|
interface GameState {
|
||||||
totalClicks: number;
|
totalClicks: number;
|
||||||
@@ -12,22 +13,23 @@ interface GameState {
|
|||||||
currentClickImage: string;
|
currentClickImage: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ClickMessage {
|
interface AuthenticatedMessage {
|
||||||
type: 'click';
|
token: string;
|
||||||
userId: string;
|
userId: string; // Expected userId from client
|
||||||
userName: string;
|
userName: string; // Expected userName from client
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PurchaseUpgradeMessage {
|
interface ClickMessage extends AuthenticatedMessage {
|
||||||
|
type: 'click';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PurchaseUpgradeMessage extends AuthenticatedMessage {
|
||||||
type: 'purchase-upgrade';
|
type: 'purchase-upgrade';
|
||||||
userId: string;
|
|
||||||
upgradeId: string;
|
upgradeId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserJoinMessage {
|
interface UserJoinMessage extends AuthenticatedMessage {
|
||||||
type: 'user-join';
|
type: 'user-join';
|
||||||
userId: string;
|
|
||||||
userName: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Message = ClickMessage | PurchaseUpgradeMessage | UserJoinMessage;
|
type Message = ClickMessage | PurchaseUpgradeMessage | UserJoinMessage;
|
||||||
@@ -50,7 +52,13 @@ const MILESTONES = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default class GameServer implements Party.Server {
|
export default class GameServer implements Party.Server {
|
||||||
constructor(readonly party: Party.Party) { }
|
clerkClient: ReturnType<typeof createClerkClient>;
|
||||||
|
|
||||||
|
constructor(readonly party: Party.Party) {
|
||||||
|
this.clerkClient = createClerkClient({
|
||||||
|
secretKey: party.env.CLERK_SECRET_KEY as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
gameState: GameState = {
|
gameState: GameState = {
|
||||||
totalClicks: 0,
|
totalClicks: 0,
|
||||||
@@ -68,68 +76,104 @@ export default class GameServer implements Party.Server {
|
|||||||
|
|
||||||
autoClickInterval?: NodeJS.Timeout;
|
autoClickInterval?: NodeJS.Timeout;
|
||||||
|
|
||||||
onConnect(conn: Party.Connection, _ctx: Party.ConnectionContext) {
|
async onConnect(conn: Party.Connection, _ctx: Party.ConnectionContext) {
|
||||||
conn.send(JSON.stringify({ type: 'game-state', state: this.gameState }));
|
conn.send(JSON.stringify({ type: 'game-state', state: this.gameState }));
|
||||||
}
|
}
|
||||||
|
|
||||||
onMessage(message: string, _sender: Party.Connection) {
|
async onMessage(message: string, sender: Party.Connection) {
|
||||||
const data = JSON.parse(message) as Message;
|
const data = JSON.parse(message) as Message;
|
||||||
|
|
||||||
switch (data.type) {
|
// Verify the Clerk token for authenticated messages
|
||||||
case 'user-join':
|
if ('token' in data && data.token) {
|
||||||
this.handleUserJoin(data);
|
try {
|
||||||
break;
|
const sessionClaims = await verifyToken(data.token, {
|
||||||
case 'click':
|
jwtKey: this.party.env.CLERK_JWT_KEY as string,
|
||||||
this.handleClick(data);
|
});
|
||||||
break;
|
const authenticatedUserId = sessionClaims.sub;
|
||||||
case 'purchase-upgrade':
|
|
||||||
this.handlePurchaseUpgrade(data);
|
// Ensure the userId from the client matches the authenticated userId
|
||||||
break;
|
if (authenticatedUserId !== data.userId) {
|
||||||
|
console.warn(`User ID mismatch: Client sent ${data.userId}, but token is for ${authenticatedUserId}`);
|
||||||
|
sender.close(); // Close connection for potential tampering
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update user info on the server with authenticated data
|
||||||
|
if (!this.gameState.users[authenticatedUserId]) {
|
||||||
|
this.gameState.users[authenticatedUserId] = {
|
||||||
|
name: data.userName, // Use the name provided by the client, which comes from Clerk
|
||||||
|
clicks: 0,
|
||||||
|
lastSeen: Date.now()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.gameState.users[authenticatedUserId].lastSeen = Date.now();
|
||||||
|
this.gameState.users[authenticatedUserId].name = data.userName; // Update name in case it changed
|
||||||
|
|
||||||
|
switch (data.type) {
|
||||||
|
case 'user-join':
|
||||||
|
// User join is handled by the token verification and user state update above
|
||||||
|
break;
|
||||||
|
case 'click':
|
||||||
|
this.handleClick(data, authenticatedUserId);
|
||||||
|
break;
|
||||||
|
case 'purchase-upgrade':
|
||||||
|
this.handlePurchaseUpgrade(data, authenticatedUserId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Clerk token verification failed:', error);
|
||||||
|
sender.close(); // Close connection if token is invalid
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For messages without a token (e.g., initial connection before token is sent, or unauthenticated actions)
|
||||||
|
// For this game, we only allow authenticated actions.
|
||||||
|
console.warn('Received unauthenticated message, closing connection.');
|
||||||
|
sender.close();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.broadcast();
|
this.broadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleUserJoin is now largely integrated into onMessage's token verification
|
||||||
|
// Keeping it for now, but it might become redundant or simplified.
|
||||||
handleUserJoin(data: UserJoinMessage) {
|
handleUserJoin(data: UserJoinMessage) {
|
||||||
if (!this.gameState.users[data.userId]) {
|
// This function is now mostly handled by the onMessage token verification logic
|
||||||
this.gameState.users[data.userId] = {
|
// It ensures the user exists in gameState.users and updates lastSeen/name.
|
||||||
name: data.userName,
|
// No explicit action needed here beyond what onMessage does.
|
||||||
clicks: 0,
|
|
||||||
lastSeen: Date.now()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
this.gameState.users[data.userId].lastSeen = Date.now();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick(data: ClickMessage) {
|
handleClick(data: ClickMessage, authenticatedUserId: string) {
|
||||||
const clickValue = this.gameState.clickMultiplier;
|
const clickValue = this.gameState.clickMultiplier;
|
||||||
|
|
||||||
this.gameState.totalClicks += clickValue;
|
this.gameState.totalClicks += clickValue;
|
||||||
|
|
||||||
if (!this.gameState.users[data.userId]) {
|
// Ensure user exists (should be handled by onMessage's token verification)
|
||||||
this.gameState.users[data.userId] = {
|
if (!this.gameState.users[authenticatedUserId]) {
|
||||||
name: data.userName,
|
this.gameState.users[authenticatedUserId] = {
|
||||||
|
name: data.userName, // Use the name from the client, which comes from Clerk
|
||||||
clicks: 0,
|
clicks: 0,
|
||||||
lastSeen: Date.now()
|
lastSeen: Date.now()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.gameState.users[data.userId].clicks += clickValue;
|
this.gameState.users[authenticatedUserId].clicks += clickValue;
|
||||||
this.gameState.users[data.userId].lastSeen = Date.now();
|
this.gameState.users[authenticatedUserId].lastSeen = Date.now();
|
||||||
|
|
||||||
this.checkMilestones();
|
this.checkMilestones();
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurchaseUpgrade(data: PurchaseUpgradeMessage) {
|
handlePurchaseUpgrade(data: PurchaseUpgradeMessage, authenticatedUserId: string) {
|
||||||
const upgrade = UPGRADES[data.upgradeId as keyof typeof UPGRADES];
|
const upgrade = UPGRADES[data.upgradeId as keyof typeof UPGRADES];
|
||||||
const currentUpgrade = this.gameState.upgrades[data.upgradeId];
|
const currentUpgrade = this.gameState.upgrades[data.upgradeId];
|
||||||
|
|
||||||
if (!upgrade || !currentUpgrade) return;
|
if (!upgrade || !currentUpgrade) return;
|
||||||
|
|
||||||
const userClicks = this.gameState.users[data.userId]?.clicks || 0;
|
const userClicks = this.gameState.users[authenticatedUserId]?.clicks || 0;
|
||||||
|
|
||||||
if (userClicks >= currentUpgrade.cost) {
|
if (userClicks >= currentUpgrade.cost) {
|
||||||
this.gameState.users[data.userId].clicks -= currentUpgrade.cost;
|
this.gameState.users[authenticatedUserId].clicks -= currentUpgrade.cost;
|
||||||
currentUpgrade.owned += 1;
|
currentUpgrade.owned += 1;
|
||||||
currentUpgrade.cost = Math.floor(upgrade.baseCost * Math.pow(upgrade.multiplier, currentUpgrade.owned));
|
currentUpgrade.cost = Math.floor(upgrade.baseCost * Math.pow(upgrade.multiplier, currentUpgrade.owned));
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"$schema": "https://www.partykit.io/schema.json",
|
||||||
"name": "bozo-clicker",
|
"name": "bozo-clicker",
|
||||||
"main": "party/index.ts"
|
"main": "party/index.ts"
|
||||||
}
|
}
|
||||||
@@ -8,8 +8,10 @@ import { Leaderboard } from './components/Leaderboard';
|
|||||||
import { Background } from './components/Background';
|
import { Background } from './components/Background';
|
||||||
import { MILESTONES } from './config/milestones';
|
import { MILESTONES } from './config/milestones';
|
||||||
import { SignedIn, SignedOut, SignInButton, UserButton } from '@clerk/clerk-react';
|
import { SignedIn, SignedOut, SignInButton, UserButton } from '@clerk/clerk-react';
|
||||||
|
import { useAuth } from '@clerk/clerk-react';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const { isSignedIn, isLoaded } = useAuth();
|
||||||
const { gameState, sendClick, purchaseUpgrade, userId } = usePartyKit();
|
const { gameState, sendClick, purchaseUpgrade, userId } = usePartyKit();
|
||||||
const [celebrationMessage, setCelebrationMessage] = useState<string | null>(null);
|
const [celebrationMessage, setCelebrationMessage] = useState<string | null>(null);
|
||||||
const [previousMilestones, setPreviousMilestones] = useState<Record<string, boolean>>({});
|
const [previousMilestones, setPreviousMilestones] = useState<Record<string, boolean>>({});
|
||||||
@@ -35,7 +37,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
}, [gameState, previousMilestones]);
|
}, [gameState, previousMilestones]);
|
||||||
|
|
||||||
if (!gameState) {
|
if (!isLoaded || !gameState) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-purple-900 to-pink-900">
|
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-purple-900 to-pink-900">
|
||||||
<div className="text-white text-2xl font-bold animate-pulse">
|
<div className="text-white text-2xl font-bold animate-pulse">
|
||||||
@@ -45,7 +47,8 @@ function App() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userClicks = gameState.users[userId]?.clicks || 0;
|
// Only calculate userClicks if userId is defined (i.e., user is signed in)
|
||||||
|
const userClicks = isSignedIn && userId ? gameState.users[userId]?.clicks || 0 : 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen relative overflow-x-hidden">
|
<div className="min-h-screen relative overflow-x-hidden">
|
||||||
@@ -99,7 +102,7 @@ function App() {
|
|||||||
{/* Right Column - Milestones and Leaderboard */}
|
{/* Right Column - Milestones and Leaderboard */}
|
||||||
<div className="lg:col-span-1 space-y-6">
|
<div className="lg:col-span-1 space-y-6">
|
||||||
<Milestones gameState={gameState} />
|
<Milestones gameState={gameState} />
|
||||||
<Leaderboard gameState={gameState} currentUserId={userId} />
|
<Leaderboard gameState={gameState} currentUserId={userId || ''} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,31 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import PartySocket from 'partysocket';
|
import PartySocket from 'partysocket';
|
||||||
import { GameState } from '../types';
|
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';
|
const PARTY_HOST = import.meta.env.DEV ? 'localhost:1999' : 'bozo-clicker.your-username.partykit.dev';
|
||||||
|
|
||||||
export function usePartyKit() {
|
export function usePartyKit() {
|
||||||
|
const { getToken, isLoaded, isSignedIn } = useAuth();
|
||||||
|
const { user } = useUser();
|
||||||
const [gameState, setGameState] = useState<GameState | null>(null);
|
const [gameState, setGameState] = useState<GameState | null>(null);
|
||||||
const [socket, setSocket] = useState<PartySocket | null>(null);
|
const [socket, setSocket] = useState<PartySocket | null>(null);
|
||||||
const [userId] = useState(() => Math.random().toString(36).substring(7));
|
|
||||||
const [userName] = useState(() => `Bozo${Math.floor(Math.random() * 1000)}`);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!isLoaded || !isSignedIn || !user) return;
|
||||||
|
|
||||||
const ws = new PartySocket({
|
const ws = new PartySocket({
|
||||||
host: PARTY_HOST,
|
host: PARTY_HOST,
|
||||||
room: 'bozo-clicker'
|
room: 'bozo-clicker'
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.onopen = () => {
|
ws.onopen = async () => {
|
||||||
|
const token = await getToken();
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
type: 'user-join',
|
type: 'user-join',
|
||||||
userId,
|
userId: user.id,
|
||||||
userName
|
userName: user.username || user.fullName || user.emailAddresses[0].emailAddress,
|
||||||
|
token
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -36,33 +41,37 @@ export function usePartyKit() {
|
|||||||
return () => {
|
return () => {
|
||||||
ws.close();
|
ws.close();
|
||||||
};
|
};
|
||||||
}, [userId, userName]);
|
}, [isLoaded, isSignedIn, user, getToken]);
|
||||||
|
|
||||||
const sendClick = useCallback(() => {
|
const sendClick = useCallback(async () => {
|
||||||
if (socket) {
|
if (socket && isSignedIn && user) {
|
||||||
|
const token = await getToken();
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
type: 'click',
|
type: 'click',
|
||||||
userId,
|
userId: user.id,
|
||||||
userName
|
userName: user.username || user.fullName || user.emailAddresses[0].emailAddress,
|
||||||
|
token
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [socket, userId, userName]);
|
}, [socket, isSignedIn, user, getToken]);
|
||||||
|
|
||||||
const purchaseUpgrade = useCallback((upgradeId: string) => {
|
const purchaseUpgrade = useCallback(async (upgradeId: string) => {
|
||||||
if (socket) {
|
if (socket && isSignedIn && user) {
|
||||||
|
const token = await getToken();
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
type: 'purchase-upgrade',
|
type: 'purchase-upgrade',
|
||||||
userId,
|
userId: user.id,
|
||||||
upgradeId
|
upgradeId,
|
||||||
|
token
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [socket, userId]);
|
}, [socket, isSignedIn, user, getToken]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
gameState,
|
gameState,
|
||||||
sendClick,
|
sendClick,
|
||||||
purchaseUpgrade,
|
purchaseUpgrade,
|
||||||
userId,
|
userId: user?.id,
|
||||||
userName
|
userName: user?.username || user?.fullName || user?.emailAddresses[0]?.emailAddress
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user