mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-24 12:06:54 -05:00
migrate plans and sort stage selector
This commit is contained in:
@@ -2,6 +2,7 @@ import { Box, Flex, IconButton } from "@chakra-ui/react";
|
||||
import { Trans } from "@lingui/macro";
|
||||
import ColorPicker from "components/common/ColorPicker";
|
||||
import { useMyTheme } from "hooks/common";
|
||||
import { Tool } from "pages/plans";
|
||||
import { useState } from "react";
|
||||
import Draggable from "react-draggable";
|
||||
import { useHotkeys } from "react-hotkeys-hook";
|
||||
@@ -16,7 +17,6 @@ import {
|
||||
FaTrashAlt,
|
||||
FaUndo,
|
||||
} from "react-icons/fa";
|
||||
import { Tool } from ".";
|
||||
|
||||
interface DraggableToolsSelectorProps {
|
||||
tool: Tool;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Box } from "@chakra-ui/react";
|
||||
import { SketchField } from "@sendou/react-sketch";
|
||||
import { Tool } from ".";
|
||||
import { Tool } from "pages/plans";
|
||||
|
||||
interface MapSketchProps {
|
||||
sketch: any;
|
||||
|
||||
@@ -19,7 +19,7 @@ import salmonRunLowTide from "lib/assets/SalmonRunLowTide.svg";
|
||||
// @ts-ignore
|
||||
import salmonRunMidTide from "lib/assets/SalmonRunMidTide.svg";
|
||||
import { salmonRunStages, stages } from "lib/lists/stages";
|
||||
import { PlannerMapBg } from ".";
|
||||
import { PlannerMapBg } from "pages/plans";
|
||||
|
||||
interface StageSelectorProps {
|
||||
handleChange: (event: React.ChangeEvent<HTMLSelectElement>) => void;
|
||||
@@ -40,11 +40,14 @@ const StageSelector: React.FC<StageSelectorProps> = ({
|
||||
return (
|
||||
<Box maxW="20rem" m="3rem auto">
|
||||
<Select value={currentBackground.stage} onChange={handleChange}>
|
||||
{salmonRunStages.concat(stages).map((stage) => (
|
||||
<option key={stage} value={stage}>
|
||||
{i18n._(stage)}
|
||||
</option>
|
||||
))}
|
||||
{salmonRunStages
|
||||
.concat(stages)
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
.map((stage) => (
|
||||
<option key={stage} value={stage}>
|
||||
{i18n._(stage)}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
{currentBackground.tide ? (
|
||||
<>
|
||||
|
||||
@@ -1,377 +0,0 @@
|
||||
import { Button, ButtonGroup, Flex } from "@chakra-ui/react";
|
||||
import { t, Trans } from "@lingui/macro";
|
||||
import Breadcrumbs from "components/common/Breadcrumbs";
|
||||
import { stages } from "lib/lists/stages";
|
||||
import { NextPage } from "next";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
FaBomb,
|
||||
FaFileDownload,
|
||||
FaFileImage,
|
||||
FaFileUpload,
|
||||
} from "react-icons/fa";
|
||||
import DraggableImageAdder from "./DraggableImageAdder";
|
||||
import DraggableToolsSelector from "./DraggableToolsSelector";
|
||||
import StageSelector from "./StageSelector";
|
||||
|
||||
const MapSketch = dynamic(() => import("./MapSketch"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export type Tool = "pencil" | "line" | "rectangle" | "circle" | "select";
|
||||
|
||||
export interface PlannerMapBg {
|
||||
view?: "M" | "R";
|
||||
stage: string;
|
||||
mode?: "SZ" | "TC" | "RM" | "CB";
|
||||
tide?: "low" | "mid" | "high";
|
||||
}
|
||||
|
||||
const stageToCode = new Map<string, string>([
|
||||
["Ancho-V Games", "AG"],
|
||||
["Arowana Mall", "AM"],
|
||||
["Blackbelly Skatepark", "BS"],
|
||||
["Camp Triggerfish", "CT"],
|
||||
["Goby Arena", "GA"],
|
||||
["Humpback Pump Track", "HP"],
|
||||
["Inkblot Art Academy", "IA"],
|
||||
["Kelp Dome", "KD"],
|
||||
["Musselforge Fitness", "MF"],
|
||||
["MakoMart", "MK"],
|
||||
["Manta Maria", "MM"],
|
||||
["Moray Towers", "MT"],
|
||||
["New Albacore Hotel", "NA"],
|
||||
["Port Mackerel", "PM"],
|
||||
["Piranha Pit", "PP"],
|
||||
["Snapper Canal", "SC"],
|
||||
["Shellendorf Institute", "SI"],
|
||||
["Starfish Mainstage", "SM"],
|
||||
["Skipper Pavilion", "SP"],
|
||||
["Sturgeon Shipyard", "SS"],
|
||||
["The Reef", "TR"],
|
||||
["Wahoo World", "WH"],
|
||||
["Walleye Warehouse", "WW"],
|
||||
]);
|
||||
|
||||
const plannerMapBgToImage = (bg: PlannerMapBg) => {
|
||||
if (!bg.tide)
|
||||
return `images/plannerMaps/${bg.view} ${stageToCode.get(bg.stage)} ${
|
||||
bg.mode
|
||||
}.png`;
|
||||
|
||||
return `images/plannerMaps/${bg.stage}-${bg.tide}.png`;
|
||||
};
|
||||
|
||||
const defaultValue = {
|
||||
shadowWidth: 0,
|
||||
shadowOffset: 0,
|
||||
enableRemoveSelected: false,
|
||||
fillWithColor: false,
|
||||
fillWithBackgroundColor: false,
|
||||
drawings: [],
|
||||
canUndo: false,
|
||||
canRedo: false,
|
||||
controlledSize: false,
|
||||
sketchWidth: 600,
|
||||
sketchHeight: 600,
|
||||
stretched: true,
|
||||
stretchedX: false,
|
||||
stretchedY: false,
|
||||
originX: "left",
|
||||
originY: "top",
|
||||
expandTools: false,
|
||||
expandControls: false,
|
||||
expandColors: false,
|
||||
expandBack: false,
|
||||
expandImages: false,
|
||||
expandControlled: false,
|
||||
enableCopyPaste: false,
|
||||
backgroundImage: {
|
||||
type: "image",
|
||||
version: "2.4.3",
|
||||
originX: "left",
|
||||
originY: "top",
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: 1127,
|
||||
height: 634,
|
||||
fill: "rgb(0,0,0)",
|
||||
stroke: null,
|
||||
strokeWidth: 0,
|
||||
strokeDashArray: null,
|
||||
strokeLineCap: "butt",
|
||||
strokeLineJoin: "miter",
|
||||
strokeMiterLimit: 4,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
angle: 0,
|
||||
flipX: false,
|
||||
flipY: false,
|
||||
opacity: 1,
|
||||
shadow: null,
|
||||
visible: true,
|
||||
clipTo: null,
|
||||
backgroundColor: "",
|
||||
fillRule: "nonzero",
|
||||
paintFirst: "fill",
|
||||
globalCompositeOperation: "source-over",
|
||||
transformMatrix: null,
|
||||
skewX: 0,
|
||||
skewY: 0,
|
||||
crossOrigin: "",
|
||||
cropX: 0,
|
||||
cropY: 0,
|
||||
src: "/images/plannerMaps/M%20TR%20SZ.png",
|
||||
filters: [],
|
||||
},
|
||||
};
|
||||
|
||||
const MapPlannerPage: NextPage = () => {
|
||||
const fileInput = useRef<HTMLInputElement | null>(null);
|
||||
const sketch = useRef<any>(null);
|
||||
const [tool, setTool] = useState<Tool>("pencil");
|
||||
const [color, setColor] = useState("#f44336");
|
||||
const [canUndo, setCanUndo] = useState(false);
|
||||
const [canRedo, setCanRedo] = useState(false);
|
||||
const [bg, setBg] = useState<PlannerMapBg>({
|
||||
view: "M",
|
||||
stage: "The Reef",
|
||||
mode: "SZ",
|
||||
});
|
||||
const [controlledValue, setControlledValue] = useState(defaultValue);
|
||||
|
||||
const addImageToSketch = (imgSrc: string) => {
|
||||
sketch.current.addImg(imgSrc);
|
||||
setTool("select");
|
||||
};
|
||||
|
||||
const addTextToSketch = () => {
|
||||
sketch.current.addText("Double-click to edit", {
|
||||
fill: color,
|
||||
fontFamily: "lato",
|
||||
stroke: "#000000",
|
||||
strokeWidth: 3,
|
||||
paintFirst: "stroke",
|
||||
});
|
||||
setTool("select");
|
||||
};
|
||||
|
||||
const undo = () => {
|
||||
sketch.current.undo();
|
||||
setCanUndo(sketch.current.canUndo());
|
||||
setCanRedo(sketch.current.canRedo());
|
||||
};
|
||||
|
||||
const redo = () => {
|
||||
sketch.current.redo();
|
||||
setCanUndo(sketch.current.canUndo());
|
||||
setCanRedo(sketch.current.canRedo());
|
||||
};
|
||||
|
||||
const removeSelected = () => {
|
||||
sketch.current.removeSelected();
|
||||
};
|
||||
|
||||
const onSketchChange = () => {
|
||||
if (!sketch.current) return;
|
||||
let prev = canUndo;
|
||||
let now = sketch.current.canUndo();
|
||||
if (prev !== now) {
|
||||
setCanUndo(now);
|
||||
}
|
||||
};
|
||||
|
||||
const getDateFormatted = () => {
|
||||
const today = new Date();
|
||||
const date =
|
||||
today.getFullYear() +
|
||||
"-" +
|
||||
(today.getMonth() + 1) +
|
||||
"-" +
|
||||
today.getDate();
|
||||
const time =
|
||||
today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
|
||||
return date + " " + time;
|
||||
};
|
||||
|
||||
const download = (dataUrl: string, extension: string) => {
|
||||
if (!bg) return;
|
||||
let a = document.createElement("a");
|
||||
document.body.appendChild(a);
|
||||
a.style.display = "none";
|
||||
a.href = dataUrl;
|
||||
a.download = `${bg.stage} plans ${getDateFormatted()}.${extension}`;
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(dataUrl);
|
||||
};
|
||||
|
||||
const handleUpload = () => {
|
||||
if (!fileInput.current) {
|
||||
return;
|
||||
}
|
||||
fileInput.current.click();
|
||||
};
|
||||
|
||||
const files = fileInput.current?.files;
|
||||
|
||||
useEffect(() => {
|
||||
if (!fileInput.current?.files?.length) return;
|
||||
|
||||
const fileObj = fileInput.current.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (event) {
|
||||
const jsonObj = JSON.parse(event.target!.result as any);
|
||||
setControlledValue(jsonObj);
|
||||
|
||||
const imgSrc = jsonObj.backgroundImage.src;
|
||||
const searchFolder = "plannerMaps";
|
||||
const imgName = imgSrc
|
||||
.slice(imgSrc.lastIndexOf(searchFolder) + searchFolder.length + 1, -4)
|
||||
.replace(/%20/g, " ");
|
||||
|
||||
const salmonRunMaps = [
|
||||
"Spawning Grounds",
|
||||
"Marooner's Bay",
|
||||
"Lost Outpost",
|
||||
"Salmonid Smokeyard",
|
||||
"Ruins of Ark Polaris",
|
||||
];
|
||||
let isSalmonRunMap = false;
|
||||
for (const map of salmonRunMaps) {
|
||||
if (imgName.startsWith(map)) {
|
||||
isSalmonRunMap = true;
|
||||
const imageNameParts = imgName.split("-");
|
||||
if (imageNameParts.length > 1) {
|
||||
const tide = imageNameParts[1];
|
||||
setBg({ tide: tide, stage: map });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSalmonRunMap) {
|
||||
const imageNameParts = imgName.split(" ");
|
||||
if (imageNameParts.length > 2) {
|
||||
const view = imageNameParts[0];
|
||||
const mapCode = imageNameParts[1];
|
||||
const mode = imageNameParts[2];
|
||||
let mapName = "";
|
||||
stageToCode.forEach((value, key) => {
|
||||
if (value === mapCode) {
|
||||
mapName = key;
|
||||
}
|
||||
});
|
||||
setBg({ view, stage: mapName, mode });
|
||||
}
|
||||
}
|
||||
};
|
||||
reader.readAsText(fileObj);
|
||||
}, [files]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!sketch.current) return;
|
||||
setCanUndo(false);
|
||||
sketch.current.setBackgroundFromDataUrl(plannerMapBgToImage(bg));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [bg]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Breadcrumbs pages={[{ name: t`Map Planner` }]} />
|
||||
<DraggableToolsSelector
|
||||
tool={tool}
|
||||
setTool={setTool}
|
||||
redo={redo}
|
||||
redoIsDisabled={!canRedo}
|
||||
undo={undo}
|
||||
undoIsDisabled={!canUndo}
|
||||
removeSelected={removeSelected}
|
||||
addText={addTextToSketch}
|
||||
color={color}
|
||||
setColor={(newColor) => setColor(newColor)}
|
||||
/>
|
||||
<DraggableImageAdder
|
||||
addImageToSketch={(imgSrc) => addImageToSketch(imgSrc)}
|
||||
/>
|
||||
<MapSketch
|
||||
sketch={sketch}
|
||||
controlledValue={controlledValue}
|
||||
color={color}
|
||||
onSketchChange={onSketchChange}
|
||||
tool={tool}
|
||||
/>
|
||||
<Flex my={2} justifyContent="space-between">
|
||||
<Button
|
||||
onClick={() => {
|
||||
sketch.current.clear();
|
||||
setBg({ ...bg });
|
||||
}}
|
||||
leftIcon={<FaBomb />}
|
||||
colorScheme="red"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
>
|
||||
<Trans>Clear drawings</Trans>
|
||||
</Button>
|
||||
<ButtonGroup variant="outline" size="sm" isAttached>
|
||||
<Button
|
||||
onClick={() => download(sketch.current.toDataURL(), "png")}
|
||||
leftIcon={<FaFileImage />}
|
||||
>
|
||||
<Trans>Download as .png</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() =>
|
||||
download(
|
||||
"data:text/json;charset=utf-8," +
|
||||
encodeURIComponent(JSON.stringify(sketch.current.toJSON())),
|
||||
"json"
|
||||
)
|
||||
}
|
||||
leftIcon={<FaFileDownload />}
|
||||
>
|
||||
<Trans>Download as .json</Trans>
|
||||
</Button>
|
||||
<Button onClick={() => handleUpload()} leftIcon={<FaFileUpload />}>
|
||||
<Trans>Load from .json</Trans>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
<StageSelector
|
||||
handleChange={(e) => {
|
||||
const newStage = e.target.value;
|
||||
if (newStage === "") {
|
||||
return;
|
||||
}
|
||||
const newIsSalmonRunStage = !stages.includes(newStage as any);
|
||||
const oldIsSalmonRunStage = !stages.includes(bg.stage as any);
|
||||
|
||||
if (newIsSalmonRunStage === oldIsSalmonRunStage) {
|
||||
setBg({ ...bg, stage: e.target.value });
|
||||
return;
|
||||
}
|
||||
|
||||
if (newIsSalmonRunStage) {
|
||||
setBg({ stage: e.target.value, tide: "mid" });
|
||||
return;
|
||||
}
|
||||
|
||||
setBg({ stage: e.target.value, mode: "SZ", view: "M" });
|
||||
}}
|
||||
currentBackground={bg}
|
||||
changeMode={(mode) => setBg({ ...bg, mode })}
|
||||
changeTide={(tide: "low" | "mid" | "high") => setBg({ ...bg, tide })}
|
||||
changeView={(view: "M" | "R") => setBg({ ...bg, view })}
|
||||
/>
|
||||
<input
|
||||
type="file"
|
||||
accept=".json"
|
||||
ref={fileInput}
|
||||
style={{ display: "none" }}
|
||||
onChange={() => setBg({ ...bg })}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MapPlannerPage;
|
||||
376
pages/plans.tsx
376
pages/plans.tsx
@@ -1,5 +1,377 @@
|
||||
import MapPlanner from "components/plans";
|
||||
import { Button, ButtonGroup, Flex } from "@chakra-ui/react";
|
||||
import { t, Trans } from "@lingui/macro";
|
||||
import Breadcrumbs from "components/common/Breadcrumbs";
|
||||
import DraggableImageAdder from "components/plans/DraggableImageAdder";
|
||||
import DraggableToolsSelector from "components/plans/DraggableToolsSelector";
|
||||
import StageSelector from "components/plans/StageSelector";
|
||||
import { stages } from "lib/lists/stages";
|
||||
import { NextPage } from "next";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
FaBomb,
|
||||
FaFileDownload,
|
||||
FaFileImage,
|
||||
FaFileUpload,
|
||||
} from "react-icons/fa";
|
||||
|
||||
const MapPlannerPage = () => <MapPlanner />;
|
||||
const MapSketch = dynamic(() => import("components/plans/MapSketch"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export type Tool = "pencil" | "line" | "rectangle" | "circle" | "select";
|
||||
|
||||
export interface PlannerMapBg {
|
||||
view?: "M" | "R";
|
||||
stage: string;
|
||||
mode?: "SZ" | "TC" | "RM" | "CB";
|
||||
tide?: "low" | "mid" | "high";
|
||||
}
|
||||
|
||||
const stageToCode = new Map<string, string>([
|
||||
["Ancho-V Games", "AG"],
|
||||
["Arowana Mall", "AM"],
|
||||
["Blackbelly Skatepark", "BS"],
|
||||
["Camp Triggerfish", "CT"],
|
||||
["Goby Arena", "GA"],
|
||||
["Humpback Pump Track", "HP"],
|
||||
["Inkblot Art Academy", "IA"],
|
||||
["Kelp Dome", "KD"],
|
||||
["Musselforge Fitness", "MF"],
|
||||
["MakoMart", "MK"],
|
||||
["Manta Maria", "MM"],
|
||||
["Moray Towers", "MT"],
|
||||
["New Albacore Hotel", "NA"],
|
||||
["Port Mackerel", "PM"],
|
||||
["Piranha Pit", "PP"],
|
||||
["Snapper Canal", "SC"],
|
||||
["Shellendorf Institute", "SI"],
|
||||
["Starfish Mainstage", "SM"],
|
||||
["Skipper Pavilion", "SP"],
|
||||
["Sturgeon Shipyard", "SS"],
|
||||
["The Reef", "TR"],
|
||||
["Wahoo World", "WH"],
|
||||
["Walleye Warehouse", "WW"],
|
||||
]);
|
||||
|
||||
const plannerMapBgToImage = (bg: PlannerMapBg) => {
|
||||
if (!bg.tide)
|
||||
return `images/plannerMaps/${bg.view} ${stageToCode.get(bg.stage)} ${
|
||||
bg.mode
|
||||
}.png`;
|
||||
|
||||
return `images/plannerMaps/${bg.stage}-${bg.tide}.png`;
|
||||
};
|
||||
|
||||
const defaultValue = {
|
||||
shadowWidth: 0,
|
||||
shadowOffset: 0,
|
||||
enableRemoveSelected: false,
|
||||
fillWithColor: false,
|
||||
fillWithBackgroundColor: false,
|
||||
drawings: [],
|
||||
canUndo: false,
|
||||
canRedo: false,
|
||||
controlledSize: false,
|
||||
sketchWidth: 600,
|
||||
sketchHeight: 600,
|
||||
stretched: true,
|
||||
stretchedX: false,
|
||||
stretchedY: false,
|
||||
originX: "left",
|
||||
originY: "top",
|
||||
expandTools: false,
|
||||
expandControls: false,
|
||||
expandColors: false,
|
||||
expandBack: false,
|
||||
expandImages: false,
|
||||
expandControlled: false,
|
||||
enableCopyPaste: false,
|
||||
backgroundImage: {
|
||||
type: "image",
|
||||
version: "2.4.3",
|
||||
originX: "left",
|
||||
originY: "top",
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: 1127,
|
||||
height: 634,
|
||||
fill: "rgb(0,0,0)",
|
||||
stroke: null,
|
||||
strokeWidth: 0,
|
||||
strokeDashArray: null,
|
||||
strokeLineCap: "butt",
|
||||
strokeLineJoin: "miter",
|
||||
strokeMiterLimit: 4,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
angle: 0,
|
||||
flipX: false,
|
||||
flipY: false,
|
||||
opacity: 1,
|
||||
shadow: null,
|
||||
visible: true,
|
||||
clipTo: null,
|
||||
backgroundColor: "",
|
||||
fillRule: "nonzero",
|
||||
paintFirst: "fill",
|
||||
globalCompositeOperation: "source-over",
|
||||
transformMatrix: null,
|
||||
skewX: 0,
|
||||
skewY: 0,
|
||||
crossOrigin: "",
|
||||
cropX: 0,
|
||||
cropY: 0,
|
||||
src: "/images/plannerMaps/M%20TR%20SZ.png",
|
||||
filters: [],
|
||||
},
|
||||
};
|
||||
|
||||
const MapPlannerPage: NextPage = () => {
|
||||
const fileInput = useRef<HTMLInputElement | null>(null);
|
||||
const sketch = useRef<any>(null);
|
||||
const [tool, setTool] = useState<Tool>("pencil");
|
||||
const [color, setColor] = useState("#f44336");
|
||||
const [canUndo, setCanUndo] = useState(false);
|
||||
const [canRedo, setCanRedo] = useState(false);
|
||||
const [bg, setBg] = useState<PlannerMapBg>({
|
||||
view: "M",
|
||||
stage: "The Reef",
|
||||
mode: "SZ",
|
||||
});
|
||||
const [controlledValue, setControlledValue] = useState(defaultValue);
|
||||
|
||||
const addImageToSketch = (imgSrc: string) => {
|
||||
sketch.current.addImg(imgSrc);
|
||||
setTool("select");
|
||||
};
|
||||
|
||||
const addTextToSketch = () => {
|
||||
sketch.current.addText("Double-click to edit", {
|
||||
fill: color,
|
||||
fontFamily: "lato",
|
||||
stroke: "#000000",
|
||||
strokeWidth: 3,
|
||||
paintFirst: "stroke",
|
||||
});
|
||||
setTool("select");
|
||||
};
|
||||
|
||||
const undo = () => {
|
||||
sketch.current.undo();
|
||||
setCanUndo(sketch.current.canUndo());
|
||||
setCanRedo(sketch.current.canRedo());
|
||||
};
|
||||
|
||||
const redo = () => {
|
||||
sketch.current.redo();
|
||||
setCanUndo(sketch.current.canUndo());
|
||||
setCanRedo(sketch.current.canRedo());
|
||||
};
|
||||
|
||||
const removeSelected = () => {
|
||||
sketch.current.removeSelected();
|
||||
};
|
||||
|
||||
const onSketchChange = () => {
|
||||
if (!sketch.current) return;
|
||||
let prev = canUndo;
|
||||
let now = sketch.current.canUndo();
|
||||
if (prev !== now) {
|
||||
setCanUndo(now);
|
||||
}
|
||||
};
|
||||
|
||||
const getDateFormatted = () => {
|
||||
const today = new Date();
|
||||
const date =
|
||||
today.getFullYear() +
|
||||
"-" +
|
||||
(today.getMonth() + 1) +
|
||||
"-" +
|
||||
today.getDate();
|
||||
const time =
|
||||
today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
|
||||
return date + " " + time;
|
||||
};
|
||||
|
||||
const download = (dataUrl: string, extension: string) => {
|
||||
if (!bg) return;
|
||||
let a = document.createElement("a");
|
||||
document.body.appendChild(a);
|
||||
a.style.display = "none";
|
||||
a.href = dataUrl;
|
||||
a.download = `${bg.stage} plans ${getDateFormatted()}.${extension}`;
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(dataUrl);
|
||||
};
|
||||
|
||||
const handleUpload = () => {
|
||||
if (!fileInput.current) {
|
||||
return;
|
||||
}
|
||||
fileInput.current.click();
|
||||
};
|
||||
|
||||
const files = fileInput.current?.files;
|
||||
|
||||
useEffect(() => {
|
||||
if (!fileInput.current?.files?.length) return;
|
||||
|
||||
const fileObj = fileInput.current.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (event) {
|
||||
const jsonObj = JSON.parse(event.target!.result as any);
|
||||
setControlledValue(jsonObj);
|
||||
|
||||
const imgSrc = jsonObj.backgroundImage.src;
|
||||
const searchFolder = "plannerMaps";
|
||||
const imgName = imgSrc
|
||||
.slice(imgSrc.lastIndexOf(searchFolder) + searchFolder.length + 1, -4)
|
||||
.replace(/%20/g, " ");
|
||||
|
||||
const salmonRunMaps = [
|
||||
"Spawning Grounds",
|
||||
"Marooner's Bay",
|
||||
"Lost Outpost",
|
||||
"Salmonid Smokeyard",
|
||||
"Ruins of Ark Polaris",
|
||||
];
|
||||
let isSalmonRunMap = false;
|
||||
for (const map of salmonRunMaps) {
|
||||
if (imgName.startsWith(map)) {
|
||||
isSalmonRunMap = true;
|
||||
const imageNameParts = imgName.split("-");
|
||||
if (imageNameParts.length > 1) {
|
||||
const tide = imageNameParts[1];
|
||||
setBg({ tide: tide, stage: map });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSalmonRunMap) {
|
||||
const imageNameParts = imgName.split(" ");
|
||||
if (imageNameParts.length > 2) {
|
||||
const view = imageNameParts[0];
|
||||
const mapCode = imageNameParts[1];
|
||||
const mode = imageNameParts[2];
|
||||
let mapName = "";
|
||||
stageToCode.forEach((value, key) => {
|
||||
if (value === mapCode) {
|
||||
mapName = key;
|
||||
}
|
||||
});
|
||||
setBg({ view, stage: mapName, mode });
|
||||
}
|
||||
}
|
||||
};
|
||||
reader.readAsText(fileObj);
|
||||
}, [files]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!sketch.current) return;
|
||||
setCanUndo(false);
|
||||
sketch.current.setBackgroundFromDataUrl(plannerMapBgToImage(bg));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [bg]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Breadcrumbs pages={[{ name: t`Map Planner` }]} />
|
||||
<DraggableToolsSelector
|
||||
tool={tool}
|
||||
setTool={setTool}
|
||||
redo={redo}
|
||||
redoIsDisabled={!canRedo}
|
||||
undo={undo}
|
||||
undoIsDisabled={!canUndo}
|
||||
removeSelected={removeSelected}
|
||||
addText={addTextToSketch}
|
||||
color={color}
|
||||
setColor={(newColor) => setColor(newColor)}
|
||||
/>
|
||||
<DraggableImageAdder
|
||||
addImageToSketch={(imgSrc) => addImageToSketch(imgSrc)}
|
||||
/>
|
||||
<MapSketch
|
||||
sketch={sketch}
|
||||
controlledValue={controlledValue}
|
||||
color={color}
|
||||
onSketchChange={onSketchChange}
|
||||
tool={tool}
|
||||
/>
|
||||
<Flex my={2} justifyContent="space-between">
|
||||
<Button
|
||||
onClick={() => {
|
||||
sketch.current.clear();
|
||||
setBg({ ...bg });
|
||||
}}
|
||||
leftIcon={<FaBomb />}
|
||||
colorScheme="red"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
>
|
||||
<Trans>Clear drawings</Trans>
|
||||
</Button>
|
||||
<ButtonGroup variant="outline" size="sm" isAttached>
|
||||
<Button
|
||||
onClick={() => download(sketch.current.toDataURL(), "png")}
|
||||
leftIcon={<FaFileImage />}
|
||||
>
|
||||
<Trans>Download as .png</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() =>
|
||||
download(
|
||||
"data:text/json;charset=utf-8," +
|
||||
encodeURIComponent(JSON.stringify(sketch.current.toJSON())),
|
||||
"json"
|
||||
)
|
||||
}
|
||||
leftIcon={<FaFileDownload />}
|
||||
>
|
||||
<Trans>Download as .json</Trans>
|
||||
</Button>
|
||||
<Button onClick={() => handleUpload()} leftIcon={<FaFileUpload />}>
|
||||
<Trans>Load from .json</Trans>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
<StageSelector
|
||||
handleChange={(e) => {
|
||||
const newStage = e.target.value;
|
||||
if (newStage === "") {
|
||||
return;
|
||||
}
|
||||
const newIsSalmonRunStage = !stages.includes(newStage as any);
|
||||
const oldIsSalmonRunStage = !stages.includes(bg.stage as any);
|
||||
|
||||
if (newIsSalmonRunStage === oldIsSalmonRunStage) {
|
||||
setBg({ ...bg, stage: e.target.value });
|
||||
return;
|
||||
}
|
||||
|
||||
if (newIsSalmonRunStage) {
|
||||
setBg({ stage: e.target.value, tide: "mid" });
|
||||
return;
|
||||
}
|
||||
|
||||
setBg({ stage: e.target.value, mode: "SZ", view: "M" });
|
||||
}}
|
||||
currentBackground={bg}
|
||||
changeMode={(mode) => setBg({ ...bg, mode })}
|
||||
changeTide={(tide: "low" | "mid" | "high") => setBg({ ...bg, tide })}
|
||||
changeView={(view: "M" | "R") => setBg({ ...bg, view })}
|
||||
/>
|
||||
<input
|
||||
type="file"
|
||||
accept=".json"
|
||||
ref={fileInput}
|
||||
style={{ display: "none" }}
|
||||
onChange={() => setBg({ ...bg })}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MapPlannerPage;
|
||||
|
||||
Reference in New Issue
Block a user