fix
This commit is contained in:
@@ -74,7 +74,14 @@ interface UserJoinMessage extends AuthenticatedMessage {
|
|||||||
type: 'user-join';
|
type: 'user-join';
|
||||||
}
|
}
|
||||||
|
|
||||||
type Message = ClickMessage | PurchaseUpgradeMessage | ApplyMultiplierBonusMessage | UserJoinMessage | AdminBroadcastMessage; // Updated Message type
|
interface EditUserMessage extends AuthenticatedMessage {
|
||||||
|
type: 'edit-user';
|
||||||
|
targetUserId: string;
|
||||||
|
clicks: number;
|
||||||
|
upgrades: Record<string, { owned: number; cost: number }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Message = ClickMessage | PurchaseUpgradeMessage | ApplyMultiplierBonusMessage | UserJoinMessage | AdminBroadcastMessage | EditUserMessage; // Updated Message type
|
||||||
|
|
||||||
const UPGRADES: Upgrade[] = [
|
const UPGRADES: Upgrade[] = [
|
||||||
{
|
{
|
||||||
@@ -369,11 +376,29 @@ export default class GameServer implements Party.Server {
|
|||||||
console.warn(`Unauthorized admin broadcast attempt from ${currentUserId}.`);
|
console.warn(`Unauthorized admin broadcast attempt from ${currentUserId}.`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'edit-user':
|
||||||
|
if (isAuthenticated && currentUserId === this.party.env.CLERK_ADMIN_USERID) {
|
||||||
|
this.handleEditUser(data);
|
||||||
|
} else {
|
||||||
|
console.warn(`Unauthorized edit user attempt from ${currentUserId}.`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.broadcast();
|
this.broadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleEditUser(data: EditUserMessage) {
|
||||||
|
const { targetUserId, clicks, upgrades } = data;
|
||||||
|
const userState = this.gameState.users[targetUserId];
|
||||||
|
|
||||||
|
if (userState) {
|
||||||
|
userState.clicks = clicks;
|
||||||
|
userState.upgrades = upgrades;
|
||||||
|
this.updateGameMultipliers(targetUserId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// handleUserJoin is now fully integrated into onMessage and can be removed or simplified.
|
// handleUserJoin is now fully integrated into onMessage and can be removed or simplified.
|
||||||
// Removing it as its logic is now directly in onMessage.
|
// Removing it as its logic is now directly in onMessage.
|
||||||
// handleUserJoin(data: UserJoinMessage) {
|
// handleUserJoin(data: UserJoinMessage) {
|
||||||
@@ -475,7 +500,7 @@ export default class GameServer implements Party.Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGameMultipliers(userId: string) {
|
updateGameMultipliers(userId: string, autoClickRateOverride?: number) {
|
||||||
const userState = this.gameState.users[userId];
|
const userState = this.gameState.users[userId];
|
||||||
if (!userState) return;
|
if (!userState) return;
|
||||||
|
|
||||||
@@ -494,6 +519,10 @@ export default class GameServer implements Party.Server {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (autoClickRateOverride !== undefined) {
|
||||||
|
userState.autoClickRate = autoClickRateOverride;
|
||||||
|
}
|
||||||
|
|
||||||
this.setupAutoClicker();
|
this.setupAutoClicker();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useUser } from '@clerk/clerk-react';
|
import { useUser } from '@clerk/clerk-react';
|
||||||
import { usePartyKit } from '../hooks/usePartyKit';
|
import { usePartyKit } from '../hooks/usePartyKit';
|
||||||
|
import { UserState } from '../types';
|
||||||
|
|
||||||
interface AdminMessage {
|
interface AdminMessage {
|
||||||
type: 'admin-message';
|
type: 'admin-message';
|
||||||
@@ -10,11 +11,15 @@ interface AdminMessage {
|
|||||||
|
|
||||||
const AdminPage: React.FC = () => {
|
const AdminPage: React.FC = () => {
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const { sendMessage, lastMessage, getToken } = usePartyKit();
|
const { sendMessage, lastMessage, getToken, gameState, editUser } = usePartyKit();
|
||||||
const [broadcastMessage, setBroadcastMessage] = useState('');
|
const [broadcastMessage, setBroadcastMessage] = useState('');
|
||||||
const [targetUserId, setTargetUserId] = useState('');
|
const [targetUserId, setTargetUserId] = useState('');
|
||||||
const [receivedMessages, setReceivedMessages] = useState<AdminMessage[]>([]);
|
const [receivedMessages, setReceivedMessages] = useState<AdminMessage[]>([]);
|
||||||
const [isAdmin, setIsAdmin] = useState(false);
|
const [isAdmin, setIsAdmin] = useState(false);
|
||||||
|
const [selectedUser, setSelectedUser] = useState<UserState & { id: string } | null>(null);
|
||||||
|
const [clicks, setClicks] = useState(0);
|
||||||
|
const [autoClickRate, setAutoClickRate] = useState(0);
|
||||||
|
const [upgrades, setUpgrades] = useState<Record<string, { owned: number; cost: number }>>({});
|
||||||
|
|
||||||
// Replace with your actual admin user ID from .env or similar
|
// Replace with your actual admin user ID from .env or similar
|
||||||
const CLERK_ADMIN_USERID = import.meta.env.VITE_CLERK_ADMIN_USERID;
|
const CLERK_ADMIN_USERID = import.meta.env.VITE_CLERK_ADMIN_USERID;
|
||||||
@@ -62,6 +67,12 @@ const AdminPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleEditUser = () => {
|
||||||
|
if (selectedUser) {
|
||||||
|
editUser(selectedUser.id, clicks, autoClickRate, upgrades);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (!isAdmin) {
|
if (!isAdmin) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center min-h-screen bg-gray-900 text-white">
|
<div className="flex items-center justify-center min-h-screen bg-gray-900 text-white">
|
||||||
@@ -111,6 +122,80 @@ const AdminPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-6 bg-gray-800 rounded-lg shadow-lg">
|
<div className="p-6 bg-gray-800 rounded-lg shadow-lg">
|
||||||
|
<h2 className="text-2xl font-semibold mb-4">User Management</h2>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-xl font-semibold mb-4">Users</h3>
|
||||||
|
<div className="max-h-96 overflow-y-auto bg-gray-700 p-4 rounded-md">
|
||||||
|
<ul>
|
||||||
|
{gameState &&
|
||||||
|
Object.entries(gameState.users).map(([id, user]) => (
|
||||||
|
<li
|
||||||
|
key={id}
|
||||||
|
className={`cursor-pointer p-3 rounded-md mb-2 ${selectedUser?.id === id ? 'bg-blue-500' : 'bg-gray-600'}`}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedUser({ id, ...user });
|
||||||
|
setClicks(user.clicks);
|
||||||
|
setUpgrades(user.upgrades);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{user.name}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{selectedUser && (
|
||||||
|
<div>
|
||||||
|
<h3 className="text-xl font-semibold mb-4">Edit User</h3>
|
||||||
|
<div className="mb-4">
|
||||||
|
<label htmlFor="clicks" className="block text-lg font-medium mb-2">
|
||||||
|
Clicks:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="clicks"
|
||||||
|
className="w-full p-3 rounded-md bg-gray-700 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
value={clicks}
|
||||||
|
onChange={(e) => setClicks(Number(e.target.value))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-4">
|
||||||
|
<label htmlFor="autoClickRate" className="block text-lg font-medium mb-2">
|
||||||
|
Auto Click Rate:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="autoClickRate"
|
||||||
|
className="w-full p-3 rounded-md bg-gray-700 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
value={autoClickRate}
|
||||||
|
onChange={(e) => setAutoClickRate(Number(e.target.value))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-4">
|
||||||
|
<label htmlFor="upgrades" className="block text-lg font-medium mb-2">
|
||||||
|
Upgrades (JSON):
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="upgrades"
|
||||||
|
className="w-full p-3 rounded-md bg-gray-700 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
rows={10}
|
||||||
|
value={JSON.stringify(upgrades, null, 2)}
|
||||||
|
onChange={(e) => setUpgrades(JSON.parse(e.target.value))}
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={handleEditUser}
|
||||||
|
className="w-full bg-green-600 hover:bg-green-700 text-white font-bold py-3 px-6 rounded-md transition duration-300 ease-in-out"
|
||||||
|
>
|
||||||
|
Save Changes
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-6 bg-gray-800 rounded-lg shadow-lg mt-8">
|
||||||
<h2 className="text-2xl font-semibold mb-4">Received Admin Messages</h2>
|
<h2 className="text-2xl font-semibold mb-4">Received Admin Messages</h2>
|
||||||
<div className="max-h-64 overflow-y-auto bg-gray-700 p-4 rounded-md">
|
<div className="max-h-64 overflow-y-auto bg-gray-700 p-4 rounded-md">
|
||||||
{receivedMessages.length === 0 ? (
|
{receivedMessages.length === 0 ? (
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import PartySocket from 'partysocket';
|
|||||||
import { GameState } from '../types';
|
import { GameState } from '../types';
|
||||||
import { useAuth, useUser } from '@clerk/clerk-react';
|
import { useAuth, useUser } from '@clerk/clerk-react';
|
||||||
|
|
||||||
const PARTY_HOST = import.meta.env.DEV ? 'localhost:1999' : 'bozo-clicker.arjunindia.partykit.dev';
|
const PARTY_HOST = import.meta.env.DEV ? 'https://solid-rotary-phone-pg69xw5vj7rc7r55-1999.app.github.dev' : 'bozo-clicker.arjunindia.partykit.dev';
|
||||||
|
|
||||||
export function usePartyKit() {
|
export function usePartyKit() {
|
||||||
const { getToken, isLoaded, isSignedIn } = useAuth();
|
const { getToken, isLoaded, isSignedIn } = useAuth();
|
||||||
@@ -120,6 +120,22 @@ export function usePartyKit() {
|
|||||||
const currentUserId = isSignedIn && user ? user.id : undefined;
|
const currentUserId = isSignedIn && user ? user.id : undefined;
|
||||||
const currentUserName = isSignedIn && user ? (user.username || user.fullName || user.emailAddresses[0].emailAddress) : undefined;
|
const currentUserName = isSignedIn && user ? (user.username || user.fullName || user.emailAddresses[0].emailAddress) : undefined;
|
||||||
|
|
||||||
|
const editUser = useCallback(async (targetUserId: string, clicks: number, autoClickRate: number, upgrades: any) => {
|
||||||
|
if (socket && isSignedIn && user) {
|
||||||
|
const token = await getToken();
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
type: 'edit-user',
|
||||||
|
token,
|
||||||
|
userId: user.id,
|
||||||
|
userName: user.username || user.fullName || user.emailAddresses[0].emailAddress,
|
||||||
|
targetUserId,
|
||||||
|
clicks,
|
||||||
|
autoClickRate,
|
||||||
|
upgrades
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [socket, isSignedIn, user, getToken]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
gameState,
|
gameState,
|
||||||
sendClick,
|
sendClick,
|
||||||
@@ -129,6 +145,7 @@ export function usePartyKit() {
|
|||||||
lastMessage, // Expose lastMessage
|
lastMessage, // Expose lastMessage
|
||||||
userId: currentUserId,
|
userId: currentUserId,
|
||||||
userName: currentUserName,
|
userName: currentUserName,
|
||||||
getToken // Expose getToken
|
getToken, // Expose getToken
|
||||||
|
editUser,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export interface Upgrade {
|
|||||||
mascotTiers?: MascotTier[]; // New: for defining mascot tiers for friendBoost
|
mascotTiers?: MascotTier[]; // New: for defining mascot tiers for friendBoost
|
||||||
oneTime?: boolean; // New: Indicates if the upgrade is a one-time purchase
|
oneTime?: boolean; // New: Indicates if the upgrade is a one-time purchase
|
||||||
newsTitles?: string[]; // New: Array of news titles for the news upgrade
|
newsTitles?: string[]; // New: Array of news titles for the news upgrade
|
||||||
|
youtubeId?: string; // New: YouTube ID for the upgrade
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MascotTier {
|
export interface MascotTier {
|
||||||
|
|||||||
Reference in New Issue
Block a user