Skip to content

Commit

Permalink
Pinning qr code after share; shouldFitViewport added
Browse files Browse the repository at this point in the history
  • Loading branch information
ilyabo committed Sep 8, 2024
1 parent 9ff37ad commit 1e7ee1a
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 33 deletions.
69 changes: 40 additions & 29 deletions assets/js/map/share-container.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Separator} from "@radix-ui/react-dropdown-menu";
import {PinIcon, Share2Icon, XIcon} from "lucide-react";
import React, {FC, useCallback, useState} from "react";
import React, {FC, useCallback, useEffect, useState, MouseEvent} from "react";
import {Button} from "../components/ui/button";
import {
DropdownMenu,
Expand All @@ -14,22 +14,28 @@ type Props = {};

const ShareContainer: FC<Props> = (props) => {
const {} = props;
const guid = useAppStore((state) => state.ydoc.guid);
const shareProject = useAppStore((state) => state.shareProject);
const isShared = useAppStore((state) => state.isShared);
const [qrCode, setQrCode] = useState<string | null>(null);
const [open, setOpen] = useState(false);
const [pinned, setPinned] = useState(false);
const handleShare = useCallback(() => {
if (isShared) {
// Copy the URL of the project to the clipboard
navigator.clipboard.writeText(window.location.href);
return;
} else {
// Share the project and update the URL
const guid = shareProject();
history.replaceState({}, "", `/${guid}`);
}
}, [isShared]);
const handleShare = useCallback(
(evt: MouseEvent) => {
if (isShared) {
// Copy the URL of the project to the clipboard
navigator.clipboard.writeText(window.location.href);
return;
} else {
// Share the project and update the URL
setPinned(true);
const guid = shareProject();
history.replaceState({}, "", `/${guid}`);
}
evt.stopPropagation();
},
[isShared]
);

const handleTogglePin = useCallback(() => {
if (!isShared) return;
Expand All @@ -46,31 +52,35 @@ const ShareContainer: FC<Props> = (props) => {
(open: boolean) => {
if (isShared) {
if (!pinned) setOpen(open);
if (open) {
(async () => {
const url = window.location.href;
const QRCode = await import("qrcode");
const dataUrl = await new Promise<string>((res, rej) => {
QRCode.toDataURL(url, (err, str) => {
if (err) rej(err);
else res(str);
});
});
setQrCode(dataUrl);
})();
}
} else {
setOpen(open);
}
},
[isShared, pinned]
);

useEffect(() => {
if (isShared && open) {
(async () => {
const url = window.location.href;
const QRCode = await import("qrcode");
const dataUrl = await new Promise<string>((res, rej) => {
QRCode.toDataURL(url, (err, str) => {
if (err) rej(err);
else res(str);
});
});
setQrCode(dataUrl);
})();
}
}, [open, isShared, guid]);

return (
<DropdownMenu open={open} onOpenChange={handleOpenChange} modal={!isShared}>
<DropdownMenuTrigger asChild>
<Button className="text-xs bg-blue-700 hover:bg-blue-600">Share</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="max-w-[195px] flex flex-col items-center">
<DropdownMenuContent className="max-w-[200px] flex flex-col items-center">
<DropdownMenuItem
className="text-xs flex flex-col gap-1 items-start"
onClick={handleShare}
Expand Down Expand Up @@ -98,10 +108,11 @@ const ShareContainer: FC<Props> = (props) => {
</span>
</>
) : (
<span>Create a new shared project based on this one.</span>
<span>
Create a new shared project based on this one to let others
collaborate with you on it.
</span>
)}
{/* Create a shareable link to this project which will allow others to
collaborate with you on it. */}
</div>
</DropdownMenuItem>
{isShared ? (
Expand Down
5 changes: 3 additions & 2 deletions assets/js/map/use-fit-bounds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {PolygonFeature, useAppStore} from "../store/store";

export function useFitBounds(mapRef: React.RefObject<MapRef>) {
const features = useAppStore((state) => state.features);
const shouldFitViewport = useAppStore((state) => state.shouldFitViewport);
const fitBounds = useCallback(
(duration: number) => {
const [minLng, minLat, maxLng, maxLat] = getFeaturesBounds(features);
Expand All @@ -21,11 +22,11 @@ export function useFitBounds(mapRef: React.RefObject<MapRef>) {

const initialFitDone = useRef(false);
useEffect(() => {
if (!initialFitDone.current && features.length > 0) {
if (!initialFitDone.current && shouldFitViewport) {
fitBounds(0);
initialFitDone.current = true;
}
}, [fitBounds, features]);
}, [fitBounds, shouldFitViewport]);

return {
handleFitBounds: useCallback(() => {
Expand Down
13 changes: 11 additions & 2 deletions assets/js/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type PolygonFeature = FeatureOf<Polygon>;
interface DrawingState {
ydoc: Y.Doc;
yfeaturesUndo: Y.UndoManager;
shouldFitViewport: boolean | undefined; // whether to fit the viewport to the features
indexedDbProvider: IndexeddbPersistence;
features: PolygonFeature[];
mapViewState: ViewState;
Expand Down Expand Up @@ -66,7 +67,13 @@ export const useAppStore = create<DrawingState>((set, get) => {
const syncFeatures = () => {
// Extract features from ydoc and store them in the state for rendering
const features = Array.from(getYFeatures(get().ydoc).values());
set({features});
set({
features,
// If it's the first sync, fit the viewport to the features only if there are any
...(get().shouldFitViewport === undefined && {
shouldFitViewport: features.length > 0,
}),
});
};

return {
Expand All @@ -75,13 +82,15 @@ export const useAppStore = create<DrawingState>((set, get) => {
// Array of features extracted from yarray used for rendering
features: [], // Don't modify this directly, use yfeatures instead
color: rgb(interpolateRainbow(Math.random())).formatHex(),
loadedEmpty: false,
isShared: false,
selectedIds: undefined,
mode: DrawingMode.SELECT,
selectedIndexes: undefined,
hexResolution: 10,
isPanning: false,
socket: undefined,
shouldFitViewport: undefined,

initProject: (guid) => {
const oldGuid = get().ydoc.guid;
Expand Down Expand Up @@ -176,6 +185,7 @@ export const useAppStore = create<DrawingState>((set, get) => {

undo: () => get().yfeaturesUndo?.undo(),
redo: () => get().yfeaturesUndo?.redo(),
clear: () => getYFeatures(get().ydoc).clear(),

setMapViewState: (viewState) => set({mapViewState: viewState}),
setHexResolution: (resolution) => set({hexResolution: resolution}),
Expand All @@ -193,7 +203,6 @@ export const useAppStore = create<DrawingState>((set, get) => {
},
setPanning: (isPanning) => set({isPanning}),
setSelectedIds: (ids) => set({selectedIds: ids}),
clear: () => getYFeatures(get().ydoc).clear(),

addOrUpdateFeatures: (features) => {
for (const feature of features) {
Expand Down

0 comments on commit 1e7ee1a

Please sign in to comment.