Skip to content

Commit

Permalink
Merge pull request #24 from JollyGrin/feat/datapack
Browse files Browse the repository at this point in the history
Feat/datapack
  • Loading branch information
JollyGrin authored Mar 30, 2024
2 parents 798b1e1 + 11e633b commit d2490cc
Show file tree
Hide file tree
Showing 10 changed files with 8,547 additions and 21 deletions.
242 changes: 242 additions & 0 deletions components/Bag/Bulk/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
import { DeckImportType } from "@/components/DeckPool/deck-import.type";
import { MapData, useLocalDeckStorage, useLocalMapStorage } from "@/lib/hooks";
import { useCopyToClipboard } from "@/lib/hooks/useCopyToClipboard";
import { useGenericImport } from "@/lib/hooks/useGenericImport";
import { useJsonCheck } from "@/lib/hooks/useJsonCheck";
import {
Box,
Button,
Divider,
FormLabel,
Grid,
HStack,
Input,
Switch,
Text,
Textarea,
useDisclosure,
} from "@chakra-ui/react";
import dynamic from "next/dynamic";
import Link from "next/link";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { useDebounce } from "use-debounce";
const DynamicReactJson = dynamic(import("react-json-view"), { ssr: false });

export const BagBulkContainer = () => {
const { query, push } = useRouter();
const { decks, pushDeck, removeDeckbyId } = useLocalDeckStorage();
const { data: maps, add: addMap } = useLocalMapStorage();

const [rawText, setRawText] = useState<string>();
const [_url, setUrl] = useState<string>();
const [url] = useDebounce(_url, 300);

const { data: dataGeneric } = useGenericImport(url);

const urlDisclosure = useDisclosure();

const localBulk = { decks, maps };

useEffect(() => {
if (query?.url) {
urlDisclosure.onOpen();
setUrl(query.url as string);
}
}, [query?.url]);

return (
<Box h="100%">
<Box p="0.5rem" minH="150px">
<Text>
Backup your existing storage or upload decks and maps in bulk!
</Text>
<HStack justifyContent="space-between">
<Box>
<HStack>
<Switch
isChecked={urlDisclosure.isOpen}
onChange={urlDisclosure.onToggle}
/>
{urlDisclosure.isOpen ? (
<Text my="0.5rem">Enter URL</Text>
) : (
<Text my="0.5rem">Viewing Local Storage</Text>
)}
</HStack>
{urlDisclosure.isOpen && (
<HStack gap="0.5rem">
<Input
isDisabled={!!query?.url as boolean}
maxW="200px"
bg="white"
defaultValue={query?.url as string | undefined}
value={url}
onChange={(e) => {
if (rawText) setRawText(undefined);
if (e.target.value === "") {
setUrl(undefined);
return;
}
setUrl(e.target.value);
}}
/>
{!query?.url && (
<Button
fontSize="0.7rem"
p="0.25rem 0.5rem"
as={Link}
href={{
pathname: "/bag",
query: { tab: 2, url },
}}
>
Share Import
</Button>
)}

{query?.url && (
<Button
fontSize="0.7rem"
p="0.25rem 0.5rem"
onClick={() => {
push({
pathname: "/bag",
query: { tab: 2 },
});
setUrl(undefined);
urlDisclosure.onClose();
}}
>
Clear Import
</Button>
)}
</HStack>
)}
</Box>
{urlDisclosure.isOpen && (
<Box>
<Text>Or copy/paste the json</Text>
<Textarea
h="50px"
fontSize="0.35rem"
value={rawText}
onChange={(e) => {
if (url) setUrl(undefined);
if (e.target.value === "") {
setRawText(undefined);
return;
}
setRawText(e.target.value);
}}
/>
</Box>
)}
</HStack>
</Box>
{!urlDisclosure.isOpen && (
<BulkGrid
bulk={localBulk}
isLocal={true}
fns={{ pushDeck, removeDeckbyId }}
/>
)}
{urlDisclosure.isOpen && rawText && (
<GridTextWrapper
text={rawText}
fns={{ pushDeck, removeDeckbyId, addMap }}
/>
)}
{urlDisclosure.isOpen && dataGeneric?.data && (
<GridTextWrapper
text={JSON.stringify(dataGeneric?.data)}
fns={{ pushDeck, removeDeckbyId, addMap }}
/>
)}
</Box>
);
};

const GridTextWrapper = ({ text, fns }: { text: string; fns?: Fns }) => {
const { data: isJsonValid } = useJsonCheck(text);
if (!isJsonValid) return <Invalid />;

const parsed = JSON.parse(text) as {
decks?: DeckImportType[];
maps?: MapData[];
};
if (!parsed?.decks && !parsed?.maps) return <Invalid />;
return <BulkGrid bulk={parsed} fns={fns} />;
};

const Invalid = () => (
<Box>
<Text>Invalid Json</Text>
</Box>
);

type Fns = {
pushDeck: (deck: DeckImportType) => void;
removeDeckbyId: (id: string) => void;
addMap?: (map: MapData) => void;
};

const BulkGrid = ({
bulk,
isLocal,
fns,
}: {
fns?: Fns;
isLocal?: boolean;
bulk: {
decks?: DeckImportType[];
maps?: MapData[];
};
}) => {
return (
<Grid templateColumns="3fr 7fr" h="100%" bg="brand.secondary">
<Box h="100%" bg="white" p="0.5rem">
<FormLabel fontWeight={700}>Decks</FormLabel>
{bulk?.decks?.map((deck) => (
<Box
key={deck.id + deck.version_id}
_hover={{ bg: isLocal ? "rgba(0,0,0,0.25)" : "green" }}
onClick={() => {
if (isLocal) {
fns?.removeDeckbyId(deck.id);
toast.success(`Removed ${deck.name}`);
return;
}
fns?.pushDeck(deck);
toast.success(`Imported ${deck.name}. Reload Page.`);
}}
>
<Text>{deck.name}</Text>
</Box>
))}
<Divider my="0.5rem" />
<FormLabel fontWeight={700}>Maps</FormLabel>

{bulk?.maps?.map((map) => (
<Box
key={map.imgUrl}
onClick={() => {
if (!fns?.addMap) return;
fns.addMap(map);
toast.success(
`Imported ${map.meta?.title ?? map?.imgUrl}. Reload page`,
);
}}
_hover={{ bg: isLocal ? "" : "green" }}
>
<Text>{map?.meta?.title ?? map?.imgUrl}</Text>
</Box>
))}
</Box>
<Box h="100%" overflowY="auto" bg="white">
<DynamicReactJson src={bulk} />
</Box>
</Grid>
);
};
18 changes: 3 additions & 15 deletions components/Bag/Deck/AddJson.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { useQuery } from "@tanstack/react-query";
import { useLocalDeckStorage } from "@/lib/hooks";
import { useRouter } from "next/router";
import axios from "axios";
import { jsonCheck } from "@/lib/hooks/helpers";
import { useJsonCheck } from "@/lib/hooks/useJsonCheck";

export const AddJson = () => {
return (
Expand All @@ -44,11 +46,7 @@ const Options = () => {

const [json, setJson] = useState<string>("");
const [url, setUrl] = useState<string>();

const { data: isJsonValid } = useQuery(
["json-check", json.length, json.substring(0, 5)],
async () => await jsonCheck(json),
);
const { data: isJsonValid } = useJsonCheck(json);

const { data: urlData } = useQuery(
["urlData"],
Expand Down Expand Up @@ -151,13 +149,3 @@ const StatusText = (props: { text: string; isValid: boolean }) => {
</HStack>
);
};

async function jsonCheck(value: string) {
console.log(value);
try {
JSON.parse(value);
return true;
} catch (err) {
return false;
}
}
1 change: 0 additions & 1 deletion components/Bag/Deck/DeckCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export const DeckCards = ({
};

const HeroCard = ({ deck }: { deck: DeckImportType }) => {
console.log({ deck });
const hero = deck?.deck_data?.hero;
const sidekick = deck?.deck_data?.sidekick;
const isSidekick =
Expand Down
4 changes: 1 addition & 3 deletions components/Bag/StarterDecks/useStarterDecks.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { DeckImportType } from "@/components/DeckPool/deck-import.type";
import { useQuery, useQueries } from "@tanstack/react-query";
import { useQueries } from "@tanstack/react-query";
import axios from "axios";
import toast from "react-hot-toast";

export const useStarterDecks = (props: { enabled: boolean }) => {
const deckKeys = Object.keys(BACKUP_DECKS);
console.log({ deckKeys });

const queries = useQueries({
queries: deckKeys.map((id) => ({
Expand Down
8 changes: 8 additions & 0 deletions lib/hooks/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export async function jsonCheck(value: string) {
try {
JSON.parse(value);
return true;
} catch (err) {
return false;
}
}
8 changes: 8 additions & 0 deletions lib/hooks/useGenericImport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useQuery } from "@tanstack/react-query";
import axios from "axios";

export const useGenericImport = (url?: string) => {
return useQuery(["urlData", url], async () => await axios.get(url ?? ""), {
enabled: !!url,
});
};
18 changes: 18 additions & 0 deletions lib/hooks/useJsonCheck.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useQuery } from "@tanstack/react-query";

export const useJsonCheck = (json?: string) => {
const jsonString = json ?? "";
return useQuery(
["json-check", jsonString.length, jsonString.substring(0, 5)],
async () => await jsonCheck(jsonString),
{ enabled: !!json && json !== "" },
);
};
async function jsonCheck(value: string) {
try {
JSON.parse(value);
return true;
} catch (err) {
return false;
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"react-dom": "18.2.0",
"react-hot-toast": "^2.4.0",
"react-icons": "^4.8.0",
"react-json-view": "^1.21.3",
"typescript": "5.0.4",
"use-debounce": "^9.0.4"
},
Expand Down
19 changes: 17 additions & 2 deletions pages/bag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,29 @@ import {
import { Navbar } from "@/components/Navbar";
import { BagDecks } from "@/components/Bag/Deck";
import { BagMap } from "@/components/Bag/Map";
import { BagBulkContainer } from "@/components/Bag/Bulk";
import { useRouter } from "next/router";

const BagPage = () => {
const { query, push } = useRouter();
const tab = query?.tab as number | undefined;

return (
<Flex flexDir={"column"} bg="brand.highlight" h="97svh">
<Flex flexDir={"column"} bg="brand.highlight" h="100svh">
<Box color="brand.secondary">
<Navbar />
</Box>
<Tabs h="100%">
<Tabs
h="100%"
index={tab ? +tab : 0}
onChange={(e) => {
push({ query: { ...query, tab: e.toString() } });
}}
>
<TabList>
<Tab>Decks</Tab>
<Tab>Maps</Tab>
<Tab>Bulk Backup/Upload</Tab>
</TabList>

<TabPanels h="100%">
Expand All @@ -37,6 +49,9 @@ const BagPage = () => {
<TabPanel p={0} h="100%">
<BagMap />
</TabPanel>
<TabPanel p={0} h="100%" bg="brand.primary">
<BagBulkContainer />
</TabPanel>
</TabPanels>
</Tabs>
</Flex>
Expand Down
Loading

0 comments on commit d2490cc

Please sign in to comment.