diff --git a/packages/frontend/src/layouts/MainLayout.tsx b/packages/frontend/src/layouts/MainLayout.tsx new file mode 100644 index 0000000..b683e89 --- /dev/null +++ b/packages/frontend/src/layouts/MainLayout.tsx @@ -0,0 +1,36 @@ +import { View } from "@adobe/react-spectrum"; +import { PropsWithChildren } from "react"; + +export default function MainLayout({ children }: PropsWithChildren) { + return ( + <> +
{children}
+ {/* footer */} + + + sauce + {" "} + •{" "} + + code license + {" "} + •{" "} + + template license + + + + ); +} diff --git a/packages/frontend/src/layouts/StickerEditLayout.tsx b/packages/frontend/src/layouts/StickerEditLayout.tsx new file mode 100644 index 0000000..ef24dea --- /dev/null +++ b/packages/frontend/src/layouts/StickerEditLayout.tsx @@ -0,0 +1,72 @@ +import SpectrumLink from "@/modules/editor/SpectrumLink"; +import { useApiSettings } from "@/modules/stickers/useApiSettings"; +import { useSticker } from "@/modules/stickers/useSticker"; +import { Button, Meter, Text, View } from "@adobe/react-spectrum"; +import ArrowLeft from "@spectrum-icons/workflow/ArrowLeft"; +import ArrowRight from "@spectrum-icons/workflow/ArrowRight"; +import Head from "next/head"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import { PropsWithChildren } from "react"; + +export default function StickerEditLayout({ children }: PropsWithChildren) { + const router = useRouter(); + const step = Number(router.asPath.match(/step\/(\d)/)?.[1]); + const prev = step == 1 ? null : step - 1; + const next = step == 4 ? null : step + 1; + const stickerId = String(router.query.stickerId); + const { sticker } = useSticker(stickerId); + const configString = router.query.config + ? `?config=${router.query.config}` + : ""; + + useApiSettings(); + + return ( + <> + + + {sticker.displayName} Telegram Sticker Template - Avoo's Sticker Stash + + + + + + {prev && ( + + )} + +
{children}
+
+ + {next ? ( + + ) : ( + + )} + + ); +} diff --git a/packages/frontend/src/modules/editor/SpectrumLink.tsx b/packages/frontend/src/modules/editor/SpectrumLink.tsx new file mode 100644 index 0000000..300dc13 --- /dev/null +++ b/packages/frontend/src/modules/editor/SpectrumLink.tsx @@ -0,0 +1,23 @@ +import Link from "next/link"; +import { AnchorHTMLAttributes, FC, PropsWithChildren } from "react"; + +interface Props + extends PropsWithChildren, + AnchorHTMLAttributes { + href: string; +} + +/** + * Link that is compatible with spectrum buttons + */ +const SpectrumLink: FC = ({ href, children, ...rest }) => { + return ( + + + {children} + + + ); +}; + +export default SpectrumLink; diff --git a/packages/frontend/src/modules/editor/StepDescription.tsx b/packages/frontend/src/modules/editor/StepDescription.tsx new file mode 100644 index 0000000..2537a6b --- /dev/null +++ b/packages/frontend/src/modules/editor/StepDescription.tsx @@ -0,0 +1,10 @@ +import { Well } from "@react-spectrum/well"; +import { FC, PropsWithChildren } from "react"; + +interface Props extends PropsWithChildren {} + +const StepDescription: FC = (props) => { + return {props.children}; +}; + +export default StepDescription; diff --git a/packages/frontend/src/modules/editor/StepStickerView.tsx b/packages/frontend/src/modules/editor/StepStickerView.tsx new file mode 100644 index 0000000..d34d83c --- /dev/null +++ b/packages/frontend/src/modules/editor/StepStickerView.tsx @@ -0,0 +1,48 @@ +import StickerRenderer from "@/modules/editor/StickerRenderer"; +import { View } from "@adobe/react-spectrum"; +import { FC } from "react"; +import { InView } from "react-intersection-observer"; +import { Lottie } from "tg-sticker-creator"; + +interface Props { + lottie: Lottie | null; + sticky: boolean; +} + +const StepStickerView: FC = (props) => { + return ( +
+ + + {({ inView, ref, entry }) => ( +
+ {inView ? ( + + ) : ( +
+ )} +
+ )} +
+
+
+ ); +}; + +export default StepStickerView; diff --git a/packages/frontend/src/modules/editor/StepTemplateChanger.tsx b/packages/frontend/src/modules/editor/StepTemplateChanger.tsx new file mode 100644 index 0000000..6b1f01c --- /dev/null +++ b/packages/frontend/src/modules/editor/StepTemplateChanger.tsx @@ -0,0 +1,37 @@ +import { Item, Menu, MenuTrigger, Text } from "@adobe/react-spectrum"; +import { ActionButton } from "@react-spectrum/button"; +import SwitchIcon from "@spectrum-icons/workflow/Switch"; +import { useRouter } from "next/router"; +import { FC, PropsWithChildren } from "react"; + +interface Props { + step: number; + stickers: { id: string; name: string }[]; +} + +const StepTemplateChanger: FC = (props) => { + const router = useRouter(); + const configString = router.query.config + ? `?config=${router.query.config}` + : ""; + + return ( + + + + Change Template + + + router.replace(`/edit/${key}/step/${props.step}${configString}`) + } + > + {props.stickers.map((s) => ( + {s.name} + ))} + + + ); +}; + +export default StepTemplateChanger; diff --git a/packages/frontend/src/modules/editor/StepToolbarContainer.tsx b/packages/frontend/src/modules/editor/StepToolbarContainer.tsx new file mode 100644 index 0000000..ad21012 --- /dev/null +++ b/packages/frontend/src/modules/editor/StepToolbarContainer.tsx @@ -0,0 +1,25 @@ +import { View } from "@adobe/react-spectrum"; +import { FC, PropsWithChildren } from "react"; + +interface Props extends PropsWithChildren {} + +const StepToolbarContainer: FC = (props) => { + return ( + + {props.children} + + ); +}; + +export default StepToolbarContainer; diff --git a/packages/frontend/src/modules/export/AddToSetButton.tsx b/packages/frontend/src/modules/export/AddToSetButton.tsx index 9e9047f..27ac646 100644 --- a/packages/frontend/src/modules/export/AddToSetButton.tsx +++ b/packages/frontend/src/modules/export/AddToSetButton.tsx @@ -9,7 +9,7 @@ import { import Export from "@spectrum-icons/workflow/Export"; import { useAtom } from "jotai"; import { useRouter } from "next/router"; -import { FC, useCallback, useState } from "react"; +import { FC, ReactNode, useCallback, useState } from "react"; import { Lottie, optimizeFilesize } from "tg-sticker-creator"; import { paletteAtom } from "../palette/ColorList"; import { configAtom } from "../stickers/configAtom"; @@ -18,8 +18,12 @@ import { authAtom } from "./auth"; import { gzip } from "./gzip"; import { saveSticker } from "./requests"; +type ActionType = "save" | "add" | "saveAndAdd"; + interface Props { lottie: Lottie; + action: ActionType; + children: ReactNode; } const AddToSetButton: FC = (props) => { @@ -30,55 +34,43 @@ const AddToSetButton: FC = (props) => { const router = useRouter(); const [loading, setLoading] = useState(false); - const save = useCallback( - async (key: string | number) => { - if (!auth.data || !auth.type) throw new Error("missing auth"); - setLoading(true); - try { - const file = await gzip(optimizeFilesize(props.lottie.clone())); - // TODO: refactor - const response = await saveSticker({ - settings: JSON.stringify(config), - palette: JSON.stringify(colors), - file, - emojis: sticker.emojis.map((e) => e.emoji).join(""), - authData: auth.data, - authType: auth.type, - sticker: sticker.id, - action: String(key) as "save" | "add" | "saveAndAdd", - }); - if (key === "save" || key === "saveAndAdd") { - router.push("/"); // TODO: close gui if used from within bot? - } - console.log("response", response); - } catch (error: any) { - alert(error.message || error); // TODO: proper error handling - console.log(error); - } finally { - setLoading(false); + const save = useCallback(async () => { + if (!auth.data || !auth.type) throw new Error("missing auth"); + setLoading(true); + try { + const file = await gzip(optimizeFilesize(props.lottie.clone())); + // TODO: refactor + const response = await saveSticker({ + settings: JSON.stringify(config), + palette: JSON.stringify(colors), + file, + emojis: sticker.emojis.map((e) => e.emoji).join(""), + authData: auth.data, + authType: auth.type, + sticker: sticker.id, + action: props.action, + }); + if (props.action === "save" || props.action === "saveAndAdd") { + router.push(router.asPath.replace(/step\/\d/, "step/4")); } - }, - [config, colors, sticker, auth, router, setLoading], - ); + console.log("response", response); + } catch (error: any) { + alert(error.message || error); // TODO: proper error handling + console.log(error); + } finally { + setLoading(false); + } + }, [config, colors, sticker, auth, router, setLoading, props]); return ( -
- - - - Save - Add to Set - Save & Add to Set - - -
+ ); }; diff --git a/packages/frontend/src/modules/export/ExportButton.tsx b/packages/frontend/src/modules/export/ExportButton.tsx new file mode 100644 index 0000000..fb80cf7 --- /dev/null +++ b/packages/frontend/src/modules/export/ExportButton.tsx @@ -0,0 +1,19 @@ +import { Button, Text } from "@adobe/react-spectrum"; +import Gears from "@spectrum-icons/workflow/Gears"; +import { FC } from "react"; +import { useExport } from "./useExport"; + +const ExportButton: FC = () => { + const { downloadJson } = useExport(); + + return ( + <> + + + ); +}; + +export default ExportButton; diff --git a/packages/frontend/src/modules/export/ImportButton.tsx b/packages/frontend/src/modules/export/ImportButton.tsx new file mode 100644 index 0000000..696bf43 --- /dev/null +++ b/packages/frontend/src/modules/export/ImportButton.tsx @@ -0,0 +1,27 @@ +import { Button, DialogContainer, Text } from "@adobe/react-spectrum"; +import Gears from "@spectrum-icons/workflow/Gears"; +import { FC, useCallback, useState } from "react"; +import JsonImportDialog from "./JsonImportDialog"; + +const ImportButton: FC = () => { + const [isOpen, setOpen] = useState(false); + + const handleImport = useCallback(() => { + setOpen(true); + }, [setOpen]); + + return ( + <> + + + setOpen(false)}> + {isOpen && } + + + ); +}; + +export default ImportButton; diff --git a/packages/frontend/src/modules/export/SettingsMenu.tsx b/packages/frontend/src/modules/export/SettingsMenu.tsx deleted file mode 100644 index 6078dfc..0000000 --- a/packages/frontend/src/modules/export/SettingsMenu.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { - ActionButton, - DialogContainer, - Item, - Menu, - MenuTrigger, - Text, -} from "@adobe/react-spectrum"; -import Gears from "@spectrum-icons/workflow/Gears"; -import { FC, useCallback, useState } from "react"; -import JsonImportDialog from "./JsonImportDialog"; -import { useExport } from "./useExport"; - -const SettingsMenu: FC = () => { - const [isOpen, setOpen] = useState(false); - - const { downloadJson } = useExport(); - - const handleExport = useCallback( - (key: string | number) => { - switch (key) { - case "import": - setOpen(true); - break; - case "export": - downloadJson(); - break; - } - }, - [setOpen], - ); - - return ( - <> - - - - Character Export - - - Import - Export - - - - setOpen(false)}> - {isOpen && } - - - ); -}; - -export default SettingsMenu; diff --git a/packages/frontend/src/modules/export/generateTgs.ts b/packages/frontend/src/modules/export/generateTgs.ts index a752704..ec796ce 100644 --- a/packages/frontend/src/modules/export/generateTgs.ts +++ b/packages/frontend/src/modules/export/generateTgs.ts @@ -11,5 +11,5 @@ export const generateTgs = async (props: Props) => { console.log("size in KiB", blob.size / (1 << 10)); - download(blob, `${props.lottie.name || "sticker"}.tgs`, "application/gzip"); + download(blob, `${props.lottie.name || "sticker"}.tgs`, "application/octet-stream"); }; diff --git a/packages/frontend/src/modules/export/gzip.ts b/packages/frontend/src/modules/export/gzip.ts index 8dce941..c75716f 100644 --- a/packages/frontend/src/modules/export/gzip.ts +++ b/packages/frontend/src/modules/export/gzip.ts @@ -12,7 +12,7 @@ export const gzip = async (obj: Lottie) => { const zipped = pako.gzip(tgsString, { level: 9 }); const blob = new Blob([zipped], { - type: "application/gzip", + type: "application/octet-stream", }); // TODO: warning if blob.size > 64 << 10 diff --git a/packages/frontend/src/modules/export/useExport.tsx b/packages/frontend/src/modules/export/useExport.tsx index 340268d..2c8a6ed 100644 --- a/packages/frontend/src/modules/export/useExport.tsx +++ b/packages/frontend/src/modules/export/useExport.tsx @@ -14,7 +14,7 @@ export const useExport = () => { "sticker-settings.json", "application/json", ); - }, [config]); + }, [config, palette]); return { downloadJson, diff --git a/packages/frontend/src/modules/overview/VideoCard.tsx b/packages/frontend/src/modules/overview/VideoCard.tsx index 1dab445..afa5021 100644 --- a/packages/frontend/src/modules/overview/VideoCard.tsx +++ b/packages/frontend/src/modules/overview/VideoCard.tsx @@ -8,11 +8,14 @@ import { } from "@adobe/react-spectrum"; import { Card } from "@react-spectrum/card"; import Edit from "@spectrum-icons/workflow/Edit"; +import Export from "@spectrum-icons/workflow/Export"; import { useRouter } from "next/router"; -import { FC, useCallback } from "react"; +import { FC, PropsWithChildren, useCallback } from "react"; import EmojiList from "../emojis/EmojiList"; import style from "./index.module.css"; import { VideoEntry } from "./VideoEntry"; +import Link from "next/link"; +import SpectrumLink from "../editor/SpectrumLink"; interface Props { entry: VideoEntry; @@ -21,13 +24,10 @@ interface Props { const VideoCard: FC = ({ entry }) => { const router = useRouter(); - const handleNavigation = useCallback(() => { - router.push( - `/edit/${entry.stickerId}?config=${encodeURIComponent( - entry.settingId || "", - )}`, - ); - }, [entry, router]); + const url = (step: number) => + `/edit/${entry.stickerId}/step/${step}?config=${encodeURIComponent( + entry.settingId || "", + )}`; return ( @@ -46,15 +46,19 @@ const VideoCard: FC = ({ entry }) => { - + diff --git a/packages/frontend/src/modules/stickers/effects.ts b/packages/frontend/src/modules/stickers/effects.ts index 541b91d..01e2441 100644 --- a/packages/frontend/src/modules/stickers/effects.ts +++ b/packages/frontend/src/modules/stickers/effects.ts @@ -2,6 +2,7 @@ import { brightnessFilter, colorMixFilter, contrastFilter, + gay, greyscaleFilter, hearts, hypnoEffect2, @@ -34,6 +35,8 @@ export const getCommonEffects = () => [ hypnoEffect2("effect.hypno"), hypnoEffect3("effect.hypno3"), + gay("effect.gay"), + sepiaFilter("effect.color.sepia"), greyscaleFilter("effect.color.greyscale"), saturationFilter("effect.color.saturation"), diff --git a/packages/frontend/src/modules/stickers/useApiSettings.ts b/packages/frontend/src/modules/stickers/useApiSettings.ts index 129dc2f..8d39a09 100644 --- a/packages/frontend/src/modules/stickers/useApiSettings.ts +++ b/packages/frontend/src/modules/stickers/useApiSettings.ts @@ -33,7 +33,7 @@ export const useApiSettings = () => { console.log(e); setUserSettingsLoaded(true); }); - }, [router.query]); + }, [router.query.config]); return { userSettings, diff --git a/packages/frontend/src/modules/stickers/useSticker.ts b/packages/frontend/src/modules/stickers/useSticker.ts index f048789..4241ef3 100644 --- a/packages/frontend/src/modules/stickers/useSticker.ts +++ b/packages/frontend/src/modules/stickers/useSticker.ts @@ -19,12 +19,12 @@ export const stickerAtom = atom({ id: "", }); -export const useSticker = (id: string) => { +export const useSticker = (id?: string) => { const [sticker, setSticker] = useAtom(stickerAtom); const [config, setConfig] = useAtom(configAtom); useEffect(() => { - if (sticker?.id === id) return; + if (sticker?.id === id || !id) return; (async () => { const sticker = await getSticker(id); if (!sticker) { diff --git a/packages/frontend/src/modules/stickers/utilities/hypno.ts b/packages/frontend/src/modules/stickers/utilities/hypno.ts index 02e1c26..9502427 100644 --- a/packages/frontend/src/modules/stickers/utilities/hypno.ts +++ b/packages/frontend/src/modules/stickers/utilities/hypno.ts @@ -147,3 +147,40 @@ export const hypnoEffect3 = (id: string) => return sticker; }, }); + +export const gay = (id: string) => + createFilter({ + mandatory: false, + niceness: 6, + id, + displayName: "Gay", + inputs: { + ringDuration: { + type: "number", + default: 20, + displayName: "Ring Expansion Rate (Frames)", + max: 50, + min: 5, + }, + easing: { + type: "easing", + default: "easeInCubic", + displayName: "Easing", + }, + }, + async apply(sticker, inputs) { + sticker.addLayerBack( + create.shapeLayer().addShapeBack( + createHypnoShape({ + colors: ["red", "orange", "yellow", "green", "blue", "purple"], + frames: sticker.finalFrame, + center: [256, 256], + nrOfFramesARingIsVisibleFor: inputs.ringDuration, + easing: inputs.easing, + }), + ), + ); + + return sticker; + }, + }); diff --git a/packages/frontend/src/pages/edit/[stickerId]/index.tsx b/packages/frontend/src/pages/edit/[stickerId]/index.tsx deleted file mode 100644 index 2542a2a..0000000 --- a/packages/frontend/src/pages/edit/[stickerId]/index.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import StickerBreadcrumb from "@/modules/breadcrumb/StickerBreadcrumb"; -import { animateAtom } from "@/modules/editor/animate"; -import StepGroups from "@/modules/editor/StepGroups"; -import StickerRenderer from "@/modules/editor/StickerRenderer"; -import AddToSetButton from "@/modules/export/AddToSetButton"; -import { authAtom } from "@/modules/export/auth"; -import ExportMenu from "@/modules/export/ExportMenu"; -import SettingsMenu from "@/modules/export/SettingsMenu"; -import ColorList from "@/modules/palette/ColorList"; -import { getSummary } from "@/modules/stickers"; -import { useApiSettings } from "@/modules/stickers/useApiSettings"; -import { useGeneratedSticker, useSticker } from "@/modules/stickers/useSticker"; -import { - Switch, - ActionButton, - Heading, - Item, - Menu, - MenuTrigger, - Text, - View, -} from "@adobe/react-spectrum"; -import SwitchIcon from "@spectrum-icons/workflow/Switch"; -import { useAtom } from "jotai"; -import { GetStaticPaths, GetStaticProps } from "next"; -import Head from "next/head"; -import { useState } from "react"; -import { InView } from "react-intersection-observer"; - -interface Props { - stickerId: string; - stickers: { - id: string; - name: string; - }[]; -} - -export default function Home(props: Props) { - const [stickerId, setStickerId] = useState(props.stickerId); - // const { sticker } = useSticker(props.stickerId); - const { sticker } = useSticker(stickerId); - const { lottie } = useGeneratedSticker(); - const [auth] = useAtom(authAtom); - - const { userSettings } = useApiSettings(); - - const [animatePreviews, setAnimatePreviews] = useAtom(animateAtom); - - return ( - - - - {sticker.displayName} Telegram Sticker Template - Avoo's Sticker Stash - - - -
- Edit - - - Animate Previews - - {auth.type !== "web-app" && } - {auth.type !== "web-app" && lottie && } - - - - Change Template - - setStickerId(key)}> - {props.stickers.map((s) => ( - {s.name} - ))} - - - {lottie && <>{auth.data && }} - - - -
- - - {({ inView, ref, entry }) => ( -
- {inView ? ( - - ) : ( -
- )} -
- )} -
-
-
- -
-
- ); -} - -export const getStaticProps: GetStaticProps = async (context) => { - const stickerId = context.params?.stickerId; - const stickers = await getSummary(); - if (typeof stickerId !== "string") throw new Error("invalid id"); - return { - props: { - stickerId, - stickers: Object.entries(stickers).map( - ([id, { displayName, emojis }]) => ({ - id, - name: `${displayName} - ${emojis.map((e) => e.emoji).join("")}`, - }), - ), - }, - }; -}; - -export const getStaticPaths: GetStaticPaths = async () => { - const stickers = await getSummary(); - return { - paths: Object.keys(stickers).map((stickerId) => ({ - params: { stickerId }, - })), - fallback: false, - }; -}; diff --git a/packages/frontend/src/pages/edit/[stickerId]/step/1.tsx b/packages/frontend/src/pages/edit/[stickerId]/step/1.tsx new file mode 100644 index 0000000..d8e882e --- /dev/null +++ b/packages/frontend/src/pages/edit/[stickerId]/step/1.tsx @@ -0,0 +1,90 @@ +import MainLayout from "@/layouts/MainLayout"; +import StickerEditLayout from "@/layouts/StickerEditLayout"; +import StickerBreadcrumb from "@/modules/breadcrumb/StickerBreadcrumb"; +import StepDescription from "@/modules/editor/StepDescription"; +import StepStickerView from "@/modules/editor/StepStickerView"; +import StepTemplateChanger from "@/modules/editor/StepTemplateChanger"; +import StepToolbarContainer from "@/modules/editor/StepToolbarContainer"; +import { authAtom } from "@/modules/export/auth"; +import ImportButton from "@/modules/export/ImportButton"; +import ColorList from "@/modules/palette/ColorList"; +import { getSummary } from "@/modules/stickers"; +import { useGeneratedSticker, useSticker } from "@/modules/stickers/useSticker"; +import { Heading } from "@adobe/react-spectrum"; +import { useAtom } from "jotai"; +import { GetStaticPaths, GetStaticProps } from "next"; +import { useRouter } from "next/router"; + +interface Props { + stickerId: string; + stickers: { + id: string; + name: string; + }[]; +} + +export default function Home(props: Props) { + const router = useRouter(); + const stickerId = String(router.query.stickerId); + const step = Number(router.query.step || 1); + const { sticker } = useSticker(); + const { lottie } = useGeneratedSticker(); + const [auth] = useAtom(authAtom); + + return ( +
+ Create Palette + + Step 1: Choose the main colors. These are usually all your fur colors + and other colors that you want to use more than once. +
+ When you're done, continue to the next step with the button at the + bottom. You can go back to this step later if you need to. +
+ + {auth.type !== "web-app" && } + + + + + +
+ ); +} + +Home.getLayout = function getLayout(page: any) { + return ( + + {page} + + ); +}; + +export const getStaticProps: GetStaticProps = async (context) => { + const stickerId = context.params?.stickerId; + const stickers = await getSummary(); + if (typeof stickerId !== "string") throw new Error("invalid id"); + return { + props: { + stickerId, + stickers: Object.entries(stickers).map( + ([id, { displayName, emojis }]) => ({ + id, + name: `${displayName} - ${emojis.map((e) => e.emoji).join("")}`, + }), + ), + }, + }; +}; + +export const getStaticPaths: GetStaticPaths = async () => { + const stickers = await getSummary(); + return { + paths: Object.keys(stickers).flatMap((stickerId) => [ + { + params: { stickerId }, + }, + ]), + fallback: false, + }; +}; diff --git a/packages/frontend/src/pages/edit/[stickerId]/step/2.tsx b/packages/frontend/src/pages/edit/[stickerId]/step/2.tsx new file mode 100644 index 0000000..7f52e4c --- /dev/null +++ b/packages/frontend/src/pages/edit/[stickerId]/step/2.tsx @@ -0,0 +1,94 @@ +import MainLayout from "@/layouts/MainLayout"; +import StickerEditLayout from "@/layouts/StickerEditLayout"; +import StickerBreadcrumb from "@/modules/breadcrumb/StickerBreadcrumb"; +import { animateAtom } from "@/modules/editor/animate"; +import StepDescription from "@/modules/editor/StepDescription"; +import StepGroups from "@/modules/editor/StepGroups"; +import StepStickerView from "@/modules/editor/StepStickerView"; +import StepTemplateChanger from "@/modules/editor/StepTemplateChanger"; +import StepToolbarContainer from "@/modules/editor/StepToolbarContainer"; +import { authAtom } from "@/modules/export/auth"; +import { getSummary } from "@/modules/stickers"; +import { useGeneratedSticker, useSticker } from "@/modules/stickers/useSticker"; +import { Heading, Switch } from "@adobe/react-spectrum"; +import { useAtom } from "jotai"; +import { GetStaticPaths, GetStaticProps } from "next"; +import { useRouter } from "next/router"; + +interface Props { + stickerId: string; + stickers: { + id: string; + name: string; + }[]; +} + +export default function Home(props: Props) { + const router = useRouter(); + const step = Number(router.query.step || 2); + const { sticker } = useSticker(); + const { lottie } = useGeneratedSticker(); + const [auth] = useAtom(authAtom); + + const [animatePreviews, setAnimatePreviews] = useAtom(animateAtom); + + return ( +
+ Edit Sticker + + Step 2: Edit the patterns and colors to match your character. Make sure + that you switch through all templates via "Change Template" to ensure + everything is colored correctly - different templates reveal different + body parts. +
+ Click the button at the bottom to continue. +
+ + + Animate Previews + + + + + + +
+ ); +} + +Home.getLayout = function getLayout(page: any) { + return ( + + {page} + + ); +}; + +export const getStaticProps: GetStaticProps = async (context) => { + const stickerId = context.params?.stickerId; + const stickers = await getSummary(); + if (typeof stickerId !== "string") throw new Error("invalid id"); + return { + props: { + stickerId, + stickers: Object.entries(stickers).map( + ([id, { displayName, emojis }]) => ({ + id, + name: `${displayName} - ${emojis.map((e) => e.emoji).join("")}`, + }), + ), + }, + }; +}; + +export const getStaticPaths: GetStaticPaths = async () => { + const stickers = await getSummary(); + return { + paths: Object.keys(stickers).flatMap((stickerId) => [ + { + params: { stickerId }, + }, + ]), + fallback: false, + }; +}; diff --git a/packages/frontend/src/pages/edit/[stickerId]/step/3.tsx b/packages/frontend/src/pages/edit/[stickerId]/step/3.tsx new file mode 100644 index 0000000..ee5dbbe --- /dev/null +++ b/packages/frontend/src/pages/edit/[stickerId]/step/3.tsx @@ -0,0 +1,94 @@ +import MainLayout from "@/layouts/MainLayout"; +import StickerEditLayout from "@/layouts/StickerEditLayout"; +import StickerBreadcrumb from "@/modules/breadcrumb/StickerBreadcrumb"; +import { animateAtom } from "@/modules/editor/animate"; +import StepDescription from "@/modules/editor/StepDescription"; +import StepStickerView from "@/modules/editor/StepStickerView"; +import StepToolbarContainer from "@/modules/editor/StepToolbarContainer"; +import AddToSetButton from "@/modules/export/AddToSetButton"; +import { authAtom } from "@/modules/export/auth"; +import ExportButton from "@/modules/export/ExportButton"; +import { getSummary } from "@/modules/stickers"; +import { useGeneratedSticker, useSticker } from "@/modules/stickers/useSticker"; +import { Heading } from "@adobe/react-spectrum"; +import { useAtom } from "jotai"; +import { GetStaticPaths, GetStaticProps } from "next"; +import { useRouter } from "next/router"; + +interface Props { + stickerId: string; + stickers: { + id: string; + name: string; + }[]; +} + +export default function Home(props: Props) { + const router = useRouter(); + const step = Number(router.query.step || 3); + const { sticker } = useSticker(); + const { lottie } = useGeneratedSticker(); + const [auth] = useAtom(authAtom); + + return ( +
+ Save + + Step 3: Export your character to a file that you can use in step 1 to + restore your character, or save your character to your account if you're + signed in via the bot. + + + {auth.type !== "web-app" && } + {lottie && ( + <> + {auth.data && ( + + Save Configuration to Your Account + + )} + + )} + + + +
+ ); +} + +Home.getLayout = function getLayout(page: any) { + return ( + + {page} + + ); +}; + +export const getStaticProps: GetStaticProps = async (context) => { + const stickerId = context.params?.stickerId; + const stickers = await getSummary(); + if (typeof stickerId !== "string") throw new Error("invalid id"); + return { + props: { + stickerId, + stickers: Object.entries(stickers).map( + ([id, { displayName, emojis }]) => ({ + id, + name: `${displayName} - ${emojis.map((e) => e.emoji).join("")}`, + }), + ), + }, + }; +}; + +export const getStaticPaths: GetStaticPaths = async () => { + const stickers = await getSummary(); + return { + paths: Object.keys(stickers).flatMap((stickerId) => [ + { + params: { stickerId }, + }, + ]), + fallback: false, + }; +}; diff --git a/packages/frontend/src/pages/edit/[stickerId]/step/4.tsx b/packages/frontend/src/pages/edit/[stickerId]/step/4.tsx new file mode 100644 index 0000000..e9d10e5 --- /dev/null +++ b/packages/frontend/src/pages/edit/[stickerId]/step/4.tsx @@ -0,0 +1,107 @@ +import MainLayout from "@/layouts/MainLayout"; +import StickerEditLayout from "@/layouts/StickerEditLayout"; +import StickerBreadcrumb from "@/modules/breadcrumb/StickerBreadcrumb"; +import { animateAtom } from "@/modules/editor/animate"; +import StepDescription from "@/modules/editor/StepDescription"; +import StepGroups from "@/modules/editor/StepGroups"; +import StepStickerView from "@/modules/editor/StepStickerView"; +import StepTemplateChanger from "@/modules/editor/StepTemplateChanger"; +import StepToolbarContainer from "@/modules/editor/StepToolbarContainer"; +import AddToSetButton from "@/modules/export/AddToSetButton"; +import { authAtom } from "@/modules/export/auth"; +import ExportMenu from "@/modules/export/ExportMenu"; +import ColorList from "@/modules/palette/ColorList"; +import { getSummary } from "@/modules/stickers"; +import { useGeneratedSticker, useSticker } from "@/modules/stickers/useSticker"; +import { Heading, Switch } from "@adobe/react-spectrum"; +import { useAtom } from "jotai"; +import { GetStaticPaths, GetStaticProps } from "next"; +import { useRouter } from "next/router"; + +interface Props { + stickerId: string; + stickers: { + id: string; + name: string; + }[]; +} + +export default function Home(props: Props) { + const router = useRouter(); + const step = Number(router.query.step || 4); + const { sticker } = useSticker(); + const { lottie } = useGeneratedSticker(); + const [auth] = useAtom(authAtom); + + const [animatePreviews, setAnimatePreviews] = useAtom(animateAtom); + + return ( +
+ Export + + Step 4: Export your stickers! If you're logged in, you can add them to + your set directly; otherwise export them to .tgs files that you can send + to the @Stickers Telegram bot to create the set by yourself. +
+ You can also export multiple versions of a sticker, like skirt and + non-skirt variants by toggling the relevant options. +
+ + + Animate Previews + + {auth.type !== "web-app" && lottie && } + + {lottie && ( + <> + {auth.data && ( + + Add to Your Set + + )} + + )} + + + + +
+ ); +} + +Home.getLayout = function getLayout(page: any) { + return ( + + {page} + + ); +}; + +export const getStaticProps: GetStaticProps = async (context) => { + const stickerId = context.params?.stickerId; + const stickers = await getSummary(); + if (typeof stickerId !== "string") throw new Error("invalid id"); + return { + props: { + stickerId, + stickers: Object.entries(stickers).map( + ([id, { displayName, emojis }]) => ({ + id, + name: `${displayName} - ${emojis.map((e) => e.emoji).join("")}`, + }), + ), + }, + }; +}; + +export const getStaticPaths: GetStaticPaths = async () => { + const stickers = await getSummary(); + return { + paths: Object.keys(stickers).flatMap((stickerId) => [ + { + params: { stickerId }, + }, + ]), + fallback: false, + }; +};