2020-10-10 11:29:42 +11:00
|
|
|
import React, { useRef, useState, useContext, useEffect } from "react";
|
2020-05-19 16:21:01 +10:00
|
|
|
import { Button, Flex, Label } from "theme-ui";
|
2020-04-23 15:02:03 +10:00
|
|
|
import shortid from "shortid";
|
2020-10-09 13:10:30 +11:00
|
|
|
import Case from "case";
|
2020-04-23 15:02:03 +10:00
|
|
|
|
2020-09-24 16:54:33 +10:00
|
|
|
import EditMapModal from "./EditMapModal";
|
2020-10-01 15:05:30 +10:00
|
|
|
import EditGroupModal from "./EditGroupModal";
|
2020-10-10 15:32:59 +11:00
|
|
|
import ConfirmModal from "./ConfirmModal";
|
2020-09-24 16:54:33 +10:00
|
|
|
|
2020-04-13 18:15:00 +10:00
|
|
|
import Modal from "../components/Modal";
|
2020-04-23 18:01:40 +10:00
|
|
|
import MapTiles from "../components/map/MapTiles";
|
2020-05-19 16:21:01 +10:00
|
|
|
import ImageDrop from "../components/ImageDrop";
|
2020-07-17 16:19:59 +10:00
|
|
|
import LoadingOverlay from "../components/LoadingOverlay";
|
2020-04-23 11:54:29 +10:00
|
|
|
|
2020-05-01 17:37:01 +10:00
|
|
|
import blobToBuffer from "../helpers/blobToBuffer";
|
2020-09-30 13:58:43 +10:00
|
|
|
import useKeyboard from "../helpers/useKeyboard";
|
2020-10-01 22:32:21 +10:00
|
|
|
import { resizeImage } from "../helpers/image";
|
|
|
|
|
import { useSearch, useGroup, handleItemSelect } from "../helpers/select";
|
2020-10-22 12:07:06 +11:00
|
|
|
import { getMapDefaultInset, getGridSize, gridSizeVaild } from "../helpers/map";
|
2020-04-26 17:12:15 +10:00
|
|
|
|
2020-05-19 16:21:01 +10:00
|
|
|
import MapDataContext from "../contexts/MapDataContext";
|
|
|
|
|
import AuthContext from "../contexts/AuthContext";
|
2020-04-23 11:54:29 +10:00
|
|
|
|
2020-04-26 12:26:32 +10:00
|
|
|
const defaultMapProps = {
|
|
|
|
|
// Grid type
|
2020-05-31 16:25:05 +10:00
|
|
|
showGrid: false,
|
2020-08-07 12:28:50 +10:00
|
|
|
snapToGrid: true,
|
2020-07-15 17:17:11 +10:00
|
|
|
quality: "original",
|
2020-10-01 15:05:30 +10:00
|
|
|
group: "",
|
2020-04-26 12:26:32 +10:00
|
|
|
};
|
|
|
|
|
|
2020-07-13 19:36:38 +10:00
|
|
|
const mapResolutions = [
|
2020-07-15 17:35:36 +10:00
|
|
|
{ size: 512, quality: 0.5, id: "low" },
|
|
|
|
|
{ size: 1024, quality: 0.6, id: "medium" },
|
|
|
|
|
{ size: 2048, quality: 0.7, id: "high" },
|
2020-07-15 17:17:11 +10:00
|
|
|
{ size: 4096, quality: 0.8, id: "ultra" },
|
2020-07-13 19:36:38 +10:00
|
|
|
];
|
|
|
|
|
|
2020-04-23 21:54:58 +10:00
|
|
|
function SelectMapModal({
|
|
|
|
|
isOpen,
|
|
|
|
|
onDone,
|
|
|
|
|
onMapChange,
|
|
|
|
|
onMapStateChange,
|
|
|
|
|
// The map currently being view in the map screen
|
|
|
|
|
currentMap,
|
|
|
|
|
}) {
|
2020-04-24 17:53:42 +10:00
|
|
|
const { userId } = useContext(AuthContext);
|
2020-05-19 16:21:01 +10:00
|
|
|
const {
|
2020-05-19 22:15:08 +10:00
|
|
|
ownedMaps,
|
2020-05-19 16:21:01 +10:00
|
|
|
mapStates,
|
|
|
|
|
addMap,
|
2020-09-30 15:44:48 +10:00
|
|
|
removeMaps,
|
2020-05-19 16:21:01 +10:00
|
|
|
resetMap,
|
|
|
|
|
updateMap,
|
2020-10-01 22:32:21 +10:00
|
|
|
updateMaps,
|
2020-11-26 16:29:10 +11:00
|
|
|
mapsLoading,
|
2020-05-19 16:21:01 +10:00
|
|
|
} = useContext(MapDataContext);
|
2020-04-26 17:12:15 +10:00
|
|
|
|
2020-10-01 15:05:30 +10:00
|
|
|
/**
|
|
|
|
|
* Search
|
|
|
|
|
*/
|
2020-09-30 15:39:56 +10:00
|
|
|
const [search, setSearch] = useState("");
|
2020-10-01 22:32:21 +10:00
|
|
|
const [filteredMaps, filteredMapScores] = useSearch(ownedMaps, search);
|
2020-09-30 15:39:56 +10:00
|
|
|
|
|
|
|
|
function handleSearchChange(event) {
|
|
|
|
|
setSearch(event.target.value);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-01 15:05:30 +10:00
|
|
|
/**
|
|
|
|
|
* Group
|
|
|
|
|
*/
|
|
|
|
|
const [isGroupModalOpen, setIsGroupModalOpen] = useState(false);
|
2020-04-23 11:54:29 +10:00
|
|
|
|
2020-10-01 15:05:30 +10:00
|
|
|
async function handleMapsGroup(group) {
|
|
|
|
|
setIsGroupModalOpen(false);
|
2020-10-01 22:32:21 +10:00
|
|
|
updateMaps(selectedMapIds, { group });
|
2020-10-01 15:05:30 +10:00
|
|
|
}
|
2020-04-30 15:15:12 +10:00
|
|
|
|
2020-10-01 22:32:21 +10:00
|
|
|
const [mapsByGroup, mapGroups] = useGroup(
|
|
|
|
|
ownedMaps,
|
|
|
|
|
filteredMaps,
|
|
|
|
|
!!search,
|
|
|
|
|
filteredMapScores
|
|
|
|
|
);
|
2020-04-13 18:15:00 +10:00
|
|
|
|
2020-10-01 15:05:30 +10:00
|
|
|
/**
|
|
|
|
|
* Image Upload
|
|
|
|
|
*/
|
2020-09-24 16:54:33 +10:00
|
|
|
|
2020-04-13 18:15:00 +10:00
|
|
|
const fileInputRef = useRef();
|
2020-10-01 15:05:30 +10:00
|
|
|
const [imageLoading, setImageLoading] = useState(false);
|
2020-04-13 18:15:00 +10:00
|
|
|
|
2020-05-31 10:53:33 +10:00
|
|
|
async function handleImagesUpload(files) {
|
2020-11-27 21:00:08 +11:00
|
|
|
if (navigator.storage) {
|
|
|
|
|
// Attempt to enable persistant storage
|
|
|
|
|
await navigator.storage.persist();
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-31 10:53:33 +10:00
|
|
|
for (let file of files) {
|
|
|
|
|
await handleImageUpload(file);
|
|
|
|
|
}
|
|
|
|
|
// Set file input to null to allow adding the same image 2 times in a row
|
2020-11-26 16:32:48 +11:00
|
|
|
if (fileInputRef.current) {
|
|
|
|
|
fileInputRef.current.value = null;
|
|
|
|
|
}
|
2020-05-31 10:53:33 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function handleImageUpload(file) {
|
2020-04-23 11:54:29 +10:00
|
|
|
if (!file) {
|
2020-05-31 10:53:33 +10:00
|
|
|
return Promise.reject();
|
2020-04-23 11:54:29 +10:00
|
|
|
}
|
|
|
|
|
let image = new Image();
|
|
|
|
|
setImageLoading(true);
|
2020-04-30 14:24:03 +10:00
|
|
|
|
2020-05-31 10:53:33 +10:00
|
|
|
const buffer = await blobToBuffer(file);
|
|
|
|
|
// Copy file to avoid permissions issues
|
|
|
|
|
const blob = new Blob([buffer]);
|
|
|
|
|
// Create and load the image temporarily to get its dimensions
|
|
|
|
|
const url = URL.createObjectURL(blob);
|
|
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
2020-07-13 19:36:38 +10:00
|
|
|
image.onload = async function () {
|
2020-10-08 19:20:14 +11:00
|
|
|
// Find name and grid size
|
|
|
|
|
let gridSize;
|
|
|
|
|
let name = "Unknown Map";
|
|
|
|
|
if (file.name) {
|
|
|
|
|
if (file.name.matchAll) {
|
|
|
|
|
// Match against a regex to find the grid size in the file name
|
|
|
|
|
// e.g. Cave 22x23 will return [["22x22", "22", "x", "23"]]
|
|
|
|
|
const gridMatches = [...file.name.matchAll(/(\d+) ?(x|X) ?(\d+)/g)];
|
2020-10-22 12:07:06 +11:00
|
|
|
for (let match of gridMatches) {
|
|
|
|
|
const matchX = parseInt(match[1]);
|
|
|
|
|
const matchY = parseInt(match[3]);
|
|
|
|
|
if (
|
|
|
|
|
!isNaN(matchX) &&
|
|
|
|
|
!isNaN(matchY) &&
|
|
|
|
|
gridSizeVaild(matchX, matchY)
|
|
|
|
|
) {
|
2020-10-08 19:20:14 +11:00
|
|
|
gridSize = { x: matchX, y: matchY };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!gridSize) {
|
2020-10-14 09:53:32 +11:00
|
|
|
gridSize = await getGridSize(image);
|
2020-10-08 19:20:14 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove file extension
|
|
|
|
|
name = file.name.replace(/\.[^/.]+$/, "");
|
|
|
|
|
// Removed grid size expression
|
|
|
|
|
name = name.replace(/(\[ ?|\( ?)?\d+ ?(x|X) ?\d+( ?\]| ?\))?/, "");
|
|
|
|
|
// Clean string
|
|
|
|
|
name = name.replace(/ +/g, " ");
|
|
|
|
|
name = name.trim();
|
2020-10-09 13:10:30 +11:00
|
|
|
// Capitalize and remove underscores
|
|
|
|
|
name = Case.capital(name);
|
2020-10-08 19:20:14 +11:00
|
|
|
}
|
|
|
|
|
|
2020-07-13 19:36:38 +10:00
|
|
|
// Create resolutions
|
2020-07-15 17:17:11 +10:00
|
|
|
const resolutions = {};
|
2020-07-13 19:36:38 +10:00
|
|
|
for (let resolution of mapResolutions) {
|
|
|
|
|
if (Math.max(image.width, image.height) > resolution.size) {
|
|
|
|
|
const resized = await resizeImage(
|
|
|
|
|
image,
|
|
|
|
|
resolution.size,
|
|
|
|
|
file.type,
|
|
|
|
|
resolution.quality
|
|
|
|
|
);
|
|
|
|
|
const resizedBuffer = await blobToBuffer(resized.blob);
|
2020-07-15 17:17:11 +10:00
|
|
|
resolutions[resolution.id] = {
|
2020-07-13 19:36:38 +10:00
|
|
|
file: resizedBuffer,
|
|
|
|
|
width: resized.width,
|
|
|
|
|
height: resized.height,
|
|
|
|
|
type: "file",
|
2020-07-15 17:17:11 +10:00
|
|
|
id: resolution.id,
|
|
|
|
|
};
|
2020-07-13 19:36:38 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-01 17:37:01 +10:00
|
|
|
handleMapAdd({
|
2020-05-03 10:44:26 +10:00
|
|
|
// Save as a buffer to send with msgpack
|
2020-05-01 17:37:01 +10:00
|
|
|
file: buffer,
|
2020-07-13 19:36:38 +10:00
|
|
|
resolutions,
|
2020-05-01 17:37:01 +10:00
|
|
|
name,
|
|
|
|
|
type: "file",
|
2020-10-02 15:19:10 +10:00
|
|
|
grid: {
|
2020-10-08 19:20:14 +11:00
|
|
|
size: gridSize,
|
|
|
|
|
inset: getMapDefaultInset(
|
|
|
|
|
image.width,
|
|
|
|
|
image.height,
|
|
|
|
|
gridSize.x,
|
|
|
|
|
gridSize.y
|
|
|
|
|
),
|
2020-10-02 15:19:10 +10:00
|
|
|
type: "square",
|
|
|
|
|
},
|
2020-05-01 17:37:01 +10:00
|
|
|
width: image.width,
|
|
|
|
|
height: image.height,
|
|
|
|
|
id: shortid.generate(),
|
|
|
|
|
created: Date.now(),
|
|
|
|
|
lastModified: Date.now(),
|
2020-09-11 16:56:40 +10:00
|
|
|
lastUsed: Date.now(),
|
2020-05-01 17:37:01 +10:00
|
|
|
owner: userId,
|
|
|
|
|
...defaultMapProps,
|
|
|
|
|
});
|
|
|
|
|
setImageLoading(false);
|
|
|
|
|
URL.revokeObjectURL(url);
|
2020-05-31 10:53:33 +10:00
|
|
|
resolve();
|
2020-05-01 17:37:01 +10:00
|
|
|
};
|
2020-05-31 10:53:33 +10:00
|
|
|
image.onerror = reject;
|
2020-05-01 17:37:01 +10:00
|
|
|
image.src = url;
|
|
|
|
|
});
|
2020-04-20 16:34:38 +10:00
|
|
|
}
|
|
|
|
|
|
2020-04-13 18:15:00 +10:00
|
|
|
function openImageDialog() {
|
|
|
|
|
if (fileInputRef.current) {
|
|
|
|
|
fileInputRef.current.click();
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 21:15:16 +10:00
|
|
|
|
2020-10-01 15:05:30 +10:00
|
|
|
/**
|
|
|
|
|
* Map Controls
|
|
|
|
|
*/
|
|
|
|
|
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
|
|
|
|
// The map selected in the modal
|
|
|
|
|
const [selectedMapIds, setSelectedMapIds] = useState([]);
|
|
|
|
|
|
|
|
|
|
const selectedMaps = ownedMaps.filter((map) =>
|
|
|
|
|
selectedMapIds.includes(map.id)
|
|
|
|
|
);
|
|
|
|
|
const selectedMapStates = mapStates.filter((state) =>
|
|
|
|
|
selectedMapIds.includes(state.mapId)
|
|
|
|
|
);
|
|
|
|
|
|
2020-04-23 15:02:03 +10:00
|
|
|
async function handleMapAdd(map) {
|
2020-05-19 16:21:01 +10:00
|
|
|
await addMap(map);
|
2020-09-30 12:30:33 +10:00
|
|
|
setSelectedMapIds([map.id]);
|
2020-04-23 15:02:03 +10:00
|
|
|
}
|
|
|
|
|
|
2020-10-10 15:32:59 +11:00
|
|
|
const [isMapsRemoveModalOpen, setIsMapsRemoveModalOpen] = useState(false);
|
2020-09-30 12:30:33 +10:00
|
|
|
async function handleMapsRemove() {
|
2020-10-10 15:32:59 +11:00
|
|
|
setIsMapsRemoveModalOpen(false);
|
2020-09-30 15:44:48 +10:00
|
|
|
await removeMaps(selectedMapIds);
|
2020-09-30 12:30:33 +10:00
|
|
|
setSelectedMapIds([]);
|
2020-04-23 21:54:58 +10:00
|
|
|
// Removed the map from the map screen if needed
|
2020-09-30 12:30:33 +10:00
|
|
|
if (currentMap && selectedMapIds.includes(currentMap.id)) {
|
2020-04-24 16:18:48 +10:00
|
|
|
onMapChange(null, null);
|
2020-04-23 21:54:58 +10:00
|
|
|
}
|
2020-04-23 15:02:03 +10:00
|
|
|
}
|
|
|
|
|
|
2020-10-10 15:32:59 +11:00
|
|
|
const [isMapsResetModalOpen, setIsMapsResetModalOpen] = useState(false);
|
2020-10-01 15:05:30 +10:00
|
|
|
async function handleMapsReset() {
|
2020-10-10 15:32:59 +11:00
|
|
|
setIsMapsResetModalOpen(false);
|
2020-10-01 15:05:30 +10:00
|
|
|
for (let id of selectedMapIds) {
|
|
|
|
|
const newState = await resetMap(id);
|
|
|
|
|
// Reset the state of the current map if needed
|
|
|
|
|
if (currentMap && currentMap.id === id) {
|
|
|
|
|
onMapStateChange(newState);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-30 12:30:33 +10:00
|
|
|
// Either single, multiple or range
|
|
|
|
|
const [selectMode, setSelectMode] = useState("single");
|
|
|
|
|
|
2020-10-01 22:32:21 +10:00
|
|
|
function handleMapSelect(map) {
|
|
|
|
|
handleItemSelect(
|
|
|
|
|
map,
|
|
|
|
|
selectMode,
|
|
|
|
|
selectedMapIds,
|
|
|
|
|
setSelectedMapIds,
|
|
|
|
|
mapsByGroup,
|
|
|
|
|
mapGroups
|
|
|
|
|
);
|
2020-04-23 15:02:03 +10:00
|
|
|
}
|
|
|
|
|
|
2020-10-01 15:05:30 +10:00
|
|
|
/**
|
|
|
|
|
* Modal Controls
|
|
|
|
|
*/
|
2020-04-23 20:32:33 +10:00
|
|
|
|
2020-08-06 13:31:46 +10:00
|
|
|
async function handleClose() {
|
|
|
|
|
onDone();
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-19 16:21:01 +10:00
|
|
|
async function handleDone() {
|
2020-07-17 16:19:59 +10:00
|
|
|
if (imageLoading) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-09-30 12:30:33 +10:00
|
|
|
if (selectedMapIds.length === 1) {
|
2020-09-11 16:56:40 +10:00
|
|
|
// Update last used for cache invalidation
|
|
|
|
|
const lastUsed = Date.now();
|
2020-10-24 09:05:16 +11:00
|
|
|
await updateMap(selectedMapIds[0], { lastUsed });
|
|
|
|
|
onMapChange({ ...selectedMaps[0], lastUsed }, selectedMapStates[0]);
|
2020-08-06 13:31:46 +10:00
|
|
|
} else {
|
|
|
|
|
onMapChange(null, null);
|
2020-04-23 17:23:34 +10:00
|
|
|
}
|
2020-04-23 21:54:58 +10:00
|
|
|
onDone();
|
2020-04-23 15:02:03 +10:00
|
|
|
}
|
|
|
|
|
|
2020-10-01 15:05:30 +10:00
|
|
|
/**
|
|
|
|
|
* Shortcuts
|
|
|
|
|
*/
|
2020-09-30 13:58:43 +10:00
|
|
|
function handleKeyDown({ key }) {
|
2020-10-01 16:41:57 +10:00
|
|
|
if (!isOpen) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-09-30 13:58:43 +10:00
|
|
|
if (key === "Shift") {
|
|
|
|
|
setSelectMode("range");
|
|
|
|
|
}
|
|
|
|
|
if (key === "Control" || key === "Meta") {
|
|
|
|
|
setSelectMode("multiple");
|
|
|
|
|
}
|
2020-10-10 15:44:07 +11:00
|
|
|
if (key === "Backspace" || key === "Delete") {
|
|
|
|
|
// Selected maps and none are default
|
|
|
|
|
if (
|
|
|
|
|
selectedMapIds.length > 0 &&
|
|
|
|
|
!selectedMaps.some((map) => map.type === "default")
|
|
|
|
|
) {
|
|
|
|
|
setIsMapsRemoveModalOpen(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-30 13:58:43 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleKeyUp({ key }) {
|
2020-10-01 16:41:57 +10:00
|
|
|
if (!isOpen) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-09-30 13:58:43 +10:00
|
|
|
if (key === "Shift" && selectMode === "range") {
|
|
|
|
|
setSelectMode("single");
|
|
|
|
|
}
|
|
|
|
|
if ((key === "Control" || key === "Meta") && selectMode === "multiple") {
|
|
|
|
|
setSelectMode("single");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
useKeyboard(handleKeyDown, handleKeyUp);
|
|
|
|
|
|
2020-10-10 11:29:42 +11:00
|
|
|
// Set select mode to single when alt+tabing
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
function handleBlur() {
|
|
|
|
|
setSelectMode("single");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window.addEventListener("blur", handleBlur);
|
|
|
|
|
return () => {
|
|
|
|
|
window.removeEventListener("blur", handleBlur);
|
|
|
|
|
};
|
|
|
|
|
}, []);
|
|
|
|
|
|
2020-04-13 18:15:00 +10:00
|
|
|
return (
|
2020-09-06 13:20:05 +10:00
|
|
|
<Modal
|
|
|
|
|
isOpen={isOpen}
|
|
|
|
|
onRequestClose={handleClose}
|
2020-09-06 16:24:23 +10:00
|
|
|
style={{ maxWidth: "542px", width: "calc(100% - 16px)" }}
|
2020-09-06 13:20:05 +10:00
|
|
|
>
|
2020-05-31 10:53:33 +10:00
|
|
|
<ImageDrop onDrop={handleImagesUpload} dropText="Drop map to upload">
|
2020-04-13 18:15:00 +10:00
|
|
|
<input
|
2020-05-31 10:53:33 +10:00
|
|
|
onChange={(event) => handleImagesUpload(event.target.files)}
|
2020-04-13 18:15:00 +10:00
|
|
|
type="file"
|
|
|
|
|
accept="image/*"
|
|
|
|
|
style={{ display: "none" }}
|
2020-05-31 10:53:33 +10:00
|
|
|
multiple
|
2020-04-13 18:15:00 +10:00
|
|
|
ref={fileInputRef}
|
|
|
|
|
/>
|
2020-04-15 21:15:16 +10:00
|
|
|
<Flex
|
|
|
|
|
sx={{
|
|
|
|
|
flexDirection: "column",
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Label pt={2} pb={1}>
|
2020-04-23 18:01:40 +10:00
|
|
|
Select or import a map
|
2020-04-15 21:15:16 +10:00
|
|
|
</Label>
|
2020-04-23 18:01:40 +10:00
|
|
|
<MapTiles
|
2020-10-01 15:05:30 +10:00
|
|
|
maps={mapsByGroup}
|
|
|
|
|
groups={mapGroups}
|
2020-04-23 13:31:54 +10:00
|
|
|
onMapAdd={openImageDialog}
|
2020-09-24 16:54:33 +10:00
|
|
|
onMapEdit={() => setIsEditModalOpen(true)}
|
2020-10-10 15:32:59 +11:00
|
|
|
onMapsReset={() => setIsMapsResetModalOpen(true)}
|
|
|
|
|
onMapsRemove={() => setIsMapsRemoveModalOpen(true)}
|
2020-09-30 12:30:33 +10:00
|
|
|
selectedMaps={selectedMaps}
|
|
|
|
|
selectedMapStates={selectedMapStates}
|
2020-04-23 15:02:03 +10:00
|
|
|
onMapSelect={handleMapSelect}
|
2020-05-19 16:21:01 +10:00
|
|
|
onDone={handleDone}
|
2020-09-30 12:30:33 +10:00
|
|
|
selectMode={selectMode}
|
|
|
|
|
onSelectModeChange={setSelectMode}
|
2020-09-30 15:39:56 +10:00
|
|
|
search={search}
|
|
|
|
|
onSearchChange={handleSearchChange}
|
2020-10-01 15:05:30 +10:00
|
|
|
onMapsGroup={() => setIsGroupModalOpen(true)}
|
2020-04-23 13:31:54 +10:00
|
|
|
/>
|
2020-05-19 16:21:01 +10:00
|
|
|
<Button
|
|
|
|
|
variant="primary"
|
2020-09-30 12:30:33 +10:00
|
|
|
disabled={imageLoading || selectedMapIds.length !== 1}
|
2020-05-19 16:21:01 +10:00
|
|
|
onClick={handleDone}
|
2020-09-24 16:54:33 +10:00
|
|
|
mt={2}
|
2020-05-19 16:21:01 +10:00
|
|
|
>
|
2020-09-24 16:54:33 +10:00
|
|
|
Select
|
2020-04-23 11:54:29 +10:00
|
|
|
</Button>
|
2020-04-13 18:15:00 +10:00
|
|
|
</Flex>
|
2020-05-19 16:21:01 +10:00
|
|
|
</ImageDrop>
|
2020-11-26 16:29:10 +11:00
|
|
|
{(imageLoading || mapsLoading) && <LoadingOverlay bg="overlay" />}
|
2020-09-24 16:54:33 +10:00
|
|
|
<EditMapModal
|
|
|
|
|
isOpen={isEditModalOpen}
|
|
|
|
|
onDone={() => setIsEditModalOpen(false)}
|
2020-09-30 12:30:33 +10:00
|
|
|
map={selectedMaps.length === 1 && selectedMaps[0]}
|
|
|
|
|
mapState={selectedMapStates.length === 1 && selectedMapStates[0]}
|
2020-09-24 16:54:33 +10:00
|
|
|
/>
|
2020-10-01 15:05:30 +10:00
|
|
|
<EditGroupModal
|
|
|
|
|
isOpen={isGroupModalOpen}
|
|
|
|
|
onChange={handleMapsGroup}
|
|
|
|
|
groups={mapGroups.filter(
|
|
|
|
|
(group) => group !== "" && group !== "default"
|
|
|
|
|
)}
|
|
|
|
|
onRequestClose={() => setIsGroupModalOpen(false)}
|
|
|
|
|
// Select the default group by testing whether all selected maps are the same
|
|
|
|
|
defaultGroup={
|
|
|
|
|
selectedMaps.length > 0 &&
|
|
|
|
|
selectedMaps
|
|
|
|
|
.map((map) => map.group)
|
|
|
|
|
.reduce((prev, curr) => (prev === curr ? curr : undefined))
|
|
|
|
|
}
|
|
|
|
|
/>
|
2020-10-10 15:32:59 +11:00
|
|
|
<ConfirmModal
|
|
|
|
|
isOpen={isMapsResetModalOpen}
|
|
|
|
|
onRequestClose={() => setIsMapsResetModalOpen(false)}
|
|
|
|
|
onConfirm={handleMapsReset}
|
|
|
|
|
confirmText="Reset"
|
|
|
|
|
label={`Reset ${selectedMapIds.length} Map${
|
|
|
|
|
selectedMapIds.length > 1 ? "s" : ""
|
|
|
|
|
}`}
|
|
|
|
|
description="This will remove all fog, drawings and tokens from the selected maps."
|
|
|
|
|
/>
|
|
|
|
|
<ConfirmModal
|
|
|
|
|
isOpen={isMapsRemoveModalOpen}
|
|
|
|
|
onRequestClose={() => setIsMapsRemoveModalOpen(false)}
|
|
|
|
|
onConfirm={handleMapsRemove}
|
|
|
|
|
confirmText="Remove"
|
|
|
|
|
label={`Remove ${selectedMapIds.length} Map${
|
|
|
|
|
selectedMapIds.length > 1 ? "s" : ""
|
|
|
|
|
}`}
|
|
|
|
|
description="This operation cannot be undone."
|
|
|
|
|
/>
|
2020-04-13 18:15:00 +10:00
|
|
|
</Modal>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-23 18:01:40 +10:00
|
|
|
export default SelectMapModal;
|