add local storage, change alerts, add share functionality
This commit is contained in:
parent
26df4881ea
commit
0ad35f44d6
6 changed files with 106 additions and 8 deletions
25
src/App.tsx
25
src/App.tsx
|
@ -7,9 +7,15 @@ import { AboutModal } from "./components/modals/AboutModal";
|
||||||
import { InfoModal } from "./components/modals/InfoModal";
|
import { InfoModal } from "./components/modals/InfoModal";
|
||||||
import { WinModal } from "./components/modals/WinModal";
|
import { WinModal } from "./components/modals/WinModal";
|
||||||
import { isWordInWordList, isWinningWord, solution } from "./lib/words";
|
import { isWordInWordList, isWinningWord, solution } from "./lib/words";
|
||||||
|
import {
|
||||||
|
loadGameStateFromLocalStorage,
|
||||||
|
saveGameStateToLocalStorage,
|
||||||
|
} from "./lib/localStorage";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [guesses, setGuesses] = useState<string[]>([]);
|
const [guesses, setGuesses] = useState<string[]>(
|
||||||
|
loadGameStateFromLocalStorage()?.guesses || []
|
||||||
|
);
|
||||||
const [currentGuess, setCurrentGuess] = useState("");
|
const [currentGuess, setCurrentGuess] = useState("");
|
||||||
const [isGameWon, setIsGameWon] = useState(false);
|
const [isGameWon, setIsGameWon] = useState(false);
|
||||||
const [isWinModalOpen, setIsWinModalOpen] = useState(false);
|
const [isWinModalOpen, setIsWinModalOpen] = useState(false);
|
||||||
|
@ -17,6 +23,11 @@ function App() {
|
||||||
const [isAboutModalOpen, setIsAboutModalOpen] = useState(false);
|
const [isAboutModalOpen, setIsAboutModalOpen] = useState(false);
|
||||||
const [isWordNotFoundAlertOpen, setIsWordNotFoundAlertOpen] = useState(false);
|
const [isWordNotFoundAlertOpen, setIsWordNotFoundAlertOpen] = useState(false);
|
||||||
const [isGameLost, setIsGameLost] = useState(false);
|
const [isGameLost, setIsGameLost] = useState(false);
|
||||||
|
const [shareComplete, setShareComplete] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
saveGameStateToLocalStorage(guesses);
|
||||||
|
}, [guesses]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isGameWon) {
|
if (isGameWon) {
|
||||||
|
@ -68,6 +79,11 @@ function App() {
|
||||||
message={`You lost, the word was ${solution}`}
|
message={`You lost, the word was ${solution}`}
|
||||||
isOpen={isGameLost}
|
isOpen={isGameLost}
|
||||||
/>
|
/>
|
||||||
|
<Alert
|
||||||
|
message="Game copied to clipboard"
|
||||||
|
isOpen={shareComplete}
|
||||||
|
variant="success"
|
||||||
|
/>
|
||||||
<div className="flex w-80 mx-auto items-center mb-8">
|
<div className="flex w-80 mx-auto items-center mb-8">
|
||||||
<h1 className="text-xl grow font-bold">Not Wordle</h1>
|
<h1 className="text-xl grow font-bold">Not Wordle</h1>
|
||||||
<InformationCircleIcon
|
<InformationCircleIcon
|
||||||
|
@ -86,6 +102,13 @@ function App() {
|
||||||
isOpen={isWinModalOpen}
|
isOpen={isWinModalOpen}
|
||||||
handleClose={() => setIsWinModalOpen(false)}
|
handleClose={() => setIsWinModalOpen(false)}
|
||||||
guesses={guesses}
|
guesses={guesses}
|
||||||
|
handleShare={() => {
|
||||||
|
setIsWinModalOpen(false);
|
||||||
|
setShareComplete(true);
|
||||||
|
return setTimeout(() => {
|
||||||
|
setShareComplete(false);
|
||||||
|
}, 2000);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<InfoModal
|
<InfoModal
|
||||||
isOpen={isInfoModalOpen}
|
isOpen={isInfoModalOpen}
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import { Transition } from "@headlessui/react";
|
import { Transition } from "@headlessui/react";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
|
variant?: "success" | "warning";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Alert = ({ isOpen, message }: Props) => {
|
export const Alert = ({ isOpen, message, variant = "warning" }: Props) => {
|
||||||
|
const classes = classNames(
|
||||||
|
"fixed top-2.5 left-1/2 transform -translate-x-1/2 max-w-sm w-full shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden",
|
||||||
|
{
|
||||||
|
"bg-rose-200": variant === "warning",
|
||||||
|
"bg-green-200": variant === "success",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Transition
|
<Transition
|
||||||
show={isOpen}
|
show={isOpen}
|
||||||
|
@ -18,7 +28,7 @@ export const Alert = ({ isOpen, message }: Props) => {
|
||||||
leaveFrom="opacity-100"
|
leaveFrom="opacity-100"
|
||||||
leaveTo="opacity-0"
|
leaveTo="opacity-0"
|
||||||
>
|
>
|
||||||
<div className="absolute top-2.5 left-1/2 transform -translate-x-1/2 max-w-sm w-full bg-rose-200 shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden">
|
<div className={classes}>
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<p className="text-sm text-center font-medium text-gray-900">
|
<p className="text-sm text-center font-medium text-gray-900">
|
||||||
{message}
|
{message}
|
||||||
|
|
|
@ -2,14 +2,21 @@ import { Fragment } from "react";
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
import { CheckIcon } from "@heroicons/react/outline";
|
import { CheckIcon } from "@heroicons/react/outline";
|
||||||
import { MiniGrid } from "../mini-grid/MiniGrid";
|
import { MiniGrid } from "../mini-grid/MiniGrid";
|
||||||
|
import { shareStatus } from "../../lib/share";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
guesses: string[];
|
guesses: string[];
|
||||||
|
handleShare: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WinModal = ({ isOpen, handleClose, guesses }: Props) => {
|
export const WinModal = ({
|
||||||
|
isOpen,
|
||||||
|
handleClose,
|
||||||
|
guesses,
|
||||||
|
handleShare,
|
||||||
|
}: Props) => {
|
||||||
return (
|
return (
|
||||||
<Transition.Root show={isOpen} as={Fragment}>
|
<Transition.Root show={isOpen} as={Fragment}>
|
||||||
<Dialog
|
<Dialog
|
||||||
|
@ -71,9 +78,12 @@ export const WinModal = ({ isOpen, handleClose, guesses }: Props) => {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex justify-center w-full rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:text-sm"
|
className="inline-flex justify-center w-full rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:text-sm"
|
||||||
onClick={handleClose}
|
onClick={() => {
|
||||||
|
shareStatus(guesses);
|
||||||
|
handleShare();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Exit
|
Share
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
18
src/lib/localStorage.ts
Normal file
18
src/lib/localStorage.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
const gameStateKey = "gameState";
|
||||||
|
|
||||||
|
type StoredGameState = {
|
||||||
|
guesses: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saveGameStateToLocalStorage = (guesses: string[]) => {
|
||||||
|
const gameState = {
|
||||||
|
guesses,
|
||||||
|
};
|
||||||
|
localStorage.setItem(gameStateKey, JSON.stringify(gameState));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadGameStateFromLocalStorage = () => {
|
||||||
|
const state = localStorage.getItem(gameStateKey);
|
||||||
|
|
||||||
|
return state ? (JSON.parse(state) as StoredGameState) : null;
|
||||||
|
};
|
34
src/lib/share.ts
Normal file
34
src/lib/share.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { getGuessStatuses } from "./statuses";
|
||||||
|
import { solutionIndex } from "./words";
|
||||||
|
|
||||||
|
export const shareStatus = (guesses: string[]) => {
|
||||||
|
navigator.clipboard.writeText(
|
||||||
|
"Wordle " +
|
||||||
|
solutionIndex +
|
||||||
|
" " +
|
||||||
|
guesses.length +
|
||||||
|
"/6\n\n" +
|
||||||
|
generateEmojiGrid(guesses)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generateEmojiGrid = (guesses: string[]) => {
|
||||||
|
return guesses
|
||||||
|
.map((guess) => {
|
||||||
|
const status = getGuessStatuses(guess);
|
||||||
|
return guess
|
||||||
|
.split("")
|
||||||
|
.map((letter, i) => {
|
||||||
|
switch (status[i]) {
|
||||||
|
case "correct":
|
||||||
|
return "🟩";
|
||||||
|
case "present":
|
||||||
|
return "🟨";
|
||||||
|
default:
|
||||||
|
return "⬜";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
};
|
|
@ -19,7 +19,10 @@ export const getWordOfDay = () => {
|
||||||
const msInDay = 86400000;
|
const msInDay = 86400000;
|
||||||
const index = Math.floor((now - epochMs) / msInDay);
|
const index = Math.floor((now - epochMs) / msInDay);
|
||||||
|
|
||||||
return WORDS[index].toUpperCase();
|
return {
|
||||||
|
solution: WORDS[index].toUpperCase(),
|
||||||
|
solutionIndex: index,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const solution = getWordOfDay();
|
export const { solution, solutionIndex } = getWordOfDay();
|
||||||
|
|
Loading…
Add table
Reference in a new issue