Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(front): shareable links in playground #43

Merged
merged 17 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions crates/web/frontend/next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
const apiEndpoint = process.env.API_ENDOINT || "http://localhost:3001";

const nextConfig = {
async rewrites() {
return [
{
source: "/api/:path*",
destination: `${apiEndpoint}/:path*`,
},
];
},
webpack: (config) => {
config.experiments = {
asyncWebAssembly: true,
Expand Down
66 changes: 65 additions & 1 deletion crates/web/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion crates/web/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@radix-ui/react-hover-card": "^1.1.2",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-radio-group": "^1.2.1",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slider": "^1.2.1",
Expand All @@ -33,6 +34,7 @@
"@radix-ui/react-toast": "^1.2.2",
"@radix-ui/react-tooltip": "^1.1.3",
"@types/js-beautify": "^1.14.3",
"@types/uuid": "^10.0.0",
"@uiw/codemirror-themes": "^4.23.5",
"@uiw/react-codemirror": "^4.23.5",
"@wasm-tool/wasm-pack-plugin": "^1.7.0",
Expand All @@ -51,9 +53,11 @@
"sonner": "^1.5.0",
"tailwind-merge": "^2.5.3",
"tailwindcss-animate": "^1.0.7",
"uuid": "^10.0.0",
"vaul": "^1.0.0",
"vscode-languageserver-protocol": "^3.17.5",
"webworker-promise": "^0.5.1"
"webworker-promise": "^0.5.1",
"zod": "^3.23.8"
},
"devDependencies": {
"@biomejs/biome": "1.9.3",
Expand Down
20 changes: 18 additions & 2 deletions crates/web/frontend/src/app/page-utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Completion } from "@/model/completion";
import type { Data } from "@/model/data";
import type FileType from "@/model/file-type";
import { Data } from "@/model/data";
import FileType from "@/model/file-type";
import { getShare } from "@/service/share-service";
import type { CompletionContext, CompletionSource } from "@codemirror/autocomplete";
import { toast } from "sonner";
import type PromiseWorker from "webworker-promise";
Expand Down Expand Up @@ -51,3 +52,18 @@ export const getQueryCompletionSource = (
};
};
};

export const importShare = async (shareId: string): Promise<{ input: Data; query: Data }> => {
const toastId = toast.loading("Importing share...");
try {
const share = await getShare(shareId);
toast.success("Share succesfully imported", { id: toastId });
return Promise.resolve({
input: new Data(share.json, FileType.JSON),
query: new Data(share.query, FileType.GQ),
});
} catch (error) {
toast.error(`Error importing share: ${error.message}`, { id: toastId, duration: 5000 });
return Promise.reject(error);
}
};
35 changes: 32 additions & 3 deletions crates/web/frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,36 @@ import { useSettings } from "@/providers/settings-provider";
import { useWorker } from "@/providers/worker-provider";
import type { CompletionSource } from "@codemirror/autocomplete";
import { Link2, Link2Off } from "lucide-react";
import { useCallback, useRef, useState } from "react";
import { useSearchParams } from "next/navigation";
import { type MutableRefObject, Suspense, useCallback, useEffect, useRef, useState } from "react";
import { toast } from "sonner";
import { applyGq, getQueryCompletionSource } from "./page-utils";
import { applyGq, getQueryCompletionSource, importShare } from "./page-utils";
import styles from "./page.module.css";

const ShareLoader = ({
updateInputEditorCallback,
updateQueryEditorCallback,
}: {
updateInputEditorCallback: MutableRefObject<(data: Data) => void>;
updateQueryEditorCallback: MutableRefObject<(data: Data) => void>;
}) => {
const shareId = useSearchParams().get("id");
daavidrgz marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
if (!shareId) return;
importShare(shareId)
.then((data) => {
updateInputEditorCallback?.current(data.input);
updateQueryEditorCallback?.current(data.query);
})
.catch(() => {});
}, [shareId, updateInputEditorCallback, updateQueryEditorCallback]);

return null;
};

const Home = () => {
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);
const [errorMessage, setErrorMessage] = useState<string>();
const [warningMessages, setWarningMessages] = useState<string[]>([]);
const inputContent = useRef<string>("");
const queryContent = useRef<string>("");
Expand Down Expand Up @@ -194,6 +217,12 @@ const Home = () => {
</aside>
</section>
<Footer className="my-auto" />
<Suspense>
<ShareLoader
updateInputEditorCallback={updateInputEditorCallback}
updateQueryEditorCallback={updateQueryEditorCallback}
/>
</Suspense>
</main>
);
};
Expand Down
5 changes: 0 additions & 5 deletions crates/web/frontend/src/components/editor/editor-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ export const exportFile = (data: Data, filename: string) => {
toast.success("File exported succesfully!");
};

export const copyToClipboard = (content: string) => {
navigator.clipboard.writeText(content);
toast.success("Copied to your clipboard!");
};

export const formatCode = async (
data: Data,
indent: number,
Expand Down
3 changes: 1 addition & 2 deletions crates/web/frontend/src/components/editor/editor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import useLazyState from "@/hooks/useLazyState";
import { gqTheme } from "@/lib/theme";
import { cn, isMac } from "@/lib/utils";
import { cn, copyToClipboard, isMac } from "@/lib/utils";
import { Data } from "@/model/data";
import FileType from "@/model/file-type";
import { type LoadingState, loading, notLoading } from "@/model/loading-state";
Expand All @@ -20,7 +20,6 @@ import EditorTitle from "./editor-title";
import { EditorTooLarge } from "./editor-too-large";
import {
convertCode,
copyToClipboard,
exportFile,
formatCode,
getCodemirrorExtensionsByFileType,
Expand Down
2 changes: 2 additions & 0 deletions crates/web/frontend/src/components/header/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { cn } from "@/lib/utils";
import type { Data } from "@/model/data";
import { memo } from "react";
import ExamplesSheet from "../examples-sheet/examples-sheet";
import SharePopup from "../share-popup/share-popup";
import ShortcutPopup from "../shortcut-popup/shortcut-popup";
import StarCount from "../star-count/star-count";

Expand All @@ -28,6 +29,7 @@ const Header = ({ className, onClickExample }: Props) => {
</h1>

<div className="flex justify-end flex-grow basis-0 gap-4">
<SharePopup />
<ThemeButton />
<SettingsSheet />
</div>
Expand Down
10 changes: 5 additions & 5 deletions crates/web/frontend/src/components/import-popup/import-popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { fromString } from "@/model/http-method";
import { type LoadingState, notLoading } from "@/model/loading-state";
import { File, FileUp, Trash } from "lucide-react";
import { type ChangeEvent, useCallback, useMemo, useState } from "react";
import BodyTab from "../body-tab/body-tab";
import HeadersTab from "../headers-tab/headers-tab";
import RequestBodyTab from "../request-body-tab/request-body-tab";
import RequestHeadersTab from "../request-headers-tab/request-headers-tab";
import { Button } from "../ui/button";
import {
Dialog,
Expand Down Expand Up @@ -211,11 +211,11 @@ const ImportPopup = ({
)}
</TabsList>
<TabsContent value="headers" className="overflow-y-auto">
<HeadersTab headers={headers} setHeaders={setHeaders} />
<RequestHeadersTab headers={headers} setHeaders={setHeaders} />
</TabsContent>
{httpMethod === "POST" && (
<TabsContent value="body" className="pb-16 overflow-y-auto min-h-full">
<BodyTab body={instantBody} setBody={setBody} />
<RequestBodyTab body={instantBody} setBody={setBody} />
</TabsContent>
)}
</Tabs>
Expand Down Expand Up @@ -275,7 +275,7 @@ const ImportPopup = ({
>
Cancel
</Button>
<Button className="py-1 px-8" variant="success" type="submit">
<Button className="py-1 px-8" variant="success" type="submit" disabled={!(file || url)}>
Import
</Button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import CodeMirror, { type Extension } from "@uiw/react-codemirror";
import { useCallback, useEffect, useMemo, useState } from "react";
import { formatCode, getCodemirrorExtensionsByFileType } from "../editor/editor-utils";

interface BodyTabProps {
interface RequestBodyTabProps {
body: string;
setBody: (body: string) => void;
}

const BodyTab = ({ body, setBody }: BodyTabProps) => {
const RequestBodyTab = ({ body, setBody }: RequestBodyTabProps) => {
const [focused, setFocused] = useState(false);
const {
settings: {
Expand Down Expand Up @@ -67,4 +67,4 @@ const BodyTab = ({ body, setBody }: BodyTabProps) => {
);
};

export default BodyTab;
export default RequestBodyTab;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ interface HeadersDatalistProps {
id: string;
}

const HeadersDatalist = ({ id }: HeadersDatalistProps) => {
const RequestHeadersDatalist = ({ id }: HeadersDatalistProps) => {
return (
<datalist id={id}>
<option value="Accept" />
Expand All @@ -26,4 +26,4 @@ const HeadersDatalist = ({ id }: HeadersDatalistProps) => {
);
};

export default HeadersDatalist;
export default RequestHeadersDatalist;
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { Trash } from "lucide-react";
import { useCallback } from "react";
import { Checkbox } from "../ui/checkbox";
import { Input } from "../ui/input";
import HeadersDatalist from "./headers-datalist";
import RequestHeadersDatalist from "./request-headers-datalist";

interface HeadersTabProps {
interface RequestHeadersTabProps {
headers: [string, string, boolean][];
setHeaders: (headers: [string, string, boolean][]) => void;
}

const HeadersTab = ({ headers, setHeaders }: HeadersTabProps) => {
const RequestHeadersTab = ({ headers, setHeaders }: RequestHeadersTabProps) => {
const updateHeaders = useCallback(
(index: number, key: string, value: string, enabled: boolean) => {
const newHeaders: [string, string, boolean][] = headers.map((header, i) =>
Expand Down Expand Up @@ -58,7 +58,7 @@ const HeadersTab = ({ headers, setHeaders }: HeadersTabProps) => {
onChange={(e) => updateHeaders(index, e.target.value, header[1], header[2])}
className="w-1/2 p-2 border rounded-md mb-0 peer-data-[state=unchecked]:opacity-50 transition-opacity"
/>
<HeadersDatalist id="header-list" />
<RequestHeadersDatalist id="header-list" />
<Input
type="text"
placeholder="Value"
Expand All @@ -85,4 +85,4 @@ const HeadersTab = ({ headers, setHeaders }: HeadersTabProps) => {
);
};

export default HeadersTab;
export default RequestHeadersTab;
Loading