Skip to content

Commit

Permalink
Merge pull request #49 from JollyGrin/feat/draft-v3-online
Browse files Browse the repository at this point in the history
Feat/draft v3 online
  • Loading branch information
JollyGrin authored Oct 9, 2024
2 parents fa6510e + 274a360 commit 5db7757
Show file tree
Hide file tree
Showing 12 changed files with 589 additions and 91 deletions.
4 changes: 2 additions & 2 deletions src/components/organisms/Crack/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { Ribbon } from "@/components/organisms/Crack/Ribbon";
import { useMemo, useState } from "react";
import { CrackStats } from "@/components/organisms/Crack/Stats";

const hTop = "7vh";
const hTop = "10vh";
const hTabs = "5vh";
const hCards = "88vh";
const hCards = "85vh";
export const gridHeight = { top: hTop, tabs: hTabs, cards: hCards };

export const CrackBoard = (props: {
Expand Down
5 changes: 5 additions & 0 deletions src/components/organisms/Draft/Ribbon/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ export function reduceCardCount(
}
return acc;
}

export function mapPackKey(pack: CardDTO[]) {
const cardKeys = pack.map((card) => card.slug.slice(0, 2));
return cardKeys.join("");
}
177 changes: 143 additions & 34 deletions src/components/organisms/Draft/Ribbon/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { Button } from "@/components/ui/button";
import { Flex, Grid } from "styled-system/jsx";
import { DraftProps } from "../types";
import { generateBoosterPack } from "../helpers";
import { DraftPlayerData, DraftProps } from "../types";
import { findAdjacentPlayers, generateBoosterPack } from "../helpers";
import { useCardFullData } from "@/utils/api/cardData/useCardData";
import { useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { SelectedCardsModal } from "./SelectedCardsModal";
import { useRouter } from "next/router";
import { mapPackKey } from "./helpers";

export const DraftRibbon = (
props: DraftProps & {
takeAndPass?(): void;
players: Record<string, DraftPlayerData>;
},
) => {
const { query } = useRouter();
const name = (query?.name as string) ?? ("p1" as string);
const [isOpen, setIsOpen] = useState(false);
const disclosure = {
isOpen,
Expand All @@ -21,10 +25,47 @@ export const DraftRibbon = (

const { data: cardData = [] } = useCardFullData();

const { nextPlayer, previousPlayer } = useMemo(() => {
return findAdjacentPlayers(props.players[name], props.players);
}, [
Object.values(props.players).map((player) => player.joinedSessionTimestamp),
]);

const pendingPackKeys = useMemo(() => {
if (!props?.player?.pendingPacks) return [];
return props.player.pendingPacks.map(mapPackKey);
}, [props.player.pendingPacks]);

// handle the state of copying finished packs and waiting for removal from prev player
const { unrequestedPacks, availablePacks } = useMemo(() => {
const [, values] = previousPlayer;
const { finishedPacks } = values;

if (!finishedPacks) return {};

const unrequestedPacks = finishedPacks.filter((pack) => {
const packKey = mapPackKey(pack);
return !pendingPackKeys.includes(packKey);
});

const requestedPacks = finishedPacks.filter((pack) => {
const packKey = mapPackKey(pack);
return pendingPackKeys.includes(packKey);
});
const requestedPackKeys = requestedPacks.map(mapPackKey);

const availablePacks = props.player.pendingPacks.filter((pack) => {
const packKey = mapPackKey(pack);
return !requestedPackKeys.includes(packKey);
});

return { unrequestedPacks, requestedPacks, availablePacks };
}, [previousPlayer[1].finishedPacks, pendingPackKeys]);

function crackBooster() {
const newBooster = generateBoosterPack({
cardData,
expansionSlug: "bet",
expansionSlug: "bet", // TODO: let players decide expansion
});
props.setPlayerData({
...props.player,
Expand All @@ -33,23 +74,10 @@ export const DraftRibbon = (
});
}

// function takeAndPass() {
// const { activePack, finishedPacks, selectedIndex, selectedCards } =
// props.player;

// if (selectedIndex === undefined) return;

// const updatedPack = [...activePack];
// const updatedSelected = [...selectedCards];

// const [card] = updatedPack.splice(selectedIndex, 1);
// updatedSelected.push(card);

// function copyToPending() {
// props.setPlayerData({
// ...props.player,
// activePack: [],
// finishedPacks: [...finishedPacks, updatedPack],
// selectedIndex: undefined,
// pendingPacks: [...props.player.pendingPacks, ...unrequestedPacks],
// });
// }

Expand All @@ -65,8 +93,79 @@ export const DraftRibbon = (
});
}

function takeAndPass() {
const { activePack, finishedPacks, selectedIndex, selectedCards } =
props.player;

if (selectedIndex === undefined) return;

const updatedPack = [...activePack];
const updatedSelected = [...selectedCards];

const [card] = updatedPack.splice(selectedIndex, 1);
updatedSelected.push(card);

props.setPlayerData({
...props.player,
activePack: [],
finishedPacks: [...finishedPacks, updatedPack],
selectedCards: updatedSelected,
selectedIndex: undefined,
});
}

const [nextPack] = props.player.pendingPacks ?? [];

useEffect(() => {
if (!props.player.pendingPacks) return;
const [, value] = previousPlayer;

// keys of my pending packs
const myPendingPackKeys = props.player.pendingPacks.map(mapPackKey);
// search for packs in previous player finished to add to my pending
// packs in previous players finished that are not in my pending
const packsToRequest = value.finishedPacks.filter((pack) => {
const packKey = mapPackKey(pack);
// find packs that are not in my pending
return !myPendingPackKeys.includes(packKey);
});

if (packsToRequest.length === 0) return;

props.setPlayerData({
...props.player,
pendingPacks: [...props.player.pendingPacks, ...packsToRequest],
});
}, [previousPlayer[1].finishedPacks]);

useEffect(() => {
if (props.player.finishedPacks.length === 0) return;
// if nextplayer has matching pack in their pending, delete from my finished
const [, value] = nextPlayer;
// get keys of packs in next player's pending
const pendingPackKeys = value.pendingPacks.map(mapPackKey);
// find packs that are not requested yet
const unrequestedPacks = props.player.finishedPacks.filter((pack) => {
const packKey = mapPackKey(pack);
return !pendingPackKeys.includes(packKey);
});

const unrequestedPackKeys = unrequestedPacks.map(mapPackKey);
const isSame = props.player.finishedPacks.every((pack) => {
const packKey = mapPackKey(pack);
return unrequestedPackKeys.includes(packKey);
});

if (isSame) return;

props.setPlayerData({
...props.player,
finishedPacks: unrequestedPacks,
});
}, [nextPlayer[1].pendingPacks]);

if (!availablePacks) return null;

return (
<>
<SelectedCardsModal
Expand All @@ -75,18 +174,24 @@ export const DraftRibbon = (
cards={props.player.selectedCards}
/>

<Grid h="100%" bg="brown" gap={0} gridTemplateColumns="1fr 4fr 1fr">
<Flex alignItems="center" justifyContent="center">
<Grid h="100%" bg="brown" gap={0} gridTemplateColumns="1fr 3fr 1fr">
<Flex alignItems="center" justifyContent="center" gap={1}>
{/* <Button */}
{/* disabled={unrequestedPacks.length === 0} */}
{/* onClick={copyToPending} */}
{/* > */}
{/* Request: {unrequestedPacks.length.toString()} */}
{/* </Button> */}
{/* <p>{requestedPacks.length.toString()}</p> */}
<Button
disabled={
props.player.pendingPacks.length === 0 ||
availablePacks.length === 0 ||
props.player.activePack.length > 0 ||
nextPack.length === 0
}
onClick={activatePendingPack}
>
Flip pack -
{(nextPack?.length > 0 && props.player.pendingPacks.length) || " 0"}
Flip -{availablePacks.length}
</Button>
</Flex>
<Grid
Expand All @@ -100,28 +205,32 @@ export const DraftRibbon = (

{props.player.activePack.length === 0 ? (
<Button
disabled={(nextPack ?? []).length > 0}
disabled={
(nextPack ?? []).length > 0 || unrequestedPacks.length > 0
}
onClick={crackBooster}
>
Open a Pack
</Button>
) : (
<Button
disabled={props.player.selectedIndex === undefined}
onClick={props.takeAndPass}
onClick={takeAndPass}
>
Take &amp; Pass
</Button>
)}
</Grid>
<Flex alignItems="center" justifyContent="center">
<Button
bg="purple.800"
onClick={disclosure.onOpen}
borderRadius="10rem"
>
View Selected
</Button>
{props.player.selectedCards.length > 0 && (
<Button
bg="purple.800"
onClick={disclosure.onOpen}
borderRadius="10rem"
>
View Selected
</Button>
)}
</Flex>
</Grid>
</>
Expand Down
49 changes: 47 additions & 2 deletions src/components/organisms/Draft/Tray/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { Box, Flex, HStack } from "styled-system/jsx";
import { DraftPlayerData } from "../types";
import { css } from "styled-system/css";

import { LuArrowBigRightDash as IconRight } from "react-icons/lu";
import { RiArrowGoBackLine as IconReturnArrow } from "react-icons/ri";
import { FaRegCopy as IconCopy } from "react-icons/fa";
import { sortPlayersByJoin } from "../helpers";
import { useRouter } from "next/router";
import { Properties } from "styled-system/types/csstype";
import { useCopyToClipboard } from "@/utils/hooks";
import toast from "react-hot-toast";

export const DraftTray = (props: {
players: Record<string, DraftPlayerData>;
}) => {
const { query, push } = useRouter();
const { query, push, pathname } = useRouter();
const players = Object.entries(props.players).sort(sortPlayersByJoin);
const [, copy] = useCopyToClipboard();

function changePlayer(name: string) {
if (pathname.split("/").includes("online")) return;
push({
query: {
...query,
Expand All @@ -23,7 +29,25 @@ export const DraftTray = (props: {
}

return (
<HStack data-testid="stats" p="1rem">
<HStack data-testid="stats" p="1rem" position="relative">
<HStack
position="absolute"
right={3}
top={"0.5rem"}
p="0.25rem 1rem"
bg="teal.200"
borderRadius="2rem"
className={IconStyle}
onClick={() => {
const text = `https://spells.bar/draft/online?gid=${query.gid}`;
copy(text);
toast.success(`Copied to clipboard`);
}}
>
<IconCopy />
<p>Share Lobby</p>
</HStack>

{players?.map(([key, value], index) => {
return (
<Flex
Expand Down Expand Up @@ -72,6 +96,9 @@ const Dots = (props: DraftPlayerData) => {
background: "red",
};

if (!props?.pendingPacks) return <div />;
if (!props?.finishedPacks) return <div />;

return (
<>
{props.pendingPacks.map(
Expand All @@ -98,6 +125,10 @@ const Dots = (props: DraftPlayerData) => {
};

function getStatus(props: DraftPlayerData) {
if (!props) return;
if (!props.activePack) return;
if (!props.selectedIndex) return;

const activeIsEmpty = props.activePack.length === 0;
// const pendingIsEmpty = props.pendingPacks.length === 0;
const isSelecting = props.selectedIndex !== undefined;
Expand All @@ -106,3 +137,17 @@ function getStatus(props: DraftPlayerData) {
if (!activeIsEmpty && !isSelecting) return "thinking";
if (!activeIsEmpty && isSelecting) return "selecting";
}

const IconStyle = css({
userSelect: "none",
transform: "scale(1)",
transition: "all 0.25s ease",
cursor: "pointer",
_hover: {
transform: "scale(1.1)",
filter: "drop-shadow(0 0 2px rgba(0,0,0,0.25))",
},
_active: {
transform: "scale(1.05)",
},
});
9 changes: 5 additions & 4 deletions src/components/organisms/Draft/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ import { DraftPlayerData } from "./types";
import { DraftRibbon } from "./Ribbon";
import { DraftTray } from "./Tray";

const hTop = "7vh";
const hTabs = "5vh";
const hCards = "88vh";
const hTop = "9vh";
const hTabs = "6vh";
const hCards = "85vh";
export const gridHeight = { top: hTop, tabs: hTabs, cards: hCards };

export const DraftBoard = (props: {
players: Record<string, DraftPlayerData>;
player: DraftPlayerData;
setPlayerData(data: DraftPlayerData): void;
takeAndPass?(): void;
}) => {
// select a card from active pack, ready for taking
function setSelectedIndex(index?: number) {
Expand All @@ -23,6 +22,8 @@ export const DraftBoard = (props: {
});
}

if (!props?.players) return "loading draft";

return (
<Grid
h="100vh"
Expand Down
Loading

0 comments on commit 5db7757

Please sign in to comment.