diff --git a/server/pkg/builtin/manifest.yml b/server/pkg/builtin/manifest.yml index f4aa3c20bc..12926b5e98 100644 --- a/server/pkg/builtin/manifest.yml +++ b/server/pkg/builtin/manifest.yml @@ -2318,6 +2318,12 @@ extensions: - id: cameraPosition type: camera title: Camera position + - id: cameraDuration + type: number + title: Duration + suffix: s + min: 0 + defaultValue: 2 - id: timePoint title: Time Point Setting fields: diff --git a/web/src/beta/components/Button/index.tsx b/web/src/beta/components/Button/index.tsx index 4ad59d0513..9a0a3ef4c8 100644 --- a/web/src/beta/components/Button/index.tsx +++ b/web/src/beta/components/Button/index.tsx @@ -108,7 +108,7 @@ const StyledButton = styled.button` ? theme.dangerous.main : buttonType === "primary" ? theme.primary.main - : theme.secondary.main}; + : theme.secondary.strong}; &:active, &:hover { background: ${({ buttonType, disabled, theme }) => diff --git a/web/src/beta/components/ListItem/index.tsx b/web/src/beta/components/ListItem/index.tsx index 8bec94ca25..457523017a 100644 --- a/web/src/beta/components/ListItem/index.tsx +++ b/web/src/beta/components/ListItem/index.tsx @@ -34,15 +34,14 @@ const ListItem: FC = ({ onOpenChange, }) => { return ( - - + + {typeof children === "string" ? ( {children} ) : ( children )} - {actionContent && ( = ({ onOpenChange={onOpenChange}> {actionContent} @@ -62,54 +61,50 @@ const ListItem: FC = ({ export default ListItem; -const Wrapper = styled.div` - position: relative; - width: 100%; -`; - -const Inner = styled.button<{ +const Wrapper = styled.div<{ border?: boolean; isSelected?: boolean; + isOpenAction?: boolean; clamp?: Clamp; }>` display: flex; width: 100%; - min-height: 38px; + min-height: 36px; align-items: center; color: ${({ theme }) => theme.content.main}; border: 1px solid ${({ border, theme }) => (border ? theme.outline.weakest : "transparent")}; border-radius: ${({ clamp }) => - clamp === "left" ? "0 6px 6px 0" : clamp === "right" ? "6px 0 0 6px" : "6px"}; - box-sizing: border-box; - padding: 8px 20px 8px 4px; - background: ${({ theme, isSelected }) => (isSelected ? theme.select.main : "inherit")}; - transition: all 0.3s; + clamp === "left" ? "0 4px 4px 0" : clamp === "right" ? "4px 0 0 4px" : "4px"}; + background: ${({ theme, isSelected, isOpenAction }) => + isSelected ? theme.select.main : isOpenAction ? theme.bg[3] : "inherit"}; + transition: all 0.2s; + cursor: pointer; :hover { ${({ isSelected, theme }) => !isSelected && `background-color:` + theme.bg[3]} } `; +const Inner = styled.div` + display: flex; + align-items: center; + height: 100%; + width: 100%; + padding: 8px 4px 8px 4px; + border-radius: 4px; +`; + const StyledText = styled(Text)` flex-grow: 1; - width: 0; word-break: break-all; text-align: left; `; -const Button = styled.button<{ clamp?: Clamp }>` - height: 100%; - position: absolute; - right: 0; - top: 50%; - transform: translateY(-50%); - padding: 4px; - margin-left: -1px; +const Button = styled.div<{ clamp?: Clamp }>` + display: flex; + align-items: center; + height: 36px; color: ${({ theme }) => theme.content.weak}; border-radius: ${({ clamp }) => - clamp === "left" ? "0 6px 6px 0" : clamp === "right" ? "6px 0 0 6px" : "6px"}; - - :hover { - background: ${({ theme }) => theme.bg[2]}; - } + clamp === "left" ? "0 4px 4px 0" : clamp === "right" ? "4px 0 0 4px" : "4px"}; `; diff --git a/web/src/beta/components/Modal/ModalFrame/index.tsx b/web/src/beta/components/Modal/ModalFrame/index.tsx index fd1b30d87a..63aab33074 100644 --- a/web/src/beta/components/Modal/ModalFrame/index.tsx +++ b/web/src/beta/components/Modal/ModalFrame/index.tsx @@ -41,10 +41,10 @@ const Modal: React.FC = ({ className, size, isVisible, title, onClose, ch {!!title && ( - + {title} - {onClose && } + {onClose && } )} {children} @@ -107,6 +107,7 @@ const HeaderWrapper = styled.div` align-self: stretch; border-top-right-radius: 8px; border-top-left-radius: 8px; - background: #393939; + color: ${({ theme }) => theme.content.main}; + background: ${({ theme }) => theme.bg[2]}; `; export default Modal; diff --git a/web/src/beta/components/Modal/index.tsx b/web/src/beta/components/Modal/index.tsx index 11c645cb92..be27701c68 100644 --- a/web/src/beta/components/Modal/index.tsx +++ b/web/src/beta/components/Modal/index.tsx @@ -115,7 +115,6 @@ const ContentWrapper = styled.div` display: flex; flex-direction: column; flex: 1; - background: ${({ theme }) => theme.bg[0]}; `; const Content = styled.div` diff --git a/web/src/beta/components/NotFound/index.tsx b/web/src/beta/components/NotFound/index.tsx index e2dd68dd8b..a8046f5e98 100644 --- a/web/src/beta/components/NotFound/index.tsx +++ b/web/src/beta/components/NotFound/index.tsx @@ -1,23 +1,28 @@ -import React from "react"; - import Text from "@reearth/beta/components/Text"; import { useT } from "@reearth/services/i18n"; import { styled } from "@reearth/services/theme"; import Icon from "../Icon"; -const NotFound: React.FC = () => { +type Props = { + customHeader?: string; + customMessage?: string; + customType?: "error" | "warning"; +}; + +const NotFound: React.FC = ({ customHeader, customMessage, customType }) => { const t = useT(); return ( -
+ - + - {`404 ${t("Not found")}`} -
+ {customHeader ?? `404 ${t("Not found")}`} + {customMessage && {customMessage}} +
); }; @@ -27,14 +32,28 @@ export default NotFound; const Wrapper = styled.div` display: flex; justify-content: center; - padding-top: 40vh; + padding-top: 30vh; height: 100%; background: ${({ theme }) => theme.bg[1]}; `; +const InnerWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + max-width: 40vw; +`; + const IconWrapper = styled.div` display: flex; justify-content: space-between; align-items: center; - margin-bottom: 20px; + margin-bottom: 5vh; + min-width: 260px; + max-width: 20vw; +`; + +const CustomMessage = styled(Text)` + margin-top: 10px; `; diff --git a/web/src/beta/features/Editor/Visualizer/convert.ts b/web/src/beta/features/Editor/Visualizer/convert.ts index 3d6d30a551..8b4c343c25 100644 --- a/web/src/beta/features/Editor/Visualizer/convert.ts +++ b/web/src/beta/features/Editor/Visualizer/convert.ts @@ -209,6 +209,7 @@ export const convertStory = (story?: GqlStory): Story | undefined => { return { id: story.id, title: story.title, + position: story.panelPosition === "RIGHT" ? "right" : "left", pages: storyPages(story.pages), }; }; @@ -432,8 +433,8 @@ export function processLayers( id: nlsLayer.id, title: nlsLayer.title, visible: nlsLayer.visible, - infobox: processInfobox(nlsLayer.infobox, parent?.infobox), - tags: processLayerTags(nlsLayer.tags), + infobox: nlsLayer.infobox ? processInfobox(nlsLayer.infobox, parent?.infobox) : undefined, + tags: processLayerTags(nlsLayer.tags ?? []), properties: nlsLayer.config?.properties, defines: nlsLayer.config?.defines, events: nlsLayer.config?.events, diff --git a/web/src/beta/features/Editor/Visualizer/index.tsx b/web/src/beta/features/Editor/Visualizer/index.tsx index 4779cc9cc7..cea024707e 100644 --- a/web/src/beta/features/Editor/Visualizer/index.tsx +++ b/web/src/beta/features/Editor/Visualizer/index.tsx @@ -85,7 +85,6 @@ const Visualizer: React.FC = ({ ), [blocks], ); - return ( = ({ meta={engineMeta} useExperimentalSandbox={useExperimentalSandbox} camera={currentCamera} + storyPanelPosition={story?.position} onCameraChange={onCameraChange} onLayerSelect={selectLayer} onWidgetLayoutUpdate={onWidgetUpdate} diff --git a/web/src/beta/features/Editor/index.tsx b/web/src/beta/features/Editor/index.tsx index b76e15cc0b..6172028e26 100644 --- a/web/src/beta/features/Editor/index.tsx +++ b/web/src/beta/features/Editor/index.tsx @@ -188,7 +188,7 @@ const Editor: React.FC = ({ sceneId, projectId, workspaceId, tab }) => { hasNav={!!secondaryNavbar} visualizerWidth={visualizerWidth}> { - const newVisibility = !isVisible; - onLayerVisibilityUpate({ layerId: id, visible: newVisibility }); - setIsVisible(newVisibility); - setValue(isVisible ? "" : "V"); - }, [id, isVisible, onLayerVisibilityUpate]); + const handleUpdateVisibility = useCallback( + (e?: MouseEvent) => { + e?.stopPropagation(); + const newVisibility = !isVisible; + onLayerVisibilityUpate({ layerId: id, visible: newVisibility }); + setIsVisible(newVisibility); + setValue(isVisible ? "" : "V"); + }, + [id, isVisible, onLayerVisibilityUpate], + ); return ( setActionOpen(!!isOpen)} @@ -96,38 +101,47 @@ const LayerItem = ({ ]} /> }> - {isEditing ? ( - - ) : ( - layerTitle - )} - {value} + + {isEditing ? ( + + ) : ( + layerTitle + )} + + {value} + + ); }; export default LayerItem; +const ContentWrapper = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; +`; + const HideLayer = styled.div` - min-width: 10px; - min-height: 20px; - padding: 3px 6px 0; + display: flex; + justify-content: center; + width: 20px; + height: 20px; cursor: pointer; border-radius: 4px; border: 1.5px solid ${({ theme }) => theme.bg[1]}; color: ${({ theme }) => theme.content.strong}; background: ${({ theme }) => theme.bg[2]}; - position: absolute; - right: 30px; - top: 50%; - transform: translateY(-50%); + :hover { background: ${({ theme }) => theme.bg[2]}; } diff --git a/web/src/beta/features/Editor/tabs/map/LeftPanel/Layers/index.tsx b/web/src/beta/features/Editor/tabs/map/LeftPanel/Layers/index.tsx index 433510aa13..c0632a5063 100644 --- a/web/src/beta/features/Editor/tabs/map/LeftPanel/Layers/index.tsx +++ b/web/src/beta/features/Editor/tabs/map/LeftPanel/Layers/index.tsx @@ -66,7 +66,7 @@ const Layers: React.FC = ({ - (_: React.MouseEvent) => { - if (!value) return; - setCopiedKey(prevState => ({ - ...prevState, - [key]: true, - })); - navigator.clipboard.writeText(value); - onCopyToClipBoard?.(); - }, + (key: keyof CopiedItemKey, value: string | undefined) => () => { + if (!value) return; + setCopiedKey(prevState => ({ + ...prevState, + [key]: true, + })); + navigator.clipboard.writeText(value); + onCopyToClipBoard?.(); + }, [onCopyToClipBoard], ); @@ -78,9 +77,11 @@ export default ( const handleClose = useCallback(() => { onClose?.(); - onAliasChange(defaultAlias); - setStatusChange(false); - setOptions(defaultAlias ? false : true); + setTimeout(() => { + onAliasChange(defaultAlias); + setStatusChange(false); + setOptions(defaultAlias ? false : true); + }, 500); }, [onClose, defaultAlias]); // eslint-disable-line react-hooks/exhaustive-deps const generateAlias = useCallback(() => { diff --git a/web/src/beta/features/Editor/tabs/publish/Nav/PublishModal/index.tsx b/web/src/beta/features/Editor/tabs/publish/Nav/PublishModal/index.tsx index e377237d74..01323a01a2 100644 --- a/web/src/beta/features/Editor/tabs/publish/Nav/PublishModal/index.tsx +++ b/web/src/beta/features/Editor/tabs/publish/Nav/PublishModal/index.tsx @@ -27,6 +27,7 @@ type Props = { onClose?: () => void; onCopyToClipBoard?: () => void; onAliasValidate?: (alias: string) => void; + onNavigateToSettings?: (page?: "story" | "public" | "asset" | "plugin" | undefined) => void; }; const PublishModal: React.FC = ({ @@ -42,6 +43,7 @@ const PublishModal: React.FC = ({ onPublish, onCopyToClipBoard, onAliasValidate, + onNavigateToSettings, }) => { const t = useT(); const theme = useTheme(); @@ -104,7 +106,7 @@ const PublishModal: React.FC = ({ : t("Continue"); }, [t, statusChanged, publishing]); - const secondaryButtonText = useMemo(() => (!statusChanged ? "Cancel" : "Close"), [statusChanged]); + const secondaryButtonText = useMemo(() => (!statusChanged ? "Cancel" : "OK"), [statusChanged]); const updateDescriptionText = useMemo(() => { return publishing === "updating" @@ -121,7 +123,13 @@ const PublishModal: React.FC = ({ isVisible={isVisible} size="sm" title={modalTitleText} - button1={ - + + {t("Copy")} + + {t("* Anyone can see your project with this URL")} - {t("Embed Code")} + {t("Embed Code")}
-
- - {embedCode} + + {embedCode} + + {t("Copy")} - -
+ {t("* Please use this code if you want to embed your project into a webpage")} @@ -163,19 +183,30 @@ const PublishModal: React.FC = ({ <>
{updateDescriptionText} +
+
+ {t("Publish domain")} {url && alias && ( - - + window.open(purl, "_blank")}> + {purl} - - + + )}
setOptions(!showOptions)}> - {t("more options")} + {t("More options")} +
+ + {t("Need to change domain related settings?")} + + +
= ({ ) : (
- +
+ + + {t("Unpublishing")} + +
{t("Your project will be unpublished.")} {t("This means that anybody with the URL will become unable to view this project.")} - + {t("**Warning**: This includes websites where this project is embedded.")}
@@ -215,20 +251,21 @@ const Subtitle = styled(Text)` text-align: left; `; -const StyledIcon = styled(Icon)` - margin-bottom: ${`${metricsSizes["xl"]}px`}; -`; - -const PublishLink = styled.a` - text-decoration: none; +const UrlWrapper = styled.div<{ justify?: string }>` + display: flex; + justify-content: ${({ justify }) => justify ?? "center"}; + align-items: center; + border: 1px solid ${({ theme }) => theme.outline.weak}; + border-radius: 4px; + padding: 8px 16px; + cursor: pointer; `; const OptionsToggle = styled.div` display: flex; justify-content: space-between; align-items: center; - margin: ${`0 0 ${metricsSizes["m"]}px 0`}; - color: ${({ theme }) => theme.classic.main.text}; + color: ${({ theme }) => theme.content.main}; cursor: pointer; user-select: none; `; @@ -239,14 +276,17 @@ const ArrowIcon = styled(Icon)<{ open?: boolean }>` open ? "translateY(10%) rotate(90deg)" : "translateY(0) rotate(180deg)"}; `; -const UrlText = styled(Text)` - text-align: center; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin: ${`${metricsSizes["2xl"]}px 0`}; -`; - const HideableSection = styled(Section)<{ showOptions?: boolean }>` display: ${props => (props.showOptions ? null : "none")}; `; + +const DomainText = styled(Text)` + margin-bottom: 8px; +`; + +const Header = styled.div` + display: flex; + gap: 12px; + color: ${({ theme }) => theme.warning.main}; + margin-bottom: 12px; +`; diff --git a/web/src/beta/features/Editor/tabs/publish/Nav/hooks.ts b/web/src/beta/features/Editor/tabs/publish/Nav/hooks.ts index c3d9e93277..ee43b3bee2 100644 --- a/web/src/beta/features/Editor/tabs/publish/Nav/hooks.ts +++ b/web/src/beta/features/Editor/tabs/publish/Nav/hooks.ts @@ -1,5 +1,6 @@ import { useCallback, useEffect, useMemo, useState } from "react"; +import { useSettingsNavigation } from "@reearth/beta/hooks"; import generateRandomString from "@reearth/beta/utils/generate-random-string"; import { useProjectFetcher } from "@reearth/services/api"; @@ -7,16 +8,17 @@ import { publishingType } from "./PublishModal"; import { type PublishStatus } from "./PublishModal/hooks"; export default ({ projectId }: { projectId?: string }) => { + const handleNavigationToSettings = useSettingsNavigation({ projectId }); const { useProjectQuery, useProjectAliasCheckLazyQuery, usePublishProject, publishProjectLoading, } = useProjectFetcher(); + const { project } = useProjectQuery(projectId); const [publishing, setPublishing] = useState("unpublishing"); - const [dropdownOpen, setDropdown] = useState(false); const [modalOpen, setModal] = useState(false); @@ -67,8 +69,9 @@ export default ({ projectId }: { projectId?: string }) => { ); const handleOpenProjectSettings = useCallback(() => { + handleNavigationToSettings?.("public"); setDropdown(false); - }, []); + }, [handleNavigationToSettings]); const handleModalOpen = useCallback((p: publishingType) => { setPublishing(p); @@ -76,9 +79,7 @@ export default ({ projectId }: { projectId?: string }) => { setModal(true); }, []); - const handleModalClose = useCallback(() => { - setModal(false); - }, []); + const handleModalClose = useCallback(() => setModal(false), []); return { publishing, @@ -95,5 +96,6 @@ export default ({ projectId }: { projectId?: string }) => { handleProjectPublish, handleProjectAliasCheck, handleOpenProjectSettings, + handleNavigationToSettings, }; }; diff --git a/web/src/beta/features/Editor/tabs/publish/Nav/index.tsx b/web/src/beta/features/Editor/tabs/publish/Nav/index.tsx index 819e8adb93..6eb0f8219c 100644 --- a/web/src/beta/features/Editor/tabs/publish/Nav/index.tsx +++ b/web/src/beta/features/Editor/tabs/publish/Nav/index.tsx @@ -42,6 +42,7 @@ const Nav: React.FC = ({ projectId, selectedProjectType, onProjectTypeCha handleProjectPublish, handleProjectAliasCheck, handleOpenProjectSettings, + handleNavigationToSettings, } = useHooks({ projectId }); const text = useMemo( @@ -113,6 +114,7 @@ const Nav: React.FC = ({ projectId, selectedProjectType, onProjectTypeCha projectAlias={alias} validAlias={validAlias} validatingAlias={validatingAlias} + onNavigateToSettings={handleNavigationToSettings} onClose={handleModalClose} onPublish={handleProjectPublish} onAliasValidate={handleProjectAliasCheck} diff --git a/web/src/beta/features/Editor/useLayers.ts b/web/src/beta/features/Editor/useLayers.ts index e03cf7cb54..cd87f60f97 100644 --- a/web/src/beta/features/Editor/useLayers.ts +++ b/web/src/beta/features/Editor/useLayers.ts @@ -69,7 +69,7 @@ export default function ({ sceneId, isVisualizerReady, visualizerRef }: LayerPro const handleLayerDelete = useCallback( async (layerId: string) => { const deletedPageIndex = nlsLayers.findIndex(l => l.id === layerId); - if (!deletedPageIndex) return; + if (deletedPageIndex === undefined) return; await useRemoveNLSLayer({ layerId, diff --git a/web/src/beta/features/Modals/AssetModal/index.tsx b/web/src/beta/features/Modals/AssetModal/index.tsx index 0d3b0b389b..dc51bc5cc9 100644 --- a/web/src/beta/features/Modals/AssetModal/index.tsx +++ b/web/src/beta/features/Modals/AssetModal/index.tsx @@ -131,19 +131,19 @@ const ChooseAssetModal: React.FC = ({ button1={