From c91c2d7636a5fc6aaecdb92423c577debedbdbf1 Mon Sep 17 00:00:00 2001 From: lby Date: Wed, 13 Nov 2024 09:02:36 +0800 Subject: [PATCH] fix(web): publish scene and story could use the same alias by mistake (#1238) --- .../Projects/Project/hooks.tsx | 2 +- .../ContentsContainer/Projects/hooks.ts | 13 +- web/src/beta/features/Dashboard/type.ts | 3 +- .../PublishToolsPanel/PublishModal/hooks.ts | 140 ------- .../PublishToolsPanel/PublishModal/index.tsx | 357 ------------------ .../PublishOrUpdateSection.tsx | 79 ++++ .../PublishedOrUpdatedSection.tsx | 106 ++++++ .../PublishOrUpdateModal/index.tsx | 150 ++++++++ .../PublishOrUpdateModal/useAlias.ts | 70 ++++ .../UnpublishModal/index.tsx | 77 ++++ .../Publish/PublishToolsPanel/common.tsx | 64 ++++ .../Editor/Publish/PublishToolsPanel/hooks.ts | 205 ++++------ .../Publish/PublishToolsPanel/index.tsx | 168 ++++----- .../beta/features/Editor/Publish/context.tsx | 7 +- web/src/beta/features/Editor/hooks/index.ts | 58 +-- web/src/beta/features/Editor/hooks/useUI.ts | 62 ++- web/src/beta/features/Editor/index.tsx | 4 +- .../beta/features/ProjectSettings/hooks.ts | 74 ++-- web/src/services/api/projectApi.ts | 4 +- web/src/services/api/publishTypes.ts | 22 ++ web/src/services/api/storytellingApi/index.ts | 3 +- web/src/services/api/toGqlStatus.ts | 10 - web/src/services/i18n/translations/en.yml | 9 +- web/src/services/i18n/translations/ja.yml | 9 +- 24 files changed, 838 insertions(+), 858 deletions(-) delete mode 100644 web/src/beta/features/Editor/Publish/PublishToolsPanel/PublishModal/hooks.ts delete mode 100644 web/src/beta/features/Editor/Publish/PublishToolsPanel/PublishModal/index.tsx create mode 100644 web/src/beta/features/Editor/Publish/PublishToolsPanel/PublishOrUpdateModal/PublishOrUpdateSection.tsx create mode 100644 web/src/beta/features/Editor/Publish/PublishToolsPanel/PublishOrUpdateModal/PublishedOrUpdatedSection.tsx create mode 100644 web/src/beta/features/Editor/Publish/PublishToolsPanel/PublishOrUpdateModal/index.tsx create mode 100644 web/src/beta/features/Editor/Publish/PublishToolsPanel/PublishOrUpdateModal/useAlias.ts create mode 100644 web/src/beta/features/Editor/Publish/PublishToolsPanel/UnpublishModal/index.tsx create mode 100644 web/src/beta/features/Editor/Publish/PublishToolsPanel/common.tsx create mode 100644 web/src/services/api/publishTypes.ts delete mode 100644 web/src/services/api/toGqlStatus.ts diff --git a/web/src/beta/features/Dashboard/ContentsContainer/Projects/Project/hooks.tsx b/web/src/beta/features/Dashboard/ContentsContainer/Projects/Project/hooks.tsx index 45a284f66a..43beef5140 100644 --- a/web/src/beta/features/Dashboard/ContentsContainer/Projects/Project/hooks.tsx +++ b/web/src/beta/features/Dashboard/ContentsContainer/Projects/Project/hooks.tsx @@ -5,11 +5,11 @@ import { useStorytellingFetcher, useProjectFetcher } from "@reearth/services/api"; +import { toPublishmentStatus } from "@reearth/services/api/publishTypes"; import { useT } from "@reearth/services/i18n"; import { MouseEvent, useCallback, useEffect, useMemo, useState } from "react"; import { Project as ProjectType } from "../../../type"; -import { toPublishmentStatus } from "../hooks"; type Props = { project: ProjectType; diff --git a/web/src/beta/features/Dashboard/ContentsContainer/Projects/hooks.ts b/web/src/beta/features/Dashboard/ContentsContainer/Projects/hooks.ts index 4a02967a81..e98cc1180b 100644 --- a/web/src/beta/features/Dashboard/ContentsContainer/Projects/hooks.ts +++ b/web/src/beta/features/Dashboard/ContentsContainer/Projects/hooks.ts @@ -2,9 +2,9 @@ import { useApolloClient } from "@apollo/client"; import useLoadMore from "@reearth/beta/hooks/useLoadMore"; import { ManagerLayout } from "@reearth/beta/ui/components/ManagerBase"; import { useProjectFetcher } from "@reearth/services/api"; +import { toPublishmentStatus } from "@reearth/services/api/publishTypes"; import { ProjectSortField, - PublishmentStatus, SortDirection, Visualizer } from "@reearth/services/gql"; @@ -303,17 +303,6 @@ export default (workspaceId?: string) => { }; }; -export const toPublishmentStatus = (s?: PublishmentStatus) => { - switch (s) { - case PublishmentStatus.Public: - return "published"; - case PublishmentStatus.Limited: - return "limited"; - default: - return "unpublished"; - } -}; - const pagination = (sort?: SortType) => { let first, last; let sortBy; diff --git a/web/src/beta/features/Dashboard/type.ts b/web/src/beta/features/Dashboard/type.ts index ea0a0ed8a4..c290b720f9 100644 --- a/web/src/beta/features/Dashboard/type.ts +++ b/web/src/beta/features/Dashboard/type.ts @@ -1,4 +1,5 @@ import { IconName } from "@reearth/beta/lib/reearth-ui"; +import { PublishStatus } from "@reearth/services/api/publishTypes"; import { TeamMember } from "@reearth/services/gql"; import { ProjectType } from "@reearth/types"; import { ReactNode } from "react"; @@ -7,7 +8,7 @@ export type Project = { id: string; name: string; imageUrl?: string | null; - status?: "published" | "limited" | "unpublished"; + status?: PublishStatus; isArchived?: boolean; description?: string; sceneId?: string; diff --git a/web/src/beta/features/Editor/Publish/PublishToolsPanel/PublishModal/hooks.ts b/web/src/beta/features/Editor/Publish/PublishToolsPanel/PublishModal/hooks.ts deleted file mode 100644 index 8b0ca6b76f..0000000000 --- a/web/src/beta/features/Editor/Publish/PublishToolsPanel/PublishModal/hooks.ts +++ /dev/null @@ -1,140 +0,0 @@ -import generateRandomString from "@reearth/beta/utils/generate-random-string"; -import { useState, useEffect, useCallback } from "react"; - -import { publishingType } from "./"; - -export type PublishStatus = "published" | "limited" | "unpublished"; - -export type Validation = "too short" | "not match"; -export type CopiedItemKey = { - url?: boolean; - embedCode?: boolean; -}; - -export default ( - publishing?: publishingType, - publishStatus?: PublishStatus, - defaultAlias?: string, - onPublish?: ( - alias: string | undefined, - publishStatus: PublishStatus - ) => void | Promise, - onClose?: () => void, - onAliasValidate?: (alias: string) => void, - onCopyToClipBoard?: () => void -) => { - const [copiedKey, setCopiedKey] = useState(); - const [alias, changeAlias] = useState(defaultAlias); - const [validation, changeValidation] = useState(); - const [statusChanged, setStatusChange] = useState(false); - const [showOptions, setOptions] = useState(!defaultAlias); - const [searchIndex, setSearchIndex] = useState(false); - - useEffect(() => { - setSearchIndex(!!(publishStatus === "published")); - }, [publishStatus]); - - const handleSearchIndexChange = useCallback(() => { - setSearchIndex(!searchIndex); - }, [searchIndex]); - - const handleCopyToClipBoard = useCallback( - (key: keyof CopiedItemKey, value: string | undefined) => () => { - if (!value) return; - setCopiedKey((prevState) => ({ - ...prevState, - [key]: true - })); - navigator.clipboard.writeText(value); - onCopyToClipBoard?.(); - }, - [onCopyToClipBoard] - ); - - const validate = useCallback( - (a?: string) => { - if (!a) { - changeValidation(undefined); - return; - } - if (a.length < 5) { - changeValidation("too short"); - } else if (!/^[A-Za-z0-9_-]*$/.test(a)) { - changeValidation("not match"); - } else { - changeValidation(undefined); - onAliasValidate?.(a); - } - }, - [onAliasValidate] - ); - - const generateAlias = useCallback(() => { - const str = generateRandomString(10); - changeAlias(str); - return str; - }, []); - - const onAliasChange = useCallback( - (value?: string) => { - const a = value || generateAlias(); - changeAlias(a); - validate(a); - }, - [validate, generateAlias] - ); - - const handleClose = useCallback(() => { - onClose?.(); - setTimeout(() => { - onAliasChange(defaultAlias); - setStatusChange(false); - setOptions(defaultAlias ? false : true); - }, 500); - }, [onClose, defaultAlias, onAliasChange]); - - useEffect(() => { - onAliasChange(defaultAlias); - }, [defaultAlias, onAliasChange]); - - const handlePublish = useCallback(async () => { - if (!publishing) return; - const a = - publishing !== "unpublishing" ? alias || generateAlias() : undefined; - - const mode = - publishing === "unpublishing" - ? "unpublished" - : !searchIndex - ? "limited" - : "published"; - await onPublish?.(a, mode); - if (publishing === "unpublishing") { - handleClose?.(); - } else { - setStatusChange(true); - } - }, [ - alias, - generateAlias, - onPublish, - publishing, - searchIndex, - setStatusChange, - handleClose - ]); - - return { - statusChanged, - alias, - validation, - copiedKey, - showOptions, - searchIndex, - handlePublish, - handleClose, - handleCopyToClipBoard, - handleSearchIndexChange, - setOptions - }; -}; diff --git a/web/src/beta/features/Editor/Publish/PublishToolsPanel/PublishModal/index.tsx b/web/src/beta/features/Editor/Publish/PublishToolsPanel/PublishModal/index.tsx deleted file mode 100644 index 558681cb67..0000000000 --- a/web/src/beta/features/Editor/Publish/PublishToolsPanel/PublishModal/index.tsx +++ /dev/null @@ -1,357 +0,0 @@ -import { - Button, - Icon, - Typography, - ModalPanel, - Modal -} from "@reearth/beta/lib/reearth-ui/components"; -import { CommonField, SwitchField } from "@reearth/beta/ui/fields"; -import { useT } from "@reearth/services/i18n"; -import { styled, useTheme } from "@reearth/services/theme"; -import { useMemo, FC } from "react"; - -import { usePublishPage } from "../../context"; - -import useHooks, { type PublishStatus } from "./hooks"; - -export type publishingType = "publishing" | "updating" | "unpublishing"; - -type Props = { - isVisible: boolean; - className?: string; - loading?: boolean; - publishStatus?: PublishStatus; - projectId?: string; - projectAlias?: string; - validAlias?: boolean; - publishing?: publishingType; - validatingAlias?: boolean; - url?: string[]; - onPublish: ( - alias: string | undefined, - publishStatus: PublishStatus - ) => void | Promise; - onClose?: () => void; - onCopyToClipBoard?: () => void; - onAliasValidate?: (alias: string) => void; - onNavigateToSettings?: ( - page?: "story" | "public" | "asset" | "plugin" | undefined, - subId?: string - ) => void; -}; - -const PublishModal: FC = ({ - isVisible, - loading, - publishing, - publishStatus, - projectAlias, - validAlias, - validatingAlias, - url, - onClose, - onPublish, - onCopyToClipBoard, - onAliasValidate, - onNavigateToSettings -}) => { - const t = useT(); - const theme = useTheme(); - - const { selectedProjectType, storyId } = usePublishPage(); - - const isStory = selectedProjectType === "story"; - - const { - statusChanged, - alias, - validation, - handlePublish, - handleClose, - handleCopyToClipBoard, - handleSearchIndexChange - } = useHooks( - publishing, - publishStatus, - projectAlias, - onPublish, - onClose, - onAliasValidate, - onCopyToClipBoard - ); - - const purl = useMemo(() => { - return ( - (url?.[0] ?? "") + (alias?.replace("/", "") ?? "") + (url?.[1] ?? "") - ); - }, [alias, url]); - - const embedCode = useMemo( - () => - `;`, - [purl] - ); - - const publishDisabled = useMemo( - () => - loading || - ((publishing === "publishing" || publishing === "updating") && - (!alias || !!validation || validatingAlias || !validAlias)), - [alias, loading, publishing, validation, validAlias, validatingAlias] - ); - - const modalTitleText = useMemo(() => { - return statusChanged - ? t("Congratulations!") - : publishing === "publishing" - ? isStory - ? t(`Publish your story`) - : t(`Publish your scene`) - : publishing === "updating" - ? isStory - ? t(`Update your story`) - : t(`Update your scene`) - : ""; - }, [t, statusChanged, publishing, isStory]); - - const primaryButtonText = useMemo(() => { - return statusChanged - ? t("Ok") - : publishing === "publishing" - ? t("Publish") - : publishing === "updating" - ? t("Update") - : t("Unpublish"); - }, [t, statusChanged, publishing]); - - const secondaryButtonText = useMemo( - () => (!statusChanged ? t("Cancel") : t("Ok")), - [t, statusChanged] - ); - - const updateDescriptionText = useMemo(() => { - return publishing === "updating" - ? isStory - ? t( - `Your published story will be updated. This means all current changes will overwrite the current published story.` - ) - : t( - `Your published scene will be updated. This means all current changes will overwrite the current published scene.` - ) - : isStory - ? t( - `Your story will be published. This means anybody with the below URL will be able to view this story.` - ) - : t( - `Your scene will be published. This means anybody with the below URL will be able to view this scene.` - ); - }, [t, publishing, isStory]); - - const actions = useMemo( - () => ( - <> -