minesweeper/src/views/store/Store.tsx

199 lines
8.4 KiB
TypeScript

import { Fragment } from "react/jsx-runtime";
import { lootboxes } from "../../../shared/lootboxes";
import { Button } from "../../components/Button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../components/Dialog";
import GemsIcon from "../../components/GemIcon";
import { themes } from "../../themes";
import { useWSMutation } from "../../hooks";
import { Rarity } from "../../components/Rarity";
import { lootboxResultAtom } from "../../atoms";
import { useAtom } from "jotai";
import { useEffect } from "react";
import Particles, { initParticlesEngine } from "@tsparticles/react";
import { loadSlim } from "@tsparticles/slim";
import { loadSeaAnemonePreset } from "@tsparticles/preset-sea-anemone";
import { motion } from "framer-motion";
const Store = () => {
const openLootbox = useWSMutation("user.openLootbox");
const [lootboxResult, setLootboxResult] = useAtom(lootboxResultAtom);
const currentLootbox = lootboxes.find((l) => l.id === lootboxResult?.lootbox);
// this should be run only once per application lifetime
useEffect(() => {
initParticlesEngine(async (engine) => {
// you can initiate the tsParticles instance (engine) here, adding custom shapes or presets
// this loads the tsparticles package bundle, it's the easiest method for getting everything ready
// starting from v2 you can add only the features you need reducing the bundle size
//await loadAll(engine);
//await loadFull(engine);
await loadSlim(engine);
await loadSeaAnemonePreset(engine);
//await loadBasic(engine);
});
}, []);
return (
<>
<Dialog
open={!!currentLootbox}
onOpenChange={() => setLootboxResult(undefined)}
>
<DialogContent className="relative h-96 w-[1000px]">
<DialogHeader>
<DialogTitle className="z-10">
Opening {currentLootbox?.name}
</DialogTitle>
</DialogHeader>
<div className="absolute flex h-full w-full flex-col items-center justify-center">
<Particles
className="rounded-md"
id="tsparticles"
options={{
id: "tsparticles",
preset: "seaAnemone",
}}
/>
<motion.div
className="text-white/90 text-6xl"
initial={{ scale: 0, rotate: 0, opacity: 0 }}
animate={{
scale: [0, 0.2, 0.5, 0.75, 1.5, 1.2, 1],
rotate: 360 * 2,
opacity: 1,
}}
transition={{ type: "spring", duration: 5 }}
>
<Rarity
// @ts-expect-error Don't care
rarity={
currentLootbox?.items.find(
(i) => i.id == lootboxResult?.result,
)?.rarity
}
>
{themes.find((t) => t.id === lootboxResult?.result)?.name}
</Rarity>
</motion.div>
</div>
</DialogContent>
</Dialog>
<div className="flex flex-col gap-4 w-full">
<h2 className="text-white/90 text-xl">Store</h2>
<div className="flex flex-row gap-y-6 gap-x-8 items-center w-full flex-wrap justify-center mb-10">
<div className="flex flex-row gap-y-6 gap-x-8 items-center w-full flex-wrap justify-center mb-10">
{lootboxes.map((lootbox) => (
<div
key={lootbox.id}
className="border-white/10 border-1 rounded-md p-4"
>
<div className="flex gap-4 justify-between">
<h3 className="text-white/90 text-lg">{lootbox.name}</h3>
<Dialog>
<DialogTrigger>
<Button variant="default" size="sm">
Preview
</Button>
</DialogTrigger>
<DialogContent
className="max-w-4xl m-4 relative top-4 translate-y-0"
overlayClassName="overflow-y-auto"
>
<DialogHeader>
<DialogTitle>{lootbox.name}</DialogTitle>
</DialogHeader>
<div className="flex gap-4 flex-wrap flex-row">
<div className="flex flex-col gap-4 basis-md">
<img
src={lootbox.image}
className="max-w-[360px] w-[min(360px,100%)]"
/>
<div className="text-white/70 text-base">
Introducing <i>{lootbox.name}</i>, the first-ever
lootbox for Minesweeper, packed with a variety of
stylish skins to customize your game like never
before! With <i>{lootbox.name}</i>, every click
becomes a statement, as you explore new looks for
your mines, tiles, and flags. Transform your
Minesweeper grid with these visually captivating
designs and make each puzzle feel fresh and
exciting.
</div>
<h3 className="text-white/90 text-lg">
What&apos;s Inside?
</h3>
<ul className="text-white/70 text-base flex flex-col gap-2">
<li>
<b>Mine Skins:</b> Swap out the classic mine icons
for creative alternatives like skulls, treasure
chests, or high-tech drones.
</li>
<li>
<b>Tile Skins:</b> Replace the plain tiles with
vibrant patterns such as tropical beaches,
medieval bricks, or sleek metallic plates.
</li>
<li>
<b>Flag Skins:</b> Mark suspected mines in style
with custom flag designs including pirate flags,
futuristic beacons, or glowing crystals.
</li>
<li>
<b>Backgrounds:</b> Enhance your gameplay
experience with unique backgrounds, from serene
mountain landscapes to bustling cityscapes or
outer space vistas.
</li>
<div className="text-white/70 text-base">
Step up your Minesweeper game and express your
personality with <i>{lootbox.name}</i>. Unlock new
looks and turn every game into a visual
masterpiece!
</div>
</ul>
</div>
<div className="flex justify-center grow">
<div className="grid grid-cols-2 gap-4">
<div>Skin</div>
<div>Rarity</div>
{lootbox.items.map((item) => (
<Fragment key={item.id}>
<div className="text-white/90">
{themes.find((t) => t.id === item.id)?.name}
</div>
<Rarity rarity={item.rarity} />
</Fragment>
))}
</div>
</div>
</div>
</DialogContent>
</Dialog>
</div>
<img src={lootbox.image} className="w-[360px]" />
<Button
variant="outline"
size="default"
className="mx-auto items-center"
onClick={() => openLootbox.mutateAsync({ id: lootbox.id })}
>
Buy for <b>{lootbox.priceText}</b> <GemsIcon />
</Button>
</div>
))}
</div>
</div>
</div>
</>
);
};
export default Store;