Live location tracking and playback for the game "manhunt"
at ben/frontend 135 lines 4.1 kB view raw
1import React, { useCallback, useEffect, useState } from "react"; 2import "@fontsource/bungee"; 3import { 4 IconBuildingBroadcastTowerFilled, 5 IconHexagonPlusFilled, 6 IconClockFilled 7} from "@tabler/icons-react"; 8import { commands, GameSettings, PlayerProfile } from "@/bindings"; 9import ProfilePicture from "./ProfilePicture"; 10import LoadingCover from "./LoadingCover"; 11import { message } from "@tauri-apps/plugin-dialog"; 12 13export const defaultSettings: () => GameSettings = () => ({ 14 random_seed: Math.floor(Math.random() * 2 ** 32), 15 hiding_time_seconds: 60 * 5, 16 ping_start: "Instant", 17 ping_minutes_interval: 10, 18 powerup_start: "Instant", 19 powerup_chance: 60, 20 powerup_minutes_cooldown: 1, 21 powerup_locations: [] 22}); 23 24const defaultProfile: PlayerProfile = { 25 display_name: "", 26 pfp_base64: null 27}; 28 29export default function MenuScreen() { 30 const [profile, setProfile] = useState<PlayerProfile>(defaultProfile); 31 const [loadingCover, setLoadingCover] = useState<boolean>(false); 32 33 useEffect(() => { 34 let cancel = false; 35 commands.getProfile().then((profile) => { 36 if (!cancel) { 37 setProfile(profile); 38 } 39 }); 40 return () => { 41 cancel = true; 42 }; 43 }, [setProfile]); 44 45 const startLobby = () => { 46 setLoadingCover(true); 47 setTimeout(() => { 48 commands.startLobby(null, defaultSettings()).catch(() => { 49 setLoadingCover(false); 50 }); 51 }, 100); 52 }; 53 54 const joinLobby = () => { 55 setLoadingCover(true); 56 const code = window.prompt("Enter join code"); 57 if (!code) { 58 setLoadingCover(false); 59 return; 60 } 61 const cleanedCode = code.toUpperCase().trim(); 62 commands 63 .checkRoomCode(cleanedCode) 64 .then((valid) => { 65 if (valid) { 66 commands.startLobby(cleanedCode, defaultSettings()).catch(() => { 67 setLoadingCover(false); 68 }); 69 } else { 70 message("Invalid Join Code", { kind: "error", title: "Failed to Join" }); 71 setLoadingCover(false); 72 } 73 }) 74 .catch(() => { 75 setLoadingCover(false); 76 }); 77 }; 78 79 const onEditName = () => { 80 const newName = window.prompt("Enter New Name"); 81 if (!newName) { 82 return; 83 } 84 85 const newProfile = { ...profile, display_name: newName }; 86 commands.updateProfile(newProfile); 87 setProfile(newProfile); 88 }; 89 90 const onEditPicture = () => { 91 setLoadingCover(true); 92 commands 93 .createProfilePicture() 94 .then((newPic) => { 95 if (newPic) { 96 const newProfile = { ...profile, pfp_base64: newPic }; 97 commands.updateProfile(newProfile); 98 setProfile(newProfile); 99 } 100 }) 101 .finally(() => { 102 setLoadingCover(false); 103 }); 104 }; 105 106 return ( 107 <> 108 <header> 109 <ProfilePicture 110 onClick={onEditPicture} 111 fallbackName={profile.display_name} 112 src={profile.pfp_base64} 113 /> 114 <span className="grow" onClick={onEditName}> 115 Hello,&nbsp;&nbsp;{profile.display_name} 116 </span> 117 </header> 118 <main className="menu"> 119 <button onClick={startLobby}> 120 <IconBuildingBroadcastTowerFilled size="5em" /> 121 Start Lobby 122 </button> 123 <button onClick={joinLobby}> 124 <IconHexagonPlusFilled size="2.5em" /> 125 Join Lobby 126 </button> 127 <button> 128 <IconClockFilled size="1.5em" /> 129 Past Games 130 </button> 131 </main> 132 <LoadingCover show={loadingCover} /> 133 </> 134 ); 135}