From bbb65724df118c782c640876e186bde6e2acd81d Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Wed, 30 Oct 2024 17:38:59 -0300 Subject: [PATCH 01/22] refactor: split into components --- packages/main/src/modules/cli.ts | 3 +- .../src/components/EditorPage/component.tsx | 4 +- .../AlternativeServers/component.tsx | 98 +++++++++++ .../AlternativeServers/index.ts | 1 + .../{ => AlternativeServers}/styles.css | 26 --- .../PublishProject/Deploy/component.tsx | 8 + .../Modals/PublishProject/Deploy/index.ts | 1 + .../Modals/PublishProject/Deploy/styles.css | 3 + .../PublishProject/Initial/component.tsx | 39 +++++ .../Modals/PublishProject/Initial/index.ts | 1 + .../Modals/PublishProject/Initial/styles.css | 27 +++ .../PublishToLand/component.tsx | 17 +- .../PublishToWorld/component.tsx | 15 +- .../Modals/PublishProject/component.tsx | 156 ++---------------- .../components/Modals/PublishProject/index.ts | 4 +- .../components/Modals/PublishProject/types.ts | 13 +- 16 files changed, 225 insertions(+), 191 deletions(-) create mode 100644 packages/renderer/src/components/Modals/PublishProject/AlternativeServers/component.tsx create mode 100644 packages/renderer/src/components/Modals/PublishProject/AlternativeServers/index.ts rename packages/renderer/src/components/Modals/PublishProject/{ => AlternativeServers}/styles.css (81%) create mode 100644 packages/renderer/src/components/Modals/PublishProject/Deploy/component.tsx create mode 100644 packages/renderer/src/components/Modals/PublishProject/Deploy/index.ts create mode 100644 packages/renderer/src/components/Modals/PublishProject/Deploy/styles.css create mode 100644 packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx create mode 100644 packages/renderer/src/components/Modals/PublishProject/Initial/index.ts create mode 100644 packages/renderer/src/components/Modals/PublishProject/Initial/styles.css diff --git a/packages/main/src/modules/cli.ts b/packages/main/src/modules/cli.ts index 35ceb296..9323fd09 100644 --- a/packages/main/src/modules/cli.ts +++ b/packages/main/src/modules/cli.ts @@ -44,6 +44,7 @@ export async function deploy({ path, target, targetContent }: DeployOptions) { deployServer = run('@dcl/sdk-commands', 'sdk-commands', { args: [ 'deploy', + '--no-browser', '--port', port.toString(), ...(target ? ['--target', target] : []), @@ -54,7 +55,7 @@ export async function deploy({ path, target, targetContent }: DeployOptions) { }); // App ready at - await deployServer.waitFor(/app ready at/i); + await deployServer.waitFor(/listening/i); return port; } diff --git a/packages/renderer/src/components/EditorPage/component.tsx b/packages/renderer/src/components/EditorPage/component.tsx index c3830302..68adc857 100644 --- a/packages/renderer/src/components/EditorPage/component.tsx +++ b/packages/renderer/src/components/EditorPage/component.tsx @@ -89,7 +89,7 @@ export function EditorPage() { setOpen(undefined); }, []); - const handleSubmitModal = useCallback( + const handleTarget = useCallback( ({ target, value }: StepValue) => { switch (target) { case 'worlds': @@ -213,7 +213,7 @@ export function EditorPage() { open={open === 'publish'} project={project} onClose={handleCloseModal} - onSubmit={handleSubmitModal} + onTarget={handleTarget} /> )} diff --git a/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/component.tsx b/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/component.tsx new file mode 100644 index 00000000..3bfa2b5e --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/component.tsx @@ -0,0 +1,98 @@ +import { type ChangeEvent, useCallback, useState } from 'react'; +import { Button, MenuItem, Select, type SelectChangeEvent } from 'decentraland-ui2'; +import { misc } from '#preload'; +import { t } from '/@/modules/store/translation/utils'; +import { isUrl } from '/shared/utils'; +import GenesisPlazaPng from '/assets/images/genesis_plaza.png'; +import type { AlternativeTarget, TargetProps, TargetValue } from '../types'; + +import './styles.css'; + +export function AlternativeServers({ onTarget: onClick }: TargetProps) { + const [option, setOption] = useState('test'); + const [customUrl, setCustomUrl] = useState(''); + const [error, setError] = useState(''); + + const handleClick = useCallback(() => { + if (option === 'custom' && !isUrl(customUrl)) { + return setError(t('modal.publish_project.alternative_servers.errors.url')); + } + const value: TargetValue = { target: option, value: customUrl }; + onClick(value); + }, [option, customUrl]); + + const handleChangeSelect = useCallback((e: SelectChangeEvent) => { + setOption(e.target.value as AlternativeTarget); + }, []); + + const handleChangeCustom = useCallback( + (e: ChangeEvent) => { + if (error) setError(''); + setCustomUrl(e.target.value); + }, + [error], + ); + + const handleClickLearnMore = useCallback(() => { + if (option === 'custom') { + return misc.openExternal( + 'https://docs.decentraland.org/creator/development-guide/sdk7/publishing/#custom-servers', + ); + } + misc.openExternal( + 'https://docs.decentraland.org/creator/development-guide/sdk7/publishing/#the-test-server', + ); + }, [option]); + + return ( +
+ {t('modal.publish_project.select')} +
+
+
+

{t('modal.publish_project.alternative_servers.list')}

+ + {option === 'custom' && ( +
+ + {t('modal.publish_project.alternative_servers.custom_server_url')} + + + {error} +
+ )} +
+ +
+
+ + {t('option_box.learn_more')} + + +
+
+
+ ); +} diff --git a/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/index.ts b/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/index.ts new file mode 100644 index 00000000..33ff9f08 --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/index.ts @@ -0,0 +1 @@ +export { AlternativeServers } from './component'; diff --git a/packages/renderer/src/components/Modals/PublishProject/styles.css b/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/styles.css similarity index 81% rename from packages/renderer/src/components/Modals/PublishProject/styles.css rename to packages/renderer/src/components/Modals/PublishProject/AlternativeServers/styles.css index e14bdcae..f2d3990e 100644 --- a/packages/renderer/src/components/Modals/PublishProject/styles.css +++ b/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/styles.css @@ -1,31 +1,5 @@ /* TODO: update the Modal component in ui2 to allow providing a className to the Material Modal...*/ -/* Initial Modal */ -.MuiModal-root .Initial { - display: flex; - flex-direction: column; -} - -.MuiModal-root .Initial .select, -.MuiModal-root .Initial .alternative_servers { - text-align: center; -} - -.MuiModal-root .Initial .options { - display: flex; - margin: 20px 0; -} - -.MuiModal-root .Initial .options .OptionBox + .OptionBox { - margin-left: 20px; -} - -.MuiModal-root .Initial .alternative_servers { - text-decoration: underline; - text-transform: uppercase; - cursor: pointer; -} - /* AlternativeServers Modal */ .MuiModal-root .AlternativeServers { display: flex; diff --git a/packages/renderer/src/components/Modals/PublishProject/Deploy/component.tsx b/packages/renderer/src/components/Modals/PublishProject/Deploy/component.tsx new file mode 100644 index 00000000..62aebfc4 --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/Deploy/component.tsx @@ -0,0 +1,8 @@ +import { Loader } from '/@/components/Loader'; +import { useEditor } from '/@/hooks/useEditor'; +import './styles.css'; + +export function Deploy() { + const { loadingPublish } = useEditor(); + return
{loadingPublish ? : 'Deploy'}
; +} diff --git a/packages/renderer/src/components/Modals/PublishProject/Deploy/index.ts b/packages/renderer/src/components/Modals/PublishProject/Deploy/index.ts new file mode 100644 index 00000000..7d6651e1 --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/Deploy/index.ts @@ -0,0 +1 @@ +export { Deploy } from './component'; diff --git a/packages/renderer/src/components/Modals/PublishProject/Deploy/styles.css b/packages/renderer/src/components/Modals/PublishProject/Deploy/styles.css new file mode 100644 index 00000000..4d68c41b --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/Deploy/styles.css @@ -0,0 +1,3 @@ +.Deploy { + color: red; +} diff --git a/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx b/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx new file mode 100644 index 00000000..cdbde597 --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx @@ -0,0 +1,39 @@ +import { OptionBox } from '/@/components/EditorPage/OptionBox'; +import { t } from '/@/modules/store/translation/utils'; +import LandPng from '/assets/images/land.png'; +import WorldsPng from '/assets/images/worlds.png'; +import type { Step } from '../types'; + +import './styles.css'; + +export function Initial({ onStepChange }: { onStepChange: (step: Step) => () => void }) { + return ( +
+ {t('modal.publish_project.select')} +
+ + +
+ + {t('modal.publish_project.alternative_servers.title')} + +
+ ); +} diff --git a/packages/renderer/src/components/Modals/PublishProject/Initial/index.ts b/packages/renderer/src/components/Modals/PublishProject/Initial/index.ts new file mode 100644 index 00000000..58143fdf --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/Initial/index.ts @@ -0,0 +1 @@ +export { Initial } from './component'; diff --git a/packages/renderer/src/components/Modals/PublishProject/Initial/styles.css b/packages/renderer/src/components/Modals/PublishProject/Initial/styles.css new file mode 100644 index 00000000..534925a4 --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/Initial/styles.css @@ -0,0 +1,27 @@ +/* TODO: update the Modal component in ui2 to allow providing a className to the Material Modal...*/ + +/* Initial Modal */ +.MuiModal-root .Initial { + display: flex; + flex-direction: column; +} + +.MuiModal-root .Initial .select, +.MuiModal-root .Initial .alternative_servers { + text-align: center; +} + +.MuiModal-root .Initial .options { + display: flex; + margin: 20px 0; +} + +.MuiModal-root .Initial .options .OptionBox + .OptionBox { + margin-left: 20px; +} + +.MuiModal-root .Initial .alternative_servers { + text-decoration: underline; + text-transform: uppercase; + cursor: pointer; +} diff --git a/packages/renderer/src/components/Modals/PublishProject/PublishToLand/component.tsx b/packages/renderer/src/components/Modals/PublishProject/PublishToLand/component.tsx index 3d1840f8..6a7d424e 100644 --- a/packages/renderer/src/components/Modals/PublishProject/PublishToLand/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/PublishToLand/component.tsx @@ -14,6 +14,7 @@ import { useEditor } from '/@/hooks/useEditor'; import { useWorkspace } from '/@/hooks/useWorkspace'; import { COLORS, type Coordinate } from './types'; +import { type TargetProps } from '../types'; function calculateParcels(project: Project, point: Coordinate): Coordinate[] { const [baseX, baseY] = project.scene.base.split(',').map(coord => parseInt(coord, 10)); @@ -23,8 +24,8 @@ function calculateParcels(project: Project, point: Coordinate): Coordinate[] { }); } -export function PublishToLand({ onClose }: { onClose: () => void }) { - const { project, publishScene } = useEditor(); +export function PublishToLand({ onTarget }: TargetProps) { + const { project } = useEditor(); const { updateProject, updateSceneJson } = useWorkspace(); const tiles = useSelector(state => state.land.tiles); const landTiles = useSelector(state => landSelectors.getLandTiles(state.land)); @@ -36,10 +37,12 @@ export function PublishToLand({ onClose }: { onClose: () => void }) { // Memoize the project parcels centered around the hover position const projectParcels = useMemo(() => calculateParcels(project, hover), [project, hover]); - const handleClickPublish = useCallback(() => { - publishScene({ target: import.meta.env.VITE_CATALYST_SERVER || DEPLOY_URLS.CATALYST_SERVER }); - onClose(); - }, [publishScene, onClose]); + const handleNext = useCallback(() => { + onTarget({ + target: 'land', + value: import.meta.env.VITE_CATALYST_SERVER || DEPLOY_URLS.CATALYST_SERVER, + }); + }, [onTarget]); const handleHover = useCallback((x: number, y: number) => { setHover({ x, y }); @@ -179,7 +182,7 @@ export function PublishToLand({ onClose }: { onClose: () => void }) { variant="contained" color="primary" size="large" - onClick={handleClickPublish} + onClick={handleNext} disabled={!placement} sx={{ height: '45px' }} > diff --git a/packages/renderer/src/components/Modals/PublishProject/PublishToWorld/component.tsx b/packages/renderer/src/components/Modals/PublishProject/PublishToWorld/component.tsx index 03cd343c..29003dcc 100644 --- a/packages/renderer/src/components/Modals/PublishProject/PublishToWorld/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/PublishToWorld/component.tsx @@ -26,6 +26,7 @@ import { getEnsProvider } from '/@/modules/store/ens/utils'; import { useAuth } from '/@/hooks/useAuth'; import { useEditor } from '/@/hooks/useEditor'; import { useWorkspace } from '/@/hooks/useWorkspace'; +import { type TargetProps } from '../types'; import EmptyWorldSVG from '/assets/images/empty-deploy-to-world.svg'; import LogoDCLSVG from '/assets/images/logo-dcl.svg'; @@ -35,14 +36,16 @@ import { Button } from '../../../Button'; import './styles.css'; -export function PublishToWorld({ onClose }: { onClose: () => void }) { - const { project, publishScene } = useEditor(); +export function PublishToWorld({ onTarget }: TargetProps) { + const { project } = useEditor(); const names = useSelector(state => state.ens.data); const emptyNames = Object.keys(names).length === 0; - const handleClickPublish = useCallback(() => { - publishScene({ targetContent: import.meta.env.VITE_WORLDS_SERVER || DEPLOY_URLS.WORLDS }); - onClose(); + const handleNext = useCallback(() => { + onTarget({ + target: 'worlds', + value: import.meta.env.VITE_WORLDS_SERVER || DEPLOY_URLS.WORLDS, + }); }, []); return emptyNames ? ( @@ -50,7 +53,7 @@ export function PublishToWorld({ onClose }: { onClose: () => void }) { ) : ( ); } diff --git a/packages/renderer/src/components/Modals/PublishProject/component.tsx b/packages/renderer/src/components/Modals/PublishProject/component.tsx index 943b863c..3ca0e415 100644 --- a/packages/renderer/src/components/Modals/PublishProject/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/component.tsx @@ -1,25 +1,15 @@ -import { type ChangeEvent, useCallback, useState } from 'react'; -import { MenuItem, Select, type SelectChangeEvent } from 'decentraland-ui2'; +import { useCallback, useState } from 'react'; import { Modal } from 'decentraland-ui2/dist/components/Modal/Modal'; - -import { misc } from '#preload'; -import { isUrl } from '/shared/utils'; import { t } from '/@/modules/store/translation/utils'; - -import GenesisPlazaPng from '/assets/images/genesis_plaza.png'; -import LandPng from '/assets/images/land.png'; -import WorldsPng from '/assets/images/worlds.png'; - -import { Button } from '../../Button'; -import { OptionBox } from '../../EditorPage/OptionBox'; +import { Initial } from './Initial'; +import { AlternativeServers } from './AlternativeServers'; import { PublishToWorld } from './PublishToWorld'; import { PublishToLand } from './PublishToLand'; +import { Deploy } from './Deploy'; -import type { AlternativeTarget, Step, StepProps, StepValue, Props } from './types'; +import type { TargetValue, Props, Step } from './types'; -import './styles.css'; - -export function PublishProject({ open, project, onSubmit, onClose }: Props) { +export function PublishProject({ open, project, onTarget, onClose }: Props) { const [step, setStep] = useState('initial'); const close = useCallback(() => { @@ -36,9 +26,9 @@ export function PublishProject({ open, project, onSubmit, onClose }: Props) { [], ); - const handleClickPublish = useCallback((value: StepValue) => { - onSubmit(value); - close(); + const handleTarget = useCallback((value: TargetValue) => { + onTarget(value); + setStep('deploy'); }, []); return ( @@ -54,130 +44,10 @@ export function PublishProject({ open, project, onSubmit, onClose }: Props) { onBack={step !== 'initial' ? handleChangeStep('initial') : undefined} > {step === 'initial' && } - {step === 'alternative-servers' && } - {step === 'publish-to-land' && } - {step === 'publish-to-world' && } + {step === 'alternative-servers' && } + {step === 'publish-to-land' && } + {step === 'publish-to-world' && } + {step === 'deploy' && } ); } - -function Initial({ onStepChange }: { onStepChange: (step: Step) => () => void }) { - return ( -
- {t('modal.publish_project.select')} -
- - -
- - {t('modal.publish_project.alternative_servers.title')} - -
- ); -} - -function AlternativeServers({ onClick }: StepProps) { - const [option, setOption] = useState('test'); - const [customUrl, setCustomUrl] = useState(''); - const [error, setError] = useState(''); - - const handleClick = useCallback(() => { - if (option === 'custom' && !isUrl(customUrl)) { - return setError(t('modal.publish_project.alternative_servers.errors.url')); - } - const value: StepValue = { target: option, value: customUrl }; - onClick(value); - }, [option, customUrl]); - - const handleChangeSelect = useCallback((e: SelectChangeEvent) => { - setOption(e.target.value as AlternativeTarget); - }, []); - - const handleChangeCustom = useCallback( - (e: ChangeEvent) => { - if (error) setError(''); - setCustomUrl(e.target.value); - }, - [error], - ); - - const handleClickLearnMore = useCallback(() => { - if (option === 'custom') { - return misc.openExternal( - 'https://docs.decentraland.org/creator/development-guide/sdk7/publishing/#custom-servers', - ); - } - misc.openExternal( - 'https://docs.decentraland.org/creator/development-guide/sdk7/publishing/#the-test-server', - ); - }, [option]); - - return ( -
- {t('modal.publish_project.select')} -
-
-
-

{t('modal.publish_project.alternative_servers.list')}

- - {option === 'custom' && ( -
- - {t('modal.publish_project.alternative_servers.custom_server_url')} - - - {error} -
- )} -
- -
-
- - {t('option_box.learn_more')} - - -
-
-
- ); -} diff --git a/packages/renderer/src/components/Modals/PublishProject/index.ts b/packages/renderer/src/components/Modals/PublishProject/index.ts index 0d3eda45..cf7bb420 100644 --- a/packages/renderer/src/components/Modals/PublishProject/index.ts +++ b/packages/renderer/src/components/Modals/PublishProject/index.ts @@ -1,3 +1,3 @@ import { PublishProject } from './component'; -import type { StepValue } from './types'; -export { PublishProject, StepValue }; +import type { TargetValue } from './types'; +export { PublishProject, TargetValue as StepValue }; diff --git a/packages/renderer/src/components/Modals/PublishProject/types.ts b/packages/renderer/src/components/Modals/PublishProject/types.ts index f007062a..c701ae2c 100644 --- a/packages/renderer/src/components/Modals/PublishProject/types.ts +++ b/packages/renderer/src/components/Modals/PublishProject/types.ts @@ -4,13 +4,18 @@ export type Props = { open: boolean; project: Project; onClose: () => void; - onSubmit: (value: StepValue) => void; + onTarget: (value: TargetValue) => void; }; -export type Step = 'initial' | 'alternative-servers' | 'publish-to-world' | 'publish-to-land'; +export type Step = + | 'initial' + | 'alternative-servers' + | 'publish-to-world' + | 'publish-to-land' + | 'deploy'; export type InitialTarget = 'worlds' | 'land'; export type AlternativeTarget = 'test' | 'custom'; export type Target = InitialTarget | AlternativeTarget; -export type StepValue = { target: Target; value?: string }; -export type StepProps = { onClick: (value: StepValue) => void }; +export type TargetValue = { target: Target; value?: string }; +export type TargetProps = { onTarget: (value: TargetValue) => void }; From 0081fe5912d38033ac5dbe12a04f5b1cda6e50c0 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Thu, 31 Oct 2024 12:07:36 -0300 Subject: [PATCH 02/22] refactor: target and history --- .../AlternativeServers/component.tsx | 8 ++--- .../PublishProject/Initial/component.tsx | 8 ++--- .../PublishToLand/component.tsx | 4 +-- .../PublishToWorld/component.tsx | 6 ++-- .../Modals/PublishProject/component.tsx | 34 ++++++++++++------- .../components/Modals/PublishProject/index.ts | 4 +-- .../components/Modals/PublishProject/types.ts | 7 ++-- packages/renderer/src/themes/theme.css | 5 +++ 8 files changed, 45 insertions(+), 31 deletions(-) diff --git a/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/component.tsx b/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/component.tsx index 3bfa2b5e..57ffc8bd 100644 --- a/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/component.tsx @@ -4,11 +4,11 @@ import { misc } from '#preload'; import { t } from '/@/modules/store/translation/utils'; import { isUrl } from '/shared/utils'; import GenesisPlazaPng from '/assets/images/genesis_plaza.png'; -import type { AlternativeTarget, TargetProps, TargetValue } from '../types'; +import type { AlternativeTarget, Target } from '../types'; import './styles.css'; -export function AlternativeServers({ onTarget: onClick }: TargetProps) { +export function AlternativeServers({ onTarget }: { onTarget: (target: Target) => void }) { const [option, setOption] = useState('test'); const [customUrl, setCustomUrl] = useState(''); const [error, setError] = useState(''); @@ -17,8 +17,8 @@ export function AlternativeServers({ onTarget: onClick }: TargetProps) { if (option === 'custom' && !isUrl(customUrl)) { return setError(t('modal.publish_project.alternative_servers.errors.url')); } - const value: TargetValue = { target: option, value: customUrl }; - onClick(value); + const value: Target = { target: option, value: customUrl }; + onTarget(value); }, [option, customUrl]); const handleChangeSelect = useCallback((e: SelectChangeEvent) => { diff --git a/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx b/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx index cdbde597..c510c593 100644 --- a/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx @@ -6,7 +6,7 @@ import type { Step } from '../types'; import './styles.css'; -export function Initial({ onStepChange }: { onStepChange: (step: Step) => () => void }) { +export function Initial({ onStepChange }: { onStepChange: (step: Step) => void }) { return (
{t('modal.publish_project.select')} @@ -16,7 +16,7 @@ export function Initial({ onStepChange }: { onStepChange: (step: Step) => () => title={t('modal.publish_project.worlds.title')} description={t('modal.publish_project.worlds.description')} buttonText={t('modal.publish_project.worlds.action')} - onClickPublish={onStepChange('publish-to-world')} + onClickPublish={() => onStepChange('publish-to-world')} learnMoreUrl="https://docs.decentraland.org/creator/worlds/about/#publish-a-world" /> () => title={t('modal.publish_project.land.title')} description={t('modal.publish_project.land.description')} buttonText={t('modal.publish_project.land.action')} - onClickPublish={onStepChange('publish-to-land')} + onClickPublish={() => onStepChange('publish-to-land')} learnMoreUrl="https://docs.decentraland.org/creator/development-guide/sdk7/publishing-permissions/#land-permission-options" />
onStepChange('alternative-servers')} > {t('modal.publish_project.alternative_servers.title')} diff --git a/packages/renderer/src/components/Modals/PublishProject/PublishToLand/component.tsx b/packages/renderer/src/components/Modals/PublishProject/PublishToLand/component.tsx index 6a7d424e..44992c01 100644 --- a/packages/renderer/src/components/Modals/PublishProject/PublishToLand/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/PublishToLand/component.tsx @@ -14,7 +14,7 @@ import { useEditor } from '/@/hooks/useEditor'; import { useWorkspace } from '/@/hooks/useWorkspace'; import { COLORS, type Coordinate } from './types'; -import { type TargetProps } from '../types'; +import { type Target } from '../types'; function calculateParcels(project: Project, point: Coordinate): Coordinate[] { const [baseX, baseY] = project.scene.base.split(',').map(coord => parseInt(coord, 10)); @@ -24,7 +24,7 @@ function calculateParcels(project: Project, point: Coordinate): Coordinate[] { }); } -export function PublishToLand({ onTarget }: TargetProps) { +export function PublishToLand({ onTarget }: { onTarget: (target: Target) => void }) { const { project } = useEditor(); const { updateProject, updateSceneJson } = useWorkspace(); const tiles = useSelector(state => state.land.tiles); diff --git a/packages/renderer/src/components/Modals/PublishProject/PublishToWorld/component.tsx b/packages/renderer/src/components/Modals/PublishProject/PublishToWorld/component.tsx index 29003dcc..3bddee86 100644 --- a/packages/renderer/src/components/Modals/PublishProject/PublishToWorld/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/PublishToWorld/component.tsx @@ -26,17 +26,17 @@ import { getEnsProvider } from '/@/modules/store/ens/utils'; import { useAuth } from '/@/hooks/useAuth'; import { useEditor } from '/@/hooks/useEditor'; import { useWorkspace } from '/@/hooks/useWorkspace'; -import { type TargetProps } from '../types'; import EmptyWorldSVG from '/assets/images/empty-deploy-to-world.svg'; import LogoDCLSVG from '/assets/images/logo-dcl.svg'; import LogoENSSVG from '/assets/images/logo-ens.svg'; import { Button } from '../../../Button'; +import { type Target } from '../types'; import './styles.css'; -export function PublishToWorld({ onTarget }: TargetProps) { +export function PublishToWorld({ onTarget }: { onTarget: (target: Target) => void }) { const { project } = useEditor(); const names = useSelector(state => state.ens.data); const emptyNames = Object.keys(names).length === 0; @@ -46,7 +46,7 @@ export function PublishToWorld({ onTarget }: TargetProps) { target: 'worlds', value: import.meta.env.VITE_WORLDS_SERVER || DEPLOY_URLS.WORLDS, }); - }, []); + }, [onTarget]); return emptyNames ? ( diff --git a/packages/renderer/src/components/Modals/PublishProject/component.tsx b/packages/renderer/src/components/Modals/PublishProject/component.tsx index 3ca0e415..fbf17a7b 100644 --- a/packages/renderer/src/components/Modals/PublishProject/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/component.tsx @@ -7,29 +7,39 @@ import { PublishToWorld } from './PublishToWorld'; import { PublishToLand } from './PublishToLand'; import { Deploy } from './Deploy'; -import type { TargetValue, Props, Step } from './types'; +import type { Target, Props, Step } from './types'; export function PublishProject({ open, project, onTarget, onClose }: Props) { const [step, setStep] = useState('initial'); + const [history, setHistory] = useState([]); - const close = useCallback(() => { + const handleClose = useCallback(() => { setStep('initial'); + setHistory([]); onClose(); - }, []); + }, [setStep, setHistory, onClose]); - const handleClose = useCallback(() => close(), []); + const handleBack = useCallback(() => { + const prev = history.pop(); + setStep(prev || 'initial'); + setHistory(history => (history.length > 0 ? history.slice(0, -1) : [])); + }, [history, setStep, setHistory]); const handleChangeStep = useCallback( - (step: Step) => () => { - setStep(step); + (newStep: Step) => { + setStep(newStep); + setHistory(history => [...history, step]); }, - [], + [step, setStep, setHistory], ); - const handleTarget = useCallback((value: TargetValue) => { - onTarget(value); - setStep('deploy'); - }, []); + const handleTarget = useCallback( + (value: Target) => { + onTarget(value); + handleChangeStep('deploy'); + }, + [onTarget, handleChangeStep], + ); return ( {step === 'initial' && } {step === 'alternative-servers' && } diff --git a/packages/renderer/src/components/Modals/PublishProject/index.ts b/packages/renderer/src/components/Modals/PublishProject/index.ts index cf7bb420..403779d9 100644 --- a/packages/renderer/src/components/Modals/PublishProject/index.ts +++ b/packages/renderer/src/components/Modals/PublishProject/index.ts @@ -1,3 +1,3 @@ import { PublishProject } from './component'; -import type { TargetValue } from './types'; -export { PublishProject, TargetValue as StepValue }; +import type { Target } from './types'; +export { PublishProject, Target as StepValue }; diff --git a/packages/renderer/src/components/Modals/PublishProject/types.ts b/packages/renderer/src/components/Modals/PublishProject/types.ts index c701ae2c..e1000522 100644 --- a/packages/renderer/src/components/Modals/PublishProject/types.ts +++ b/packages/renderer/src/components/Modals/PublishProject/types.ts @@ -4,7 +4,7 @@ export type Props = { open: boolean; project: Project; onClose: () => void; - onTarget: (value: TargetValue) => void; + onTarget: (value: Target) => void; }; export type Step = @@ -16,6 +16,5 @@ export type Step = export type InitialTarget = 'worlds' | 'land'; export type AlternativeTarget = 'test' | 'custom'; -export type Target = InitialTarget | AlternativeTarget; -export type TargetValue = { target: Target; value?: string }; -export type TargetProps = { onTarget: (value: TargetValue) => void }; +export type TargetType = InitialTarget | AlternativeTarget; +export type Target = { target: TargetType; value?: string }; diff --git a/packages/renderer/src/themes/theme.css b/packages/renderer/src/themes/theme.css index 94302ea4..96674bc3 100644 --- a/packages/renderer/src/themes/theme.css +++ b/packages/renderer/src/themes/theme.css @@ -96,3 +96,8 @@ a { .MuiDialog-paper { position: inherit !important; } + +/* Temp: make disabled buttons look disabled */ +.Button.MuiButton-root.Mui-disabled { + opacity: 0.5; +} From a0750a97a4c5a2a600d1054ec52a59fa3bfa4971 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Thu, 31 Oct 2024 14:53:54 -0300 Subject: [PATCH 03/22] feat: sign in modal --- .../PublishProject/Initial/component.tsx | 19 +++++++++++++++++++ .../Modals/PublishProject/component.tsx | 12 ++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx b/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx index c510c593..3a889527 100644 --- a/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx @@ -5,8 +5,27 @@ import WorldsPng from '/assets/images/worlds.png'; import type { Step } from '../types'; import './styles.css'; +import { useAuth } from '/@/hooks/useAuth'; +import { Button } from 'decentraland-ui2'; export function Initial({ onStepChange }: { onStepChange: (step: Step) => void }) { + const { isSignedIn, signIn } = useAuth(); + + if (!isSignedIn) { + return ( +
+

You need to sign in before you can publish your scene.

+ +
+ ); + } + return (
{t('modal.publish_project.select')} diff --git a/packages/renderer/src/components/Modals/PublishProject/component.tsx b/packages/renderer/src/components/Modals/PublishProject/component.tsx index fbf17a7b..5163cae7 100644 --- a/packages/renderer/src/components/Modals/PublishProject/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/component.tsx @@ -1,6 +1,7 @@ import { useCallback, useState } from 'react'; import { Modal } from 'decentraland-ui2/dist/components/Modal/Modal'; import { t } from '/@/modules/store/translation/utils'; +import { useAuth } from '/@/hooks/useAuth'; import { Initial } from './Initial'; import { AlternativeServers } from './AlternativeServers'; import { PublishToWorld } from './PublishToWorld'; @@ -12,6 +13,7 @@ import type { Target, Props, Step } from './types'; export function PublishProject({ open, project, onTarget, onClose }: Props) { const [step, setStep] = useState('initial'); const [history, setHistory] = useState([]); + const { isSignedIn } = useAuth(); const handleClose = useCallback(() => { setStep('initial'); @@ -45,13 +47,15 @@ export function PublishProject({ open, project, onTarget, onClose }: Props) { 0 ? handleBack : undefined} > {step === 'initial' && } {step === 'alternative-servers' && } From f14a5f3871bae13ac76b9e721140ce0a382d3547 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Fri, 1 Nov 2024 15:11:58 -0300 Subject: [PATCH 04/22] feat: publish modal --- .../src/components/EditorPage/component.tsx | 23 +--- .../AlternativeServers/component.tsx | 98 -------------- .../PublishProject/Deploy/component.tsx | 8 -- .../PublishProject/Initial/component.tsx | 58 -------- .../PublishProject/PublishModal/component.tsx | 45 +++++++ .../PublishProject/PublishModal/index.ts | 1 + .../Modals/PublishProject/component.tsx | 60 +++------ .../components/Modals/PublishProject/index.ts | 3 +- .../steps/AlternativeServers/component.tsx | 112 +++++++++++++++ .../{ => steps}/AlternativeServers/index.ts | 0 .../{ => steps}/AlternativeServers/styles.css | 0 .../PublishProject/steps/Deploy/component.tsx | 14 ++ .../{ => steps}/Deploy/index.ts | 0 .../{ => steps}/Deploy/styles.css | 0 .../steps/Initial/component.tsx | 68 ++++++++++ .../{ => steps}/Initial/index.ts | 0 .../{ => steps}/Initial/styles.css | 0 .../{ => steps}/PublishToLand/component.tsx | 127 +++++++++--------- .../{ => steps}/PublishToLand/index.ts | 0 .../{ => steps}/PublishToLand/types.ts | 0 .../{ => steps}/PublishToWorld/component.tsx | 54 ++++---- .../{ => steps}/PublishToWorld/index.ts | 0 .../{ => steps}/PublishToWorld/styles.css | 0 .../components/Modals/PublishProject/types.ts | 5 +- 24 files changed, 355 insertions(+), 321 deletions(-) delete mode 100644 packages/renderer/src/components/Modals/PublishProject/AlternativeServers/component.tsx delete mode 100644 packages/renderer/src/components/Modals/PublishProject/Deploy/component.tsx delete mode 100644 packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx create mode 100644 packages/renderer/src/components/Modals/PublishProject/PublishModal/component.tsx create mode 100644 packages/renderer/src/components/Modals/PublishProject/PublishModal/index.ts create mode 100644 packages/renderer/src/components/Modals/PublishProject/steps/AlternativeServers/component.tsx rename packages/renderer/src/components/Modals/PublishProject/{ => steps}/AlternativeServers/index.ts (100%) rename packages/renderer/src/components/Modals/PublishProject/{ => steps}/AlternativeServers/styles.css (100%) create mode 100644 packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx rename packages/renderer/src/components/Modals/PublishProject/{ => steps}/Deploy/index.ts (100%) rename packages/renderer/src/components/Modals/PublishProject/{ => steps}/Deploy/styles.css (100%) create mode 100644 packages/renderer/src/components/Modals/PublishProject/steps/Initial/component.tsx rename packages/renderer/src/components/Modals/PublishProject/{ => steps}/Initial/index.ts (100%) rename packages/renderer/src/components/Modals/PublishProject/{ => steps}/Initial/styles.css (100%) rename packages/renderer/src/components/Modals/PublishProject/{ => steps}/PublishToLand/component.tsx (65%) rename packages/renderer/src/components/Modals/PublishProject/{ => steps}/PublishToLand/index.ts (100%) rename packages/renderer/src/components/Modals/PublishProject/{ => steps}/PublishToLand/types.ts (100%) rename packages/renderer/src/components/Modals/PublishProject/{ => steps}/PublishToWorld/component.tsx (91%) rename packages/renderer/src/components/Modals/PublishProject/{ => steps}/PublishToWorld/index.ts (100%) rename packages/renderer/src/components/Modals/PublishProject/{ => steps}/PublishToWorld/styles.css (100%) diff --git a/packages/renderer/src/components/EditorPage/component.tsx b/packages/renderer/src/components/EditorPage/component.tsx index 68adc857..73ed9c9d 100644 --- a/packages/renderer/src/components/EditorPage/component.tsx +++ b/packages/renderer/src/components/EditorPage/component.tsx @@ -8,7 +8,6 @@ import PublicIcon from '@mui/icons-material/Public'; import { useSelector } from '#store'; -import { DEPLOY_URLS } from '/shared/types/deploy'; import { isWorkspaceError } from '/shared/types/workspace'; import { t } from '/@/modules/store/translation/utils'; @@ -17,7 +16,7 @@ import { useEditor } from '/@/hooks/useEditor'; import EditorPng from '/assets/images/editor.png'; -import { PublishProject, type StepValue } from '../Modals/PublishProject'; +import { PublishProject } from '../Modals/PublishProject'; import { Button } from '../Button'; import { Header } from '../Header'; import { Row } from '../Row'; @@ -35,7 +34,6 @@ export function EditorPage() { saveAndGetThumbnail, inspectorPort, openPreview, - publishScene, openCode, updateScene, loadingPreview, @@ -89,24 +87,6 @@ export function EditorPage() { setOpen(undefined); }, []); - const handleTarget = useCallback( - ({ target, value }: StepValue) => { - switch (target) { - case 'worlds': - return publishScene({ - targetContent: import.meta.env.VITE_WORLDS_SERVER || DEPLOY_URLS.WORLDS, - }); - case 'test': - return publishScene({ target: import.meta.env.VITE_TEST_SERVER || DEPLOY_URLS.TEST }); - case 'custom': - return publishScene({ target: value }); - default: - return publishScene(); - } - }, - [isReady], - ); - // inspector url const htmlUrl = `http://localhost:${import.meta.env.VITE_INSPECTOR_PORT || inspectorPort}`; let binIndexJsUrl = `${htmlUrl}/bin/index.js`; @@ -213,7 +193,6 @@ export function EditorPage() { open={open === 'publish'} project={project} onClose={handleCloseModal} - onTarget={handleTarget} /> )} diff --git a/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/component.tsx b/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/component.tsx deleted file mode 100644 index 57ffc8bd..00000000 --- a/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/component.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { type ChangeEvent, useCallback, useState } from 'react'; -import { Button, MenuItem, Select, type SelectChangeEvent } from 'decentraland-ui2'; -import { misc } from '#preload'; -import { t } from '/@/modules/store/translation/utils'; -import { isUrl } from '/shared/utils'; -import GenesisPlazaPng from '/assets/images/genesis_plaza.png'; -import type { AlternativeTarget, Target } from '../types'; - -import './styles.css'; - -export function AlternativeServers({ onTarget }: { onTarget: (target: Target) => void }) { - const [option, setOption] = useState('test'); - const [customUrl, setCustomUrl] = useState(''); - const [error, setError] = useState(''); - - const handleClick = useCallback(() => { - if (option === 'custom' && !isUrl(customUrl)) { - return setError(t('modal.publish_project.alternative_servers.errors.url')); - } - const value: Target = { target: option, value: customUrl }; - onTarget(value); - }, [option, customUrl]); - - const handleChangeSelect = useCallback((e: SelectChangeEvent) => { - setOption(e.target.value as AlternativeTarget); - }, []); - - const handleChangeCustom = useCallback( - (e: ChangeEvent) => { - if (error) setError(''); - setCustomUrl(e.target.value); - }, - [error], - ); - - const handleClickLearnMore = useCallback(() => { - if (option === 'custom') { - return misc.openExternal( - 'https://docs.decentraland.org/creator/development-guide/sdk7/publishing/#custom-servers', - ); - } - misc.openExternal( - 'https://docs.decentraland.org/creator/development-guide/sdk7/publishing/#the-test-server', - ); - }, [option]); - - return ( -
- {t('modal.publish_project.select')} -
-
-
-

{t('modal.publish_project.alternative_servers.list')}

- - {option === 'custom' && ( -
- - {t('modal.publish_project.alternative_servers.custom_server_url')} - - - {error} -
- )} -
- -
-
- - {t('option_box.learn_more')} - - -
-
-
- ); -} diff --git a/packages/renderer/src/components/Modals/PublishProject/Deploy/component.tsx b/packages/renderer/src/components/Modals/PublishProject/Deploy/component.tsx deleted file mode 100644 index 62aebfc4..00000000 --- a/packages/renderer/src/components/Modals/PublishProject/Deploy/component.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { Loader } from '/@/components/Loader'; -import { useEditor } from '/@/hooks/useEditor'; -import './styles.css'; - -export function Deploy() { - const { loadingPublish } = useEditor(); - return
{loadingPublish ? : 'Deploy'}
; -} diff --git a/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx b/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx deleted file mode 100644 index 3a889527..00000000 --- a/packages/renderer/src/components/Modals/PublishProject/Initial/component.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { OptionBox } from '/@/components/EditorPage/OptionBox'; -import { t } from '/@/modules/store/translation/utils'; -import LandPng from '/assets/images/land.png'; -import WorldsPng from '/assets/images/worlds.png'; -import type { Step } from '../types'; - -import './styles.css'; -import { useAuth } from '/@/hooks/useAuth'; -import { Button } from 'decentraland-ui2'; - -export function Initial({ onStepChange }: { onStepChange: (step: Step) => void }) { - const { isSignedIn, signIn } = useAuth(); - - if (!isSignedIn) { - return ( -
-

You need to sign in before you can publish your scene.

- -
- ); - } - - return ( -
- {t('modal.publish_project.select')} -
- onStepChange('publish-to-world')} - learnMoreUrl="https://docs.decentraland.org/creator/worlds/about/#publish-a-world" - /> - onStepChange('publish-to-land')} - learnMoreUrl="https://docs.decentraland.org/creator/development-guide/sdk7/publishing-permissions/#land-permission-options" - /> -
- onStepChange('alternative-servers')} - > - {t('modal.publish_project.alternative_servers.title')} - -
- ); -} diff --git a/packages/renderer/src/components/Modals/PublishProject/PublishModal/component.tsx b/packages/renderer/src/components/Modals/PublishProject/PublishModal/component.tsx new file mode 100644 index 00000000..49ffb743 --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/PublishModal/component.tsx @@ -0,0 +1,45 @@ +import { styled } from 'decentraland-ui2'; +import { Modal as BaseModal } from 'decentraland-ui2/dist/components/Modal/Modal'; +import { type ModalProps } from 'decentraland-ui2/dist/components/Modal/Modal.types'; + +function noop() {} + +const Modal = styled(BaseModal)(props => ({ + '& > .MuiPaper-root .MuiBox-root:first-child': { + paddingBottom: 8, + }, + '& > .MuiPaper-root .MuiBox-root:first-child h5': { + lineHeight: '2em', + }, + '& > .MuiPaper-root > h6': { + textAlign: 'center', + color: 'var(--dcl-silver)', + }, + '& > .MuiBackdrop-root': { + transition: 'none!important', + }, + '& > .MuiPaper-root': { + backgroundColor: 'var(--darker-gray)', + backgroundImage: 'none', + }, + '& [aria-label="back"]': + props.onBack !== noop + ? {} + : { + opacity: 0, + cursor: 'default', + }, +})); + +export function PublishModal(props: React.PropsWithChildren) { + const { onBack, ...rest } = props; + return ( + + {props.children} + + ); +} diff --git a/packages/renderer/src/components/Modals/PublishProject/PublishModal/index.ts b/packages/renderer/src/components/Modals/PublishProject/PublishModal/index.ts new file mode 100644 index 00000000..d236bc7f --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/PublishModal/index.ts @@ -0,0 +1 @@ +export { PublishModal } from './component'; diff --git a/packages/renderer/src/components/Modals/PublishProject/component.tsx b/packages/renderer/src/components/Modals/PublishProject/component.tsx index 5163cae7..9c3ba121 100644 --- a/packages/renderer/src/components/Modals/PublishProject/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/component.tsx @@ -1,19 +1,15 @@ import { useCallback, useState } from 'react'; -import { Modal } from 'decentraland-ui2/dist/components/Modal/Modal'; -import { t } from '/@/modules/store/translation/utils'; -import { useAuth } from '/@/hooks/useAuth'; -import { Initial } from './Initial'; -import { AlternativeServers } from './AlternativeServers'; -import { PublishToWorld } from './PublishToWorld'; -import { PublishToLand } from './PublishToLand'; -import { Deploy } from './Deploy'; +import { Initial } from './steps/Initial'; +import { AlternativeServers } from './steps/AlternativeServers'; +import { PublishToWorld } from './steps/PublishToWorld'; +import { PublishToLand } from './steps/PublishToLand'; +import { Deploy } from './steps/Deploy'; -import type { Target, Props, Step } from './types'; +import type { Props, Step } from './types'; -export function PublishProject({ open, project, onTarget, onClose }: Props) { +export function PublishProject({ open, project, onClose }: Omit) { const [step, setStep] = useState('initial'); const [history, setHistory] = useState([]); - const { isSignedIn } = useAuth(); const handleClose = useCallback(() => { setStep('initial'); @@ -27,7 +23,7 @@ export function PublishProject({ open, project, onTarget, onClose }: Props) { setHistory(history => (history.length > 0 ? history.slice(0, -1) : [])); }, [history, setStep, setHistory]); - const handleChangeStep = useCallback( + const handleStep = useCallback( (newStep: Step) => { setStep(newStep); setHistory(history => [...history, step]); @@ -35,33 +31,21 @@ export function PublishProject({ open, project, onTarget, onClose }: Props) { [step, setStep, setHistory], ); - const handleTarget = useCallback( - (value: Target) => { - onTarget(value); - handleChangeStep('deploy'); - }, - [onTarget, handleChangeStep], - ); + const props: Props = { + open, + project, + onClose: handleClose, + onBack: handleBack, + onStep: handleStep, + }; return ( - 0 ? handleBack : undefined} - > - {step === 'initial' && } - {step === 'alternative-servers' && } - {step === 'publish-to-land' && } - {step === 'publish-to-world' && } - {step === 'deploy' && } - + <> + {step === 'initial' && } + {step === 'alternative-servers' && } + {step === 'publish-to-land' && } + {step === 'publish-to-world' && } + {step === 'deploy' && } + ); } diff --git a/packages/renderer/src/components/Modals/PublishProject/index.ts b/packages/renderer/src/components/Modals/PublishProject/index.ts index 403779d9..8e2b6e6f 100644 --- a/packages/renderer/src/components/Modals/PublishProject/index.ts +++ b/packages/renderer/src/components/Modals/PublishProject/index.ts @@ -1,3 +1,2 @@ import { PublishProject } from './component'; -import type { Target } from './types'; -export { PublishProject, Target as StepValue }; +export { PublishProject }; diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/AlternativeServers/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/AlternativeServers/component.tsx new file mode 100644 index 00000000..fd436e11 --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/steps/AlternativeServers/component.tsx @@ -0,0 +1,112 @@ +import { type ChangeEvent, useCallback, useState } from 'react'; +import { Button, MenuItem, Select, type SelectChangeEvent } from 'decentraland-ui2'; +import { misc } from '#preload'; +import { t } from '/@/modules/store/translation/utils'; +import { isUrl } from '/shared/utils'; +import GenesisPlazaPng from '/assets/images/genesis_plaza.png'; +import type { AlternativeTarget, Props } from '../../types'; + +import './styles.css'; +import { useEditor } from '/@/hooks/useEditor'; +import { PublishModal } from '../../PublishModal'; + +export function AlternativeServers(props: Props) { + const { publishScene } = useEditor(); + const [option, setOption] = useState('test'); + const [customUrl, setCustomUrl] = useState(''); + const [error, setError] = useState(''); + + const handleClick = useCallback(() => { + if (option === 'custom' && !isUrl(customUrl)) { + return setError(t('modal.publish_project.alternative_servers.errors.url')); + } + if (option === 'test') { + void publishScene(); + } else if (option === 'custom') { + if (!isUrl(customUrl)) { + return setError(t('modal.publish_project.alternative_servers.errors.url')); + } + void publishScene({ target: customUrl }); + } else { + throw new Error('Invalid option'); + } + props.onStep('deploy'); + }, [option, customUrl, props.onStep, publishScene]); + + const handleChangeSelect = useCallback((e: SelectChangeEvent) => { + setOption(e.target.value as AlternativeTarget); + }, []); + + const handleChangeCustom = useCallback( + (e: ChangeEvent) => { + if (error) setError(''); + setCustomUrl(e.target.value); + }, + [error], + ); + + const handleClickLearnMore = useCallback(() => { + if (option === 'custom') { + return misc.openExternal( + 'https://docs.decentraland.org/creator/development-guide/sdk7/publishing/#custom-servers', + ); + } + misc.openExternal( + 'https://docs.decentraland.org/creator/development-guide/sdk7/publishing/#the-test-server', + ); + }, [option]); + + return ( + +
+ {t('modal.publish_project.select')} +
+
+
+

{t('modal.publish_project.alternative_servers.list')}

+ + {option === 'custom' && ( +
+ + {t('modal.publish_project.alternative_servers.custom_server_url')} + + + {error} +
+ )} +
+ +
+
+ + {t('option_box.learn_more')} + + +
+
+
+
+ ); +} diff --git a/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/index.ts b/packages/renderer/src/components/Modals/PublishProject/steps/AlternativeServers/index.ts similarity index 100% rename from packages/renderer/src/components/Modals/PublishProject/AlternativeServers/index.ts rename to packages/renderer/src/components/Modals/PublishProject/steps/AlternativeServers/index.ts diff --git a/packages/renderer/src/components/Modals/PublishProject/AlternativeServers/styles.css b/packages/renderer/src/components/Modals/PublishProject/steps/AlternativeServers/styles.css similarity index 100% rename from packages/renderer/src/components/Modals/PublishProject/AlternativeServers/styles.css rename to packages/renderer/src/components/Modals/PublishProject/steps/AlternativeServers/styles.css diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx new file mode 100644 index 00000000..d7da80c1 --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx @@ -0,0 +1,14 @@ +import { Loader } from '/@/components/Loader'; +import { useEditor } from '/@/hooks/useEditor'; +import { type Props } from '../../types'; +import './styles.css'; +import { PublishModal } from '../../PublishModal'; + +export function Deploy(props: Props) { + const { loadingPublish } = useEditor(); + return ( + +
{loadingPublish ? : 'Deploy'}
+
+ ); +} diff --git a/packages/renderer/src/components/Modals/PublishProject/Deploy/index.ts b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/index.ts similarity index 100% rename from packages/renderer/src/components/Modals/PublishProject/Deploy/index.ts rename to packages/renderer/src/components/Modals/PublishProject/steps/Deploy/index.ts diff --git a/packages/renderer/src/components/Modals/PublishProject/Deploy/styles.css b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css similarity index 100% rename from packages/renderer/src/components/Modals/PublishProject/Deploy/styles.css rename to packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Initial/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/Initial/component.tsx new file mode 100644 index 00000000..d5e2942f --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Initial/component.tsx @@ -0,0 +1,68 @@ +import { OptionBox } from '/@/components/EditorPage/OptionBox'; +import { t } from '/@/modules/store/translation/utils'; +import LandPng from '/assets/images/land.png'; +import WorldsPng from '/assets/images/worlds.png'; +import type { Props } from '../../types'; + +import './styles.css'; +import { useAuth } from '/@/hooks/useAuth'; +import { Button } from 'decentraland-ui2'; +import { PublishModal } from '../../PublishModal'; + +export function Initial(props: Props) { + const { isSignedIn, signIn } = useAuth(); + let content = null; + if (!isSignedIn) { + content = ( +
+

You need to sign in before you can publish your scene.

+ +
+ ); + } else { + content = ( +
+
+ props.onStep('publish-to-world')} + learnMoreUrl="https://docs.decentraland.org/creator/worlds/about/#publish-a-world" + /> + props.onStep('publish-to-land')} + learnMoreUrl="https://docs.decentraland.org/creator/development-guide/sdk7/publishing-permissions/#land-permission-options" + /> +
+ props.onStep('alternative-servers')} + > + {t('modal.publish_project.alternative_servers.title')} + +
+ ); + } + const { onBack: _, ...rest } = props; + return ( + + {content} + + ); +} diff --git a/packages/renderer/src/components/Modals/PublishProject/Initial/index.ts b/packages/renderer/src/components/Modals/PublishProject/steps/Initial/index.ts similarity index 100% rename from packages/renderer/src/components/Modals/PublishProject/Initial/index.ts rename to packages/renderer/src/components/Modals/PublishProject/steps/Initial/index.ts diff --git a/packages/renderer/src/components/Modals/PublishProject/Initial/styles.css b/packages/renderer/src/components/Modals/PublishProject/steps/Initial/styles.css similarity index 100% rename from packages/renderer/src/components/Modals/PublishProject/Initial/styles.css rename to packages/renderer/src/components/Modals/PublishProject/steps/Initial/styles.css diff --git a/packages/renderer/src/components/Modals/PublishProject/PublishToLand/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx similarity index 65% rename from packages/renderer/src/components/Modals/PublishProject/PublishToLand/component.tsx rename to packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx index 44992c01..b7258627 100644 --- a/packages/renderer/src/components/Modals/PublishProject/PublishToLand/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx @@ -14,7 +14,8 @@ import { useEditor } from '/@/hooks/useEditor'; import { useWorkspace } from '/@/hooks/useWorkspace'; import { COLORS, type Coordinate } from './types'; -import { type Target } from '../types'; +import { type Props } from '../../types'; +import { PublishModal } from '../../PublishModal'; function calculateParcels(project: Project, point: Coordinate): Coordinate[] { const [baseX, baseY] = project.scene.base.split(',').map(coord => parseInt(coord, 10)); @@ -24,8 +25,8 @@ function calculateParcels(project: Project, point: Coordinate): Coordinate[] { }); } -export function PublishToLand({ onTarget }: { onTarget: (target: Target) => void }) { - const { project } = useEditor(); +export function PublishToLand(props: Props) { + const { project, publishScene } = useEditor(); const { updateProject, updateSceneJson } = useWorkspace(); const tiles = useSelector(state => state.land.tiles); const landTiles = useSelector(state => landSelectors.getLandTiles(state.land)); @@ -38,11 +39,11 @@ export function PublishToLand({ onTarget }: { onTarget: (target: Target) => void const projectParcels = useMemo(() => calculateParcels(project, hover), [project, hover]); const handleNext = useCallback(() => { - onTarget({ - target: 'land', - value: import.meta.env.VITE_CATALYST_SERVER || DEPLOY_URLS.CATALYST_SERVER, + void publishScene({ + target: import.meta.env.VITE_CATALYST_SERVER || DEPLOY_URLS.CATALYST_SERVER, }); - }, [onTarget]); + props.onStep('deploy'); + }, [props.onStep]); const handleHover = useCallback((x: number, y: number) => { setHover({ x, y }); @@ -130,65 +131,67 @@ export function PublishToLand({ onTarget }: { onTarget: (target: Target) => void }, []); return ( - - - {/* @ts-expect-error TODO: Update properties in UI2, making the not required `optional` */} - - - + + - - {placement - ? t('modal.publish_project.land.select_parcel.place_scene', { - coords: `${placement.x},${placement.y}`, - }) - : t('modal.publish_project.land.select_parcel.select_parcel')} - - {placement && ( - - )} + {/* @ts-expect-error TODO: Update properties in UI2, making the not required `optional` */} + - + + + {placement + ? t('modal.publish_project.land.select_parcel.place_scene', { + coords: `${placement.x},${placement.y}`, + }) + : t('modal.publish_project.land.select_parcel.select_parcel')} + + {placement && ( + + )} + + + - + ); } diff --git a/packages/renderer/src/components/Modals/PublishProject/PublishToLand/index.ts b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/index.ts similarity index 100% rename from packages/renderer/src/components/Modals/PublishProject/PublishToLand/index.ts rename to packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/index.ts diff --git a/packages/renderer/src/components/Modals/PublishProject/PublishToLand/types.ts b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/types.ts similarity index 100% rename from packages/renderer/src/components/Modals/PublishProject/PublishToLand/types.ts rename to packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/types.ts diff --git a/packages/renderer/src/components/Modals/PublishProject/PublishToWorld/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/component.tsx similarity index 91% rename from packages/renderer/src/components/Modals/PublishProject/PublishToWorld/component.tsx rename to packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/component.tsx index 3bddee86..53643c7a 100644 --- a/packages/renderer/src/components/Modals/PublishProject/PublishToWorld/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/component.tsx @@ -31,30 +31,37 @@ import EmptyWorldSVG from '/assets/images/empty-deploy-to-world.svg'; import LogoDCLSVG from '/assets/images/logo-dcl.svg'; import LogoENSSVG from '/assets/images/logo-ens.svg'; -import { Button } from '../../../Button'; -import { type Target } from '../types'; +import { Button } from '../../../../Button'; +import { type Props } from '../../types'; import './styles.css'; +import { PublishModal } from '../../PublishModal'; -export function PublishToWorld({ onTarget }: { onTarget: (target: Target) => void }) { - const { project } = useEditor(); +export function PublishToWorld(props: Props) { + const { project, publishScene } = useEditor(); const names = useSelector(state => state.ens.data); const emptyNames = Object.keys(names).length === 0; const handleNext = useCallback(() => { - onTarget({ - target: 'worlds', - value: import.meta.env.VITE_WORLDS_SERVER || DEPLOY_URLS.WORLDS, - }); - }, [onTarget]); + publishScene({ targetContent: import.meta.env.VITE_WORLDS_SERVER || DEPLOY_URLS.WORLDS }); + props.onStep('deploy'); + }, [props.onStep, publishScene]); - return emptyNames ? ( - - ) : ( - + return ( + + {!emptyNames ? ( + + ) : ( + + )} + ); } @@ -140,21 +147,6 @@ function SelectWorld({ project, onPublish }: { project: Project; onPublish: () = return (
-
- - {t('modal.publish_project.worlds.select_world.title')} - - - {t('modal.publish_project.worlds.select_world.description')} - -
{!projectIsReady ? : } diff --git a/packages/renderer/src/components/Modals/PublishProject/PublishToWorld/index.ts b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/index.ts similarity index 100% rename from packages/renderer/src/components/Modals/PublishProject/PublishToWorld/index.ts rename to packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/index.ts diff --git a/packages/renderer/src/components/Modals/PublishProject/PublishToWorld/styles.css b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/styles.css similarity index 100% rename from packages/renderer/src/components/Modals/PublishProject/PublishToWorld/styles.css rename to packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/styles.css diff --git a/packages/renderer/src/components/Modals/PublishProject/types.ts b/packages/renderer/src/components/Modals/PublishProject/types.ts index e1000522..b78a0659 100644 --- a/packages/renderer/src/components/Modals/PublishProject/types.ts +++ b/packages/renderer/src/components/Modals/PublishProject/types.ts @@ -4,7 +4,8 @@ export type Props = { open: boolean; project: Project; onClose: () => void; - onTarget: (value: Target) => void; + onBack?: () => void; + onStep: (step: Step) => void; }; export type Step = @@ -13,8 +14,8 @@ export type Step = | 'publish-to-world' | 'publish-to-land' | 'deploy'; + export type InitialTarget = 'worlds' | 'land'; export type AlternativeTarget = 'test' | 'custom'; export type TargetType = InitialTarget | AlternativeTarget; -export type Target = { target: TargetType; value?: string }; From a11c595d2e1eb770475fb0f921c18cffa2957b50 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Fri, 1 Nov 2024 15:58:56 -0300 Subject: [PATCH 05/22] chore: fixes --- .../steps/AlternativeServers/component.tsx | 7 +- .../steps/Initial/component.tsx | 98 ++++++++++--------- .../steps/PublishToLand/component.tsx | 19 +++- .../steps/PublishToWorld/styles.css | 1 + 4 files changed, 71 insertions(+), 54 deletions(-) diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/AlternativeServers/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/AlternativeServers/component.tsx index fd436e11..54d45cd0 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/AlternativeServers/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/AlternativeServers/component.tsx @@ -57,9 +57,12 @@ export function AlternativeServers(props: Props) { }, [option]); return ( - +
- {t('modal.publish_project.select')}
diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Initial/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/Initial/component.tsx index d5e2942f..791fe37b 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Initial/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Initial/component.tsx @@ -11,58 +11,60 @@ import { PublishModal } from '../../PublishModal'; export function Initial(props: Props) { const { isSignedIn, signIn } = useAuth(); - let content = null; + const { onBack: _, ...rest } = props; if (!isSignedIn) { - content = ( -
-

You need to sign in before you can publish your scene.

- -
+ return ( + +
+ +
+
); } else { - content = ( -
-
- props.onStep('publish-to-world')} - learnMoreUrl="https://docs.decentraland.org/creator/worlds/about/#publish-a-world" - /> - props.onStep('publish-to-land')} - learnMoreUrl="https://docs.decentraland.org/creator/development-guide/sdk7/publishing-permissions/#land-permission-options" - /> + return ( + +
+
+ props.onStep('publish-to-world')} + learnMoreUrl="https://docs.decentraland.org/creator/worlds/about/#publish-a-world" + /> + props.onStep('publish-to-land')} + learnMoreUrl="https://docs.decentraland.org/creator/development-guide/sdk7/publishing-permissions/#land-permission-options" + /> +
+ props.onStep('alternative-servers')} + > + {t('modal.publish_project.alternative_servers.title')} +
- props.onStep('alternative-servers')} - > - {t('modal.publish_project.alternative_servers.title')} - -
+ ); } - const { onBack: _, ...rest } = props; - return ( - - {content} - - ); } diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx index b7258627..4bdffebf 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx @@ -1,5 +1,5 @@ import { useCallback, useMemo, useState } from 'react'; -import { Box, Button, Typography } from 'decentraland-ui2'; +import { Box, Button, styled, Typography } from 'decentraland-ui2'; import { Atlas } from 'decentraland-ui2/dist/components/Atlas/Atlas'; import type { SceneParcels } from '@dcl/schemas'; @@ -25,6 +25,12 @@ function calculateParcels(project: Project, point: Coordinate): Coordinate[] { }); } +const PublishToLandModal = styled(PublishModal)({ + '& > .MuiPaper-root > .MuiBox-root:last-child': { + padding: 0, + }, +}); + export function PublishToLand(props: Props) { const { project, publishScene } = useEditor(); const { updateProject, updateSceneJson } = useWorkspace(); @@ -131,7 +137,11 @@ export function PublishToLand(props: Props) { }, []); return ( - + - + ); } diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/styles.css b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/styles.css index df36afe0..50869598 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/styles.css +++ b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/styles.css @@ -24,6 +24,7 @@ .MuiModal-root .SelectWorld .thumbnail img { width: 100%; object-fit: cover; + border-radius: 8px; } .MuiModal-root .SelectWorld .box .selection { From c16144b25a8c2e14835ac5bb55e11cb7f8ffe49b Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Mon, 4 Nov 2024 21:17:46 -0300 Subject: [PATCH 06/22] feat: deploy screen --- .../renderer/src/components/Loader/styles.css | 2 +- .../PublishProject/steps/Deploy/component.tsx | 151 +++++++++++++++++- .../steps/Deploy/images/pin.svg | 6 + .../steps/Deploy/images/verified.svg | 29 ++++ .../PublishProject/steps/Deploy/styles.css | 130 ++++++++++++++- .../PublishProject/steps/Deploy/types.ts | 16 ++ packages/renderer/src/hooks/useIsMounted.ts | 14 ++ types/env.d.ts | 1 + 8 files changed, 343 insertions(+), 6 deletions(-) create mode 100644 packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/pin.svg create mode 100644 packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/verified.svg create mode 100644 packages/renderer/src/components/Modals/PublishProject/steps/Deploy/types.ts create mode 100644 packages/renderer/src/hooks/useIsMounted.ts diff --git a/packages/renderer/src/components/Loader/styles.css b/packages/renderer/src/components/Loader/styles.css index 876f4e63..aade5f89 100644 --- a/packages/renderer/src/components/Loader/styles.css +++ b/packages/renderer/src/components/Loader/styles.css @@ -1,6 +1,6 @@ .Loader { width: 100%; - height: 100vh; + height: 100%; display: flex; justify-content: center; align-items: center; diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx index d7da80c1..85a3b12c 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx @@ -1,14 +1,157 @@ +import { useEffect, useState } from 'react'; import { Loader } from '/@/components/Loader'; import { useEditor } from '/@/hooks/useEditor'; +import { useIsMounted } from '/@/hooks/useIsMounted'; +import { PublishModal } from '../../PublishModal'; import { type Props } from '../../types'; +import type { File, Info } from './types'; import './styles.css'; -import { PublishModal } from '../../PublishModal'; +import { useAuth } from '/@/hooks/useAuth'; +import { ChainId } from '@dcl/schemas'; +import { addBase64ImagePrefix } from '/@/modules/image'; +import { Typography } from 'decentraland-ui2'; +import { Button } from '../../../../Button'; + +const MAX_FILE_PATH_LENGTH = 30; + +function getPath(filename: string) { + return filename.length > MAX_FILE_PATH_LENGTH + ? `${filename.slice(0, MAX_FILE_PATH_LENGTH / 2)}...${filename.slice( + -(MAX_FILE_PATH_LENGTH / 2), + )}` + : filename; +} + +const KB = 1024; +const MB = KB * 1024; +const GB = MB * 1024; + +function getSize(size: number) { + if (size < KB) { + return `${size.toFixed(2)} B`; + } + if (size < MB) { + return `${(size / KB).toFixed(2)} KB`; + } + if (size < GB) { + return `${(size / MB).toFixed(2)} MB`; + } + return `${(size / GB).toFixed(2)} GB`; +} export function Deploy(props: Props) { - const { loadingPublish } = useEditor(); + const { chainId, wallet, avatar } = useAuth(); + const [files, setFiles] = useState([]); + const [info, setInfo] = useState(null); + const { loadingPublish, publishPort, project } = useEditor(); + const isMounted = useIsMounted(); + + useEffect(() => { + const port = import.meta.env.VITE_CLI_DEPLOY_PORT || publishPort; + const url = `http://localhost:${port}/api`; + async function fetchFiles() { + const resp = await fetch(`${url}/files`); + const files = await resp.json(); + return files as File[]; + } + async function fetchInfo() { + const resp = await fetch(`${url}/info`); + const info = await resp.json(); + return info as Info; + } + if (port) { + void Promise.all([fetchFiles(), fetchInfo()]).then(([files, info]) => { + if (!isMounted()) return; + setFiles(files); + setInfo(info); + }); + } + }, [publishPort]); return ( - -
{loadingPublish ? : 'Deploy'}
+ +
+ {loadingPublish || !info ? ( + + ) : ( + <> +
+
+ {chainId === ChainId.ETHEREUM_MAINNET ? 'Mainnet' : 'Testnet'} +
+ {wallet ? ( +
+ {wallet.slice(0, 6)}...{wallet.slice(-4)} +
+ ) : null} + {info.isWorld ? ( + avatar ? ( +
+ {avatar.name} + {avatar.hasClaimedName ? : null} +
+ ) : null + ) : ( +
+ + {info.baseParcel} +
+ )} +
+
+
+ {project ? ( +
+ ) : null} +
+ {info.title} + + {info.description} + +
+
+
+
+
{files.length} files
+
+ Total Size:{' '} + {getSize(files.reduce((total, file) => total + file.size, 0))} +
+
+
+ {files.map(file => ( +
+
+ {getPath(file.name)} +
+
{getSize(file.size)}
+
+ ))} +
+
+ +
+
+
+ + )} +
); } diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/pin.svg b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/pin.svg new file mode 100644 index 00000000..d8f3ea56 --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/pin.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/verified.svg b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/verified.svg new file mode 100644 index 00000000..046adbcd --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/verified.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css index 4d68c41b..3c260dcb 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css @@ -1,3 +1,131 @@ .Deploy { - color: red; + min-height: 300px; + display: flex; + flex-direction: column; + gap: 24px; +} + +.Deploy .Loader { + top: 110px; + position: relative; +} + +.Deploy .ethereum { + display: flex; + gap: 8px; +} + +.Deploy .chip { + min-height: 24px; + border-radius: 16px; + padding: 4px 10px; + font-size: 13px; + line-height: 18px; + background-color: rgba(255, 255, 255, 0.08); + font-weight: 600; + display: flex; + align-items: center; + gap: 4px; +} + +.Deploy .chip.network { + background-color: #1f87e5; + text-transform: uppercase; +} + +.Deploy .chip.username .verified { + display: inline-flex; + width: 16px; + height: 16px; + background: center no-repeat url(images/verified.svg); +} + +.Deploy .chip.parcel .pin { + display: inline-flex; + width: 16px; + height: 16px; + background: center no-repeat url(images/pin.svg); +} + +.Deploy .scene { + display: flex; + gap: 32px; +} + +.Deploy .scene .info { + flex: 0 0 auto; + width: 256px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.Deploy .scene .info .thumbnail { + width: 100%; + height: 160px; + background-position: center; + background-size: cover; + background-repeat: no-repeat; + border-radius: 8px; +} + +.Deploy .scene .info .text { + display: flex; + flex-direction: column; + gap: 8px; +} + +.Deploy .scene .files { + display: flex; + flex-direction: column; + gap: 16px; + justify-content: stretch; + flex: 1 1 auto; +} + +.Deploy .scene .files .filters { + display: flex; + flex: 0 0 auto; + justify-content: space-between; + color: #a09ba8; + background-color: rgba(255, 255, 255, 0.05); + height: 48px; + align-items: center; + padding: 0px 16px; + border-radius: 48px; +} + +.Deploy .scene .files .filters .count { + flex: none; + text-transform: uppercase; +} + +.Deploy .scene .files .filters .size { + flex: none; +} + +.Deploy .scene .files .list { + flex: 1 1 auto; + display: flex; + flex-direction: column; + overflow: auto; + max-height: 400px; +} + +.Deploy .scene .files .list .file { + display: flex; + justify-content: space-between; + border-bottom: 1px solid rgba(255, 255, 255, 0.12); + padding: 6px 16px; + color: #a09ba8; +} + +.Deploy .scene .files .list .file:last-child { + border-bottom: none; +} + +.Deploy .scene .files .actions { + flex: 0 0 auto; + display: flex; + justify-content: flex-end; } diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/types.ts b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/types.ts new file mode 100644 index 00000000..ec6eb7b7 --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/types.ts @@ -0,0 +1,16 @@ +export type File = { + name: string; + size: number; +}; + +export type Info = { + baseParcel: string; + debug: boolean; + description: string; + isPortableExperience: boolean; + isWorld: boolean; + parcels: string[]; + rootCID: string; + skipValidations: boolean; + title: string; +}; diff --git a/packages/renderer/src/hooks/useIsMounted.ts b/packages/renderer/src/hooks/useIsMounted.ts new file mode 100644 index 00000000..739300df --- /dev/null +++ b/packages/renderer/src/hooks/useIsMounted.ts @@ -0,0 +1,14 @@ +import { useCallback, useEffect, useRef } from 'react'; + +export function useIsMounted() { + const isMounted = useRef(false); + + useEffect(() => { + isMounted.current = true; + return () => { + isMounted.current = false; + }; + }, []); + + return useCallback(() => isMounted.current, []); +} diff --git a/types/env.d.ts b/types/env.d.ts index a9bd0fc9..48413b14 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -29,6 +29,7 @@ interface ImportMetaEnv { VITE_ASSET_PACKS_CONTENT_URL: string | undefined; VITE_ASSET_PACKS_JS_PORT: string | undefined; VITE_ASSET_PACKS_JS_PATH: string | undefined; + VITE_CLI_DEPLOY_PORT: string | undefined; // Publish VITE_WORLDS_SERVER: string | undefined; From c9773d73f30901c77c2c08508a0e83847cdde74c Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Tue, 5 Nov 2024 10:55:59 -0300 Subject: [PATCH 07/22] feat: initial placement --- .../PublishProject/steps/Deploy/component.tsx | 3 +- .../steps/PublishToLand/component.tsx | 41 +++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx index 85a3b12c..21eafd38 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx @@ -12,7 +12,7 @@ import { addBase64ImagePrefix } from '/@/modules/image'; import { Typography } from 'decentraland-ui2'; import { Button } from '../../../../Button'; -const MAX_FILE_PATH_LENGTH = 30; +const MAX_FILE_PATH_LENGTH = 50; function getPath(filename: string) { return filename.length > MAX_FILE_PATH_LENGTH @@ -72,6 +72,7 @@ export function Deploy(props: Props) { title={ info ? (info.isWorld ? 'Publish to your World' : 'Publish to your Land') : 'Loading...' } + size="large" {...props} >
diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx index 4bdffebf..7c9f73ca 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { Box, Button, styled, Typography } from 'decentraland-ui2'; import { Atlas } from 'decentraland-ui2/dist/components/Atlas/Atlas'; import type { SceneParcels } from '@dcl/schemas'; @@ -17,10 +17,13 @@ import { COLORS, type Coordinate } from './types'; import { type Props } from '../../types'; import { PublishModal } from '../../PublishModal'; +function parseCoords(coords: string) { + return coords.split(',').map(coord => parseInt(coord, 10)) as [number, number]; +} function calculateParcels(project: Project, point: Coordinate): Coordinate[] { - const [baseX, baseY] = project.scene.base.split(',').map(coord => parseInt(coord, 10)); + const [baseX, baseY] = parseCoords(project.scene.base); return project.scene.parcels.map(parcel => { - const [x, y] = parcel.split(',').map(coord => parseInt(coord, 10)); + const [x, y] = parseCoords(parcel); return { x: x - baseX + point.x, y: y - baseY + point.y }; }); } @@ -38,6 +41,8 @@ export function PublishToLand(props: Props) { const landTiles = useSelector(state => landSelectors.getLandTiles(state.land)); const [hover, setHover] = useState({ x: 0, y: 0 }); const [placement, setPlacement] = useState(null); + const [initialPlacement, setInitialPlacement] = useState(null); + const [didAutoPlace, setDidAutoPlace] = useState(false); if (!project) return null; @@ -136,6 +141,34 @@ export function PublishToLand(props: Props) { setPlacement(null); }, []); + // set initial placement + useEffect(() => { + if (!initialPlacement) { + const { base, parcels } = project.scene; + // use the base parcel if it's a valid coord + if (base in landTiles && parcels.every(parcel => parcel in landTiles)) { + const [x, y] = parseCoords(base); + setInitialPlacement({ x, y }); + } else { + // if the base parcel in the scene.json is not valid (ie. it is 0,0) then select the first coord of the available tiles as the initial placement + const available = Object.keys(landTiles); + if (available.length > 0) { + const [x, y] = parseCoords(available[0]); + setInitialPlacement({ x, y }); + } + } + } + }, [initialPlacement, setInitialPlacement, project, landTiles]); + + // use initial placement if possible + useEffect(() => { + if (didAutoPlace) return; + if (!placement && initialPlacement) { + setPlacement(initialPlacement); + setDidAutoPlace(true); + } + }, [placement, setPlacement, initialPlacement, didAutoPlace, setDidAutoPlace]); + return ( Date: Tue, 5 Nov 2024 11:04:54 -0300 Subject: [PATCH 08/22] fix: set placement only if all parcels are valid --- .../PublishProject/steps/PublishToLand/component.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx index 7c9f73ca..19436857 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx @@ -164,10 +164,13 @@ export function PublishToLand(props: Props) { useEffect(() => { if (didAutoPlace) return; if (!placement && initialPlacement) { - setPlacement(initialPlacement); - setDidAutoPlace(true); + const initialPlacementParcels = calculateParcels(project, initialPlacement); + if (initialPlacementParcels.every(({ x, y }) => !!landTiles[`${x},${y}`])) { + setPlacement(initialPlacement); + setDidAutoPlace(true); + } } - }, [placement, setPlacement, initialPlacement, didAutoPlace, setDidAutoPlace]); + }, [placement, setPlacement, initialPlacement, didAutoPlace, setDidAutoPlace, landTiles]); return ( Date: Tue, 5 Nov 2024 12:53:57 -0300 Subject: [PATCH 09/22] feat: deploy --- .../PublishProject/steps/Deploy/component.tsx | 76 ++++++++++++++++--- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx index 21eafd38..7f413feb 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx @@ -1,4 +1,6 @@ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { type AuthChain, Authenticator } from '@dcl/crypto'; +import { localStorageGetIdentity } from '@dcl/single-sign-on-client'; import { Loader } from '/@/components/Loader'; import { useEditor } from '/@/hooks/useEditor'; import { useIsMounted } from '/@/hooks/useIsMounted'; @@ -45,10 +47,55 @@ export function Deploy(props: Props) { const [info, setInfo] = useState(null); const { loadingPublish, publishPort, project } = useEditor(); const isMounted = useIsMounted(); + const [isDeploying, setIsDeploying] = useState(false); + const [error, setError] = useState(null); - useEffect(() => { + const url = useMemo(() => { const port = import.meta.env.VITE_CLI_DEPLOY_PORT || publishPort; - const url = `http://localhost:${port}/api`; + return port ? `http://localhost:${port}/api` : null; + }, [publishPort]); + + const handlePublish = useCallback(() => { + if (!url) return; + async function deploy(payload: { address: string; authChain: AuthChain; chainId: ChainId }) { + setIsDeploying(true); + setError(null); + const resp = await fetch(`${url}/deploy`, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }); + if (resp.ok) { + return; + } + const error = (await resp.json()).message as string; + throw new Error(error); + } + if (wallet && info && info.rootCID) { + const identity = localStorageGetIdentity(wallet); + if (identity && chainId) { + const authChain = Authenticator.signPayload(identity, info.rootCID); + void deploy({ address: wallet, authChain, chainId }) + .then(() => { + if (!isMounted()) return; + setIsDeploying(false); + console.log('success'); + }) + .catch(error => { + setIsDeploying(false); + setError(error.message); + console.log('error', error); + }); + } else { + setError('Invalid identity or chainId'); + } + } + }, [wallet, info, url, chainId]); + + useEffect(() => { + if (!url) return; async function fetchFiles() { const resp = await fetch(`${url}/files`); const files = await resp.json(); @@ -59,14 +106,12 @@ export function Deploy(props: Props) { const info = await resp.json(); return info as Info; } - if (port) { - void Promise.all([fetchFiles(), fetchInfo()]).then(([files, info]) => { - if (!isMounted()) return; - setFiles(files); - setInfo(info); - }); - } - }, [publishPort]); + void Promise.all([fetchFiles(), fetchInfo()]).then(([files, info]) => { + if (!isMounted()) return; + setFiles(files); + setInfo(info); + }); + }, [url]); return (
- + {error ?

{error}

: null} +
From abe45c9689054a590a1fdb07fad159ffa9b2e3b5 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Tue, 5 Nov 2024 12:58:13 -0300 Subject: [PATCH 10/22] chore: use local ui2 for now --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4e2b8815..457ff29f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "@vitejs/plugin-react": "^4.3.1", "classnames": "^2.5.1", "cross-env": "7.0.3", - "decentraland-ui2": "^0.6.0", + "decentraland-ui2": "file:decentraland-ui2-0.0.0-development.tgz", "electron": "31.1.0", "electron-builder": "24.13.3", "eslint": "8.57.0", @@ -9454,9 +9454,9 @@ } }, "node_modules/decentraland-ui2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/decentraland-ui2/-/decentraland-ui2-0.6.0.tgz", - "integrity": "sha512-Wgsge+Yt4pJPR7nAD2ATEvNeS0IOD/kbLeVhozCFnTToMr1qZNklPCvEc+pX5JAXqIQur5kb86b4lR7sV7SqhQ==", + "version": "0.0.0-development", + "resolved": "file:decentraland-ui2-0.0.0-development.tgz", + "integrity": "sha512-sA7RwIeeo4SYgtxOrr6EcKg0RahgMV8jnfX0xMl+O+jdFrt+4jKqyuGA1t9YqvY5YkpsftBO4OWpy0v+Xa9blw==", "dev": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index d3877df7..ca0dc3df 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "@vitejs/plugin-react": "^4.3.1", "classnames": "^2.5.1", "cross-env": "7.0.3", - "decentraland-ui2": "^0.6.0", + "decentraland-ui2": "file:decentraland-ui2-0.0.0-development.tgz", "electron": "31.1.0", "electron-builder": "24.13.3", "eslint": "8.57.0", From b5c62e3717685ad78318b48ef6547542a3523bf5 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Tue, 5 Nov 2024 18:09:16 -0300 Subject: [PATCH 11/22] fix: remove worldConfiguration from scene --- .../PublishProject/steps/Deploy/component.tsx | 3 +- .../steps/Deploy/images/deploy.svg | 5 +++ .../PublishProject/steps/Deploy/styles.css | 27 ++++++++++++-- .../steps/PublishToLand/component.tsx | 35 +++++++++---------- .../steps/PublishToWorld/component.tsx | 2 +- packages/renderer/src/hooks/useWorkspace.ts | 2 +- 6 files changed, 50 insertions(+), 24 deletions(-) create mode 100644 packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/deploy.svg diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx index 7f413feb..91032def 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx @@ -191,13 +191,14 @@ export function Deploy(props: Props) { ))}
- {error ?

{error}

: null} +

{error}

diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/deploy.svg b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/deploy.svg new file mode 100644 index 00000000..93cf28cd --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/deploy.svg @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css index 3c260dcb..3b2517a4 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css @@ -5,7 +5,7 @@ gap: 24px; } -.Deploy .Loader { +.Deploy > .Loader { top: 110px; position: relative; } @@ -127,5 +127,28 @@ .Deploy .scene .files .actions { flex: 0 0 auto; display: flex; - justify-content: flex-end; + justify-content: space-between; + align-items: center; +} + +.Deploy .scene .files .Button .deploy-icon { + width: 24px; + height: 24px; + background: center no-repeat url(images/deploy.svg); + margin-left: 8px; +} + +.Deploy .scene .files .Button .Loader { + margin-left: 12px; +} + +.Deploy .scene .files .Button { + font-size: 15px; + padding: 8px 22px; + max-height: 40px; + flex: none; +} + +.Deploy .scene .files .error { + color: var(--error); } diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx index 19436857..65501ba3 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx @@ -49,12 +49,24 @@ export function PublishToLand(props: Props) { // Memoize the project parcels centered around the hover position const projectParcels = useMemo(() => calculateParcels(project, hover), [project, hover]); - const handleNext = useCallback(() => { + const handleNext = useCallback(async () => { + if (!placement) return; + const sceneUpdates: SceneParcels = { + base: `${placement.x},${placement.y}`, + parcels: calculateParcels(project, placement).map(({ x, y }) => `${x},${y}`), + }; + await updateSceneJson(project.path, { scene: sceneUpdates, worldConfiguration: undefined }); + updateProject({ + ...project, + scene: sceneUpdates, + worldConfiguration: undefined, // Cannot deploy to a LAND with a world configuration + updatedAt: Date.now(), + }); void publishScene({ target: import.meta.env.VITE_CATALYST_SERVER || DEPLOY_URLS.CATALYST_SERVER, }); props.onStep('deploy'); - }, [props.onStep]); + }, [placement, props.onStep]); const handleHover = useCallback((x: number, y: number) => { setHover({ x, y }); @@ -117,24 +129,9 @@ export function PublishToLand(props: Props) { const handlePlacement = useCallback( (x: number, y: number) => { if (!isValid) return; - - const newPlacement = { x, y }; - setPlacement(newPlacement); - - const sceneUpdates: SceneParcels = { - base: `${x},${y}`, - parcels: calculateParcels(project, newPlacement).map(({ x, y }) => `${x},${y}`), - }; - - updateSceneJson(project.path, { scene: sceneUpdates }); - updateProject({ - ...project, - scene: sceneUpdates, - worldConfiguration: undefined, // Cannot deploy to a LAND with a world configuration - updatedAt: Date.now(), - }); + setPlacement({ x, y }); }, - [project, isValid, updateProject, updateSceneJson], + [project, isValid], ); const handleClearPlacement = useCallback(() => { diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/component.tsx index 53643c7a..6084f13d 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/component.tsx @@ -31,11 +31,11 @@ import EmptyWorldSVG from '/assets/images/empty-deploy-to-world.svg'; import LogoDCLSVG from '/assets/images/logo-dcl.svg'; import LogoENSSVG from '/assets/images/logo-ens.svg'; +import { PublishModal } from '../../PublishModal'; import { Button } from '../../../../Button'; import { type Props } from '../../types'; import './styles.css'; -import { PublishModal } from '../../PublishModal'; export function PublishToWorld(props: Props) { const { project, publishScene } = useEditor(); diff --git a/packages/renderer/src/hooks/useWorkspace.ts b/packages/renderer/src/hooks/useWorkspace.ts index 485b8aa7..69734376 100644 --- a/packages/renderer/src/hooks/useWorkspace.ts +++ b/packages/renderer/src/hooks/useWorkspace.ts @@ -64,7 +64,7 @@ export const useWorkspace = () => { }, []); const updateSceneJson = useCallback((path: string, updates: Partial) => { - dispatch(workspaceActions.updateSceneJson({ path, updates })); + return dispatch(workspaceActions.updateSceneJson({ path, updates })); }, []); const isLoading = workspace.status === 'loading'; From 632fadc0542d2df214411373bb338f464bdf8be1 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Wed, 6 Nov 2024 12:59:16 -0300 Subject: [PATCH 12/22] fix: upgrade dcl-ui2 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 457ff29f..96440e9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "@vitejs/plugin-react": "^4.3.1", "classnames": "^2.5.1", "cross-env": "7.0.3", - "decentraland-ui2": "file:decentraland-ui2-0.0.0-development.tgz", + "decentraland-ui2": "^0.6.1", "electron": "31.1.0", "electron-builder": "24.13.3", "eslint": "8.57.0", @@ -9454,9 +9454,9 @@ } }, "node_modules/decentraland-ui2": { - "version": "0.0.0-development", - "resolved": "file:decentraland-ui2-0.0.0-development.tgz", - "integrity": "sha512-sA7RwIeeo4SYgtxOrr6EcKg0RahgMV8jnfX0xMl+O+jdFrt+4jKqyuGA1t9YqvY5YkpsftBO4OWpy0v+Xa9blw==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/decentraland-ui2/-/decentraland-ui2-0.6.1.tgz", + "integrity": "sha512-DmQeKsAdgcCw60BWSxGa8R/e3mwyugXkBifd1rNZ24J+AdgKz36wG9nU/MYG2KNpZgftQQw7OcgMAzq6zBfctw==", "dev": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index ca0dc3df..c03ff496 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "@vitejs/plugin-react": "^4.3.1", "classnames": "^2.5.1", "cross-env": "7.0.3", - "decentraland-ui2": "file:decentraland-ui2-0.0.0-development.tgz", + "decentraland-ui2": "^0.6.1", "electron": "31.1.0", "electron-builder": "24.13.3", "eslint": "8.57.0", From 0300f97eb3923a7c5313401c9ab9e0354062e747 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Wed, 6 Nov 2024 15:34:57 -0300 Subject: [PATCH 13/22] feat: handle cli error --- packages/main/src/modules/bin.ts | 71 +++++++++++-------- packages/main/src/modules/cli.ts | 4 +- packages/main/src/modules/handle.ts | 14 +++- packages/preload/src/modules/invoke.ts | 11 ++- .../PublishProject/steps/Deploy/component.tsx | 23 ++++-- .../steps/Deploy/images/warning.svg | 28 ++++++++ .../PublishProject/steps/Deploy/styles.css | 19 ++++- .../src/modules/store/editor/slice.ts | 9 ++- packages/shared/types/ipc.ts | 8 +++ 9 files changed, 145 insertions(+), 42 deletions(-) create mode 100644 packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/warning.svg diff --git a/packages/main/src/modules/bin.ts b/packages/main/src/modules/bin.ts index 89d6ec06..29be0add 100644 --- a/packages/main/src/modules/bin.ts +++ b/packages/main/src/modules/bin.ts @@ -176,27 +176,28 @@ export async function install() { type Error = 'COMMAND_FAILED'; export class StreamError extends ErrorBase { - constructor( - type: Error, - message: string, - public stdout: Buffer, - public stderr: Buffer, - ) { + constructor(type: Error, message: string, public stdout: Buffer, public stderr: Buffer) { super(type, message); } } +export type StreamType = 'all' | 'stdout' | 'stderr'; + export type Child = { pkg: string; bin: string; args: string[]; cwd: string; process: Electron.UtilityProcess; - on: (pattern: RegExp, handler: (data?: string) => void) => number; - once: (pattern: RegExp, handler: (data?: string) => void) => number; + on: (pattern: RegExp, handler: (data?: string) => void, streamType?: StreamType) => number; + once: (pattern: RegExp, handler: (data?: string) => void, streamType?: StreamType) => number; off: (index: number) => void; wait: () => Promise; - waitFor: (resolvePattern: RegExp, rejectPattern?: RegExp) => Promise; + waitFor: ( + resolvePattern: RegExp, + rejectPattern?: RegExp, + opts?: { resolve?: StreamType; reject?: StreamType }, + ) => Promise; kill: () => Promise; alive: () => boolean; }; @@ -205,6 +206,7 @@ type Matcher = { pattern: RegExp; handler: (data: string) => void; enabled: boolean; + streamType: StreamType; }; type RunOptions = { @@ -245,13 +247,13 @@ export function run(pkg: string, bin: string, options: RunOptions = {}): Child { const stdout: Uint8Array[] = []; forked.stdout!.on('data', (data: Buffer) => { - handleData(data, matchers); + handleData(data, matchers, 'stdout'); stdout.push(Uint8Array.from(data)); }); const stderr: Uint8Array[] = []; forked.stderr!.on('data', (data: Buffer) => { - handleData(data, matchers); + handleData(data, matchers, 'stderr'); stderr.push(Uint8Array.from(data)); }); @@ -287,12 +289,14 @@ export function run(pkg: string, bin: string, options: RunOptions = {}): Child { } }); - function handleStream(stream: NodeJS.ReadableStream) { - stream!.on('data', (data: Buffer) => handleData(data, matchers)); + function handleStream(stream: NodeJS.ReadableStream, type: StreamType) { + stream!.on('data', (data: Buffer) => handleData(data, matchers, type)); } - handleStream(forked.stdout!); - handleStream(forked.stderr!); + promise.catch(error => console.log('ERRORR', error)); + + handleStream(forked.stdout!, 'stdout'); + handleStream(forked.stderr!, 'stderr'); const child: Child = { pkg, @@ -300,17 +304,21 @@ export function run(pkg: string, bin: string, options: RunOptions = {}): Child { args, cwd, process: forked, - on: (pattern, handler) => { + on: (pattern, handler, streamType = 'all') => { if (alive) { - return matchers.push({ pattern, handler, enabled: true }) - 1; + return matchers.push({ pattern, handler, enabled: true, streamType }) - 1; } throw new Error('Process has been killed'); }, - once: (pattern, handler) => { - const index = child.on(pattern, data => { - handler(data); - child.off(index); - }); + once: (pattern, handler, streamType) => { + const index = child.on( + pattern, + data => { + handler(data); + child.off(index); + }, + streamType, + ); return index; }, off: index => { @@ -319,11 +327,11 @@ export function run(pkg: string, bin: string, options: RunOptions = {}): Child { } }, wait: () => promise, - waitFor: (resolvePattern, rejectPattern) => + waitFor: (resolvePattern, rejectPattern, opts) => new Promise((resolve, reject) => { - child.once(resolvePattern, data => resolve(data!)); + child.once(resolvePattern, data => resolve(data!), opts?.resolve); if (rejectPattern) { - child.once(rejectPattern, data => reject(new Error(data))); + child.once(rejectPattern, data => reject(new Error(data)), opts?.reject); } }), kill: async () => { @@ -382,14 +390,21 @@ export function run(pkg: string, bin: string, options: RunOptions = {}): Child { return child; } -async function handleData(buffer: Buffer, matchers: Matcher[]) { +async function handleData(buffer: Buffer, matchers: Matcher[], type: StreamType) { const data = buffer.toString('utf8'); log.info(`[UtilityProcess] ${data}`); // pipe data to console - for (const { pattern, handler, enabled } of matchers) { + for (const { pattern, handler, enabled, streamType } of matchers) { if (!enabled) continue; + if (streamType !== 'all' && streamType !== type) continue; pattern.lastIndex = 0; // reset regexp if (pattern.test(data)) { - handler(data); + // remove control characters from data + const text = data.replace( + // eslint-disable-next-line no-control-regex + /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, + '', + ); + handler(text); } } } diff --git a/packages/main/src/modules/cli.ts b/packages/main/src/modules/cli.ts index 9323fd09..a6cf9480 100644 --- a/packages/main/src/modules/cli.ts +++ b/packages/main/src/modules/cli.ts @@ -55,7 +55,9 @@ export async function deploy({ path, target, targetContent }: DeployOptions) { }); // App ready at - await deployServer.waitFor(/listening/i); + await deployServer.waitFor(/listening/i, /error:/i, { reject: 'stderr' }); + + deployServer.waitFor(/close the terminal/gi).then(() => deployServer?.kill()); return port; } diff --git a/packages/main/src/modules/handle.ts b/packages/main/src/modules/handle.ts index 179fd5df..9f55a631 100644 --- a/packages/main/src/modules/handle.ts +++ b/packages/main/src/modules/handle.ts @@ -1,6 +1,6 @@ import { ipcMain } from 'electron'; import log from 'electron-log'; -import type { Ipc } from '/shared/types/ipc'; +import type { Ipc, IpcError, IpcResult } from '/shared/types/ipc'; // wrapper for ipcMain.handle with types export async function handle( @@ -14,11 +14,19 @@ export async function handle( .map((arg, idx) => `args[${idx}]=${JSON.stringify(arg)}`) .join(' ')}`.trim(), ); - const result = await handler(event, ...(args as Parameters)); + const value = await handler(event, ...(args as Parameters)); + const result: IpcResult = { + success: true, + value, + }; return result; } catch (error: any) { log.error(`[IPC] channel=${channel} error=${error.message}`); - throw error; + const result: IpcError = { + success: false, + error: error.message, + }; + return result; } }); } diff --git a/packages/preload/src/modules/invoke.ts b/packages/preload/src/modules/invoke.ts index 5bbda222..c25d2ebe 100644 --- a/packages/preload/src/modules/invoke.ts +++ b/packages/preload/src/modules/invoke.ts @@ -1,10 +1,17 @@ import { ipcRenderer } from 'electron'; -import type { Ipc } from '/shared/types/ipc'; +import type { Ipc, IpcError, IpcResult } from '/shared/types/ipc'; // wrapper for ipcRenderer.invoke with types export async function invoke( channel: T, ...args: Parameters ): Promise> { - return ipcRenderer.invoke(channel, ...args); + const result = await (ipcRenderer.invoke(channel, ...args) as Promise< + IpcResult> | IpcError + >); + if (result.success) { + return result.value; + } else { + throw new Error(result.error); + } } diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx index 91032def..211f4f53 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx @@ -45,7 +45,7 @@ export function Deploy(props: Props) { const { chainId, wallet, avatar } = useAuth(); const [files, setFiles] = useState([]); const [info, setInfo] = useState(null); - const { loadingPublish, publishPort, project } = useEditor(); + const { loadingPublish, publishPort, project, publishError } = useEditor(); const isMounted = useIsMounted(); const [isDeploying, setIsDeploying] = useState(false); const [error, setError] = useState(null); @@ -115,15 +115,30 @@ export function Deploy(props: Props) { return (
- {loadingPublish || !info ? ( + {loadingPublish ? ( - ) : ( + ) : publishError ? ( + <> +
+
+

{publishError}

+
+ + ) : !info ? null : ( <>
diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/warning.svg b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/warning.svg new file mode 100644 index 00000000..1e1dec42 --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/warning.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css index 3b2517a4..261123e4 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css @@ -2,11 +2,13 @@ min-height: 300px; display: flex; flex-direction: column; + justify-content: center; + align-items: stretch; gap: 24px; } .Deploy > .Loader { - top: 110px; + top: -40px; position: relative; } @@ -152,3 +154,18 @@ .Deploy .scene .files .error { color: var(--error); } + +.Deploy .cli-publish-error { + color: var(--dcl-silver); + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 32px; +} + +.Deploy .Warning { + width: 81px; + height: 70px; + background: center no-repeat url(images/warning.svg); +} diff --git a/packages/renderer/src/modules/store/editor/slice.ts b/packages/renderer/src/modules/store/editor/slice.ts index 71a98e87..44264123 100644 --- a/packages/renderer/src/modules/store/editor/slice.ts +++ b/packages/renderer/src/modules/store/editor/slice.ts @@ -22,8 +22,9 @@ export type EditorState = { project?: Project; inspectorPort: number; publishPort: number; - loadingInspector: boolean; loadingPublish: boolean; + publishError: string | null; + loadingInspector: boolean; loadingPreview: boolean; isInstalling: boolean; isInstalled: boolean; @@ -35,8 +36,9 @@ const initialState: EditorState = { version: null, inspectorPort: 0, publishPort: 0, - loadingInspector: false, loadingPublish: false, + publishError: null, + loadingInspector: false, loadingPreview: false, isInstalling: false, isInstalled: false, @@ -84,13 +86,14 @@ export const slice = createSlice({ builder.addCase(publishScene.pending, state => { state.publishPort = 0; state.loadingPublish = true; + state.publishError = null; }); builder.addCase(publishScene.fulfilled, (state, action) => { state.publishPort = action.payload; state.loadingPublish = false; }); builder.addCase(publishScene.rejected, (state, action) => { - state.error = action.error.message ? new Error(action.error.message) : null; + state.publishError = action.error.message || null; state.loadingPublish = false; }); builder.addCase(workspaceActions.createProject.pending, state => { diff --git a/packages/shared/types/ipc.ts b/packages/shared/types/ipc.ts index ce56b8b7..baa90f16 100644 --- a/packages/shared/types/ipc.ts +++ b/packages/shared/types/ipc.ts @@ -3,6 +3,14 @@ import { type OpenDialogOptions } from 'electron'; import type { Outdated } from '/shared/types/npm'; export type DeployOptions = { path: string; target?: string; targetContent?: string }; +export type IpcResult = { + success: true; + value: T; +}; +export type IpcError = { + success: false; + error: string; +}; export interface Ipc { 'electron.getAppHome': () => string; From 2fa0ae9834277d1d519676ce4321b4817d5f3394 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Wed, 6 Nov 2024 16:05:39 -0300 Subject: [PATCH 14/22] feat: handle deploy errors --- .../PublishProject/steps/Deploy/component.tsx | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx index 211f4f53..7190b1ba 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx @@ -49,6 +49,7 @@ export function Deploy(props: Props) { const isMounted = useIsMounted(); const [isDeploying, setIsDeploying] = useState(false); const [error, setError] = useState(null); + const [isSuccessful, setIsSuccessful] = useState(false); const url = useMemo(() => { const port = import.meta.env.VITE_CLI_DEPLOY_PORT || publishPort; @@ -70,7 +71,14 @@ export function Deploy(props: Props) { if (resp.ok) { return; } - const error = (await resp.json()).message as string; + let error = (await resp.json()).message as string; + if (/Response was/.test(error)) { + try { + error = error.split('["')[1].split('"]')[0]; + } catch (e) { + /* */ + } + } throw new Error(error); } if (wallet && info && info.rootCID) { @@ -81,12 +89,11 @@ export function Deploy(props: Props) { .then(() => { if (!isMounted()) return; setIsDeploying(false); - console.log('success'); + setIsSuccessful(true); }) .catch(error => { setIsDeploying(false); setError(error.message); - console.log('error', error); }); } else { setError('Invalid identity or chainId'); @@ -95,7 +102,7 @@ export function Deploy(props: Props) { }, [wallet, info, url, chainId]); useEffect(() => { - if (!url) return; + if (!url || isSuccessful) return; async function fetchFiles() { const resp = await fetch(`${url}/files`); const files = await resp.json(); @@ -106,12 +113,22 @@ export function Deploy(props: Props) { const info = await resp.json(); return info as Info; } - void Promise.all([fetchFiles(), fetchInfo()]).then(([files, info]) => { - if (!isMounted()) return; - setFiles(files); - setInfo(info); - }); - }, [url]); + void Promise.all([fetchFiles(), fetchInfo()]) + .then(([files, info]) => { + if (!isMounted()) return; + setFiles(files); + setInfo(info); + }) + .catch(); + }, [url, isSuccessful]); + + // set publish error + useEffect(() => { + if (publishError) { + setError(publishError); + } + }, [publishError, setError]); + return ( {loadingPublish ? ( - ) : publishError ? ( + ) : error ? ( <>
-

{publishError}

+

{error}

) : !info ? null : ( @@ -209,7 +226,7 @@ export function Deploy(props: Props) {

{error}

-
-
-
{files.length} files
-
- Total Size:{' '} - {getSize(files.reduce((total, file) => total + file.size, 0))} + {!isSuccessful ? ( +
+
+
{files.length} files
+
+ Total Size:{' '} + {getSize(files.reduce((total, file) => total + file.size, 0))} +
-
-
- {files.map(file => ( -
+
+ {files.map(file => (
- {getPath(file.name)} +
+ {getPath(file.name)} +
+
{getSize(file.size)}
-
{getSize(file.size)}
-
- ))} + ))} +
+
+

{error}

+ +
-
-

{error}

- + ) : ( +
+
+ +
Your scene is successfully published
+
+ +
+ {jumpInUrl} + jumpInUrl && misc.copyToClipboard(jumpInUrl)} + /> +
+
+
+
+ +
-
+ )}
)} diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/copy.svg b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/copy.svg new file mode 100644 index 00000000..79c78bb4 --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/copy.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/jump-in.svg b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/jump-in.svg new file mode 100644 index 00000000..f80b5781 --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/jump-in.svg @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/success.svg b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/success.svg new file mode 100644 index 00000000..9f1a2ffe --- /dev/null +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/images/success.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css index 261123e4..971cbc12 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css @@ -144,7 +144,7 @@ margin-left: 12px; } -.Deploy .scene .files .Button { +.Deploy .scene .Button { font-size: 15px; padding: 8px 22px; max-height: 40px; @@ -169,3 +169,79 @@ height: 70px; background: center no-repeat url(images/warning.svg); } + +.Deploy .scene .success { + flex: 1 1 auto; + display: flex; + flex-direction: column; + padding-left: 32px; + border-left: 1px solid var(--dark-gray); +} + +.Deploy .scene .success .content { + flex: 1 1 auto; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: center; + min-height: 320px; + gap: 16px; +} + +.Deploy .scene .success .content .success-icon { + width: 80px; + height: 80px; + background: center no-repeat url(images/success.svg); +} + +.Deploy .scene .success .content .message { + color: var(--dcl-silver); + font-size: 20px; + font-weight: 500; + text-transform: uppercase; +} + +.Deploy .scene .success .content .url { + color: var(--light-gray); + border: 1px solid var(--dark-gray); + font-size: 13px; + font-weight: 600; + line-height: 24px; + letter-spacing: 0.46px; + padding: 4px 10px; + border-radius: 10px; + display: flex; + align-items: center; +} + +.Deploy .scene .success .content .jump-in-url label { + color: var(--dcl-silver); + line-height: 24px; + font-size: 14px; + letter-spacing: 0.17px; + font-weight: 500; + margin-bottom: 2px; + display: block; +} + +.Deploy .scene .success .content .jump-in-url .copy-icon { + width: 18px; + height: 18px; + background: center no-repeat url(images/copy.svg); + margin-left: 8px; + display: inline-block; + cursor: pointer; +} + +.Deploy .scene .success .actions { + flex: none; + display: flex; + justify-content: flex-end; +} + +.Deploy .scene .success .Button .jump-in-icon { + width: 24px; + height: 24px; + background: center no-repeat url(images/jump-in.svg); + margin-left: 8px; +} diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/component.tsx index 6084f13d..29ad2678 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToWorld/component.tsx @@ -1,5 +1,4 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; -import { ChainId } from '@dcl/schemas/dist/dapps/chain-id'; import { Checkbox, CircularProgress as Loader, @@ -23,7 +22,6 @@ import { t } from '/@/modules/store/translation/utils'; import { addBase64ImagePrefix } from '/@/modules/image'; import { ENSProvider } from '/@/modules/store/ens/types'; import { getEnsProvider } from '/@/modules/store/ens/utils'; -import { useAuth } from '/@/hooks/useAuth'; import { useEditor } from '/@/hooks/useEditor'; import { useWorkspace } from '/@/hooks/useWorkspace'; @@ -66,7 +64,6 @@ export function PublishToWorld(props: Props) { } function SelectWorld({ project, onPublish }: { project: Project; onPublish: () => void }) { - const { chainId } = useAuth(); const { updateSceneJson, updateProject } = useWorkspace(); const names = useSelector(state => state.ens.data); const [name, setName] = useState(project.worldConfiguration?.name || ''); @@ -89,13 +86,6 @@ function SelectWorld({ project, onPublish }: { project: Project; onPublish: () = return _names; }, [names, ensProvider]); - const getExplorerUrl = useMemo(() => { - if (chainId === ChainId.ETHEREUM_SEPOLIA) { - return `decentraland://?realm=${DEPLOY_URLS.DEV_WORLDS}/world/${name}&NETWORK=sepolia`; - } - return `decentraland://?realm=${name}`; - }, [name]); - const handleClick = useCallback(() => { onPublish(); }, [project, name]); @@ -216,22 +206,6 @@ function SelectWorld({ project, onPublish }: { project: Project; onPublish: () = - {!!name && ( - - {t('modal.publish_project.worlds.select_world.world_url_description', { - b: (child: JSX.Element) => {child}, - br: () =>
, - world_url: ( - misc.openExternal(getExplorerUrl)} - > - {getExplorerUrl} - - ), - })} -
- )} {hasWorldContent && (
diff --git a/packages/renderer/src/modules/store/translation/locales/en.json b/packages/renderer/src/modules/store/translation/locales/en.json index 3d867ff0..c5a9c3a0 100644 --- a/packages/renderer/src/modules/store/translation/locales/en.json +++ b/packages/renderer/src/modules/store/translation/locales/en.json @@ -112,7 +112,6 @@ "dcl": "NAME", "ens": "ENS Domain" }, - "world_url_description": "The URL to jump in your World will be:

{world_url}", "world_has_content": "The existing content in {world} World will be replaced with the new content you're about to publish.", "confirm_world_replace_content": "I understand that this action is irreversible" }, @@ -286,4 +285,4 @@ "title": "Creator Docs" } } -} +} \ No newline at end of file diff --git a/packages/renderer/src/modules/store/translation/locales/es.json b/packages/renderer/src/modules/store/translation/locales/es.json index 737beeec..d890f78a 100644 --- a/packages/renderer/src/modules/store/translation/locales/es.json +++ b/packages/renderer/src/modules/store/translation/locales/es.json @@ -111,7 +111,6 @@ "dcl": "NOMBRE", "ens": "DOMINIO ENS" }, - "world_url_description": "La URL para acceder a tu mundo será:

{world_url}", "world_has_content": "El contenido existente en el mundo {world} será reemplazado por el nuevo contenido que estás a punto de publicar.", "confirm_world_replace_content": "Entiendo que esta acción es irreversible." }, @@ -284,4 +283,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/renderer/src/modules/store/translation/locales/zh.json b/packages/renderer/src/modules/store/translation/locales/zh.json index 5245cc41..a976d984 100644 --- a/packages/renderer/src/modules/store/translation/locales/zh.json +++ b/packages/renderer/src/modules/store/translation/locales/zh.json @@ -111,7 +111,6 @@ "dcl": "姓名", "ens": "ENS 域" }, - "world_url_description": "在您的世界中跳躍的 URL 為:

{world_url}", "world_has_content": "{world} World 中的現有內容將替換為您要發布的新內容。", "confirm_world_replace_content": "我了解此操作不可逆轉" }, @@ -284,4 +283,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/shared/types/ipc.ts b/packages/shared/types/ipc.ts index baa90f16..9adde080 100644 --- a/packages/shared/types/ipc.ts +++ b/packages/shared/types/ipc.ts @@ -18,6 +18,7 @@ export interface Ipc { 'electron.getWorkspaceConfigPath': (path: string) => Promise; 'electron.showOpenDialog': (opts: Partial) => Promise; 'electron.openExternal': (url: string) => Promise; + 'electron.copyToClipboard': (text: string) => Promise; 'inspector.start': () => Promise; 'bin.install': () => Promise; 'bin.code': (path: string) => Promise; From 12575bf88508eb8a6de21764257ef3b2bbdb0f93 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Fri, 8 Nov 2024 14:48:50 -0300 Subject: [PATCH 16/22] feat: warning --- .../renderer/src/components/Button/styles.css | 12 +++++ .../PublishProject/steps/Deploy/component.tsx | 52 ++++++++++++++++++- .../PublishProject/steps/Deploy/styles.css | 35 +++++++++++++ 3 files changed, 97 insertions(+), 2 deletions(-) diff --git a/packages/renderer/src/components/Button/styles.css b/packages/renderer/src/components/Button/styles.css index 531edc3d..5862e56b 100644 --- a/packages/renderer/src/components/Button/styles.css +++ b/packages/renderer/src/components/Button/styles.css @@ -45,3 +45,15 @@ .Button.MuiButton-colorInfo:hover { background-color: var(--light-gray); } + +.Button.MuiButton-outlined { + color: var(--dcl) !important; + background-color: transparent; + border-color: var(--dcl); +} + +.Button.MuiButton-outlined:hover { + color: var(--dcl) !important; + background-color: transparent; + border-color: var(--dcl-dark); +} diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx index bfff4687..65072ec0 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx @@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { type AuthChain, Authenticator } from '@dcl/crypto'; import { localStorageGetIdentity } from '@dcl/single-sign-on-client'; import { ChainId } from '@dcl/schemas'; -import { Typography } from 'decentraland-ui2'; +import { Typography, Checkbox } from 'decentraland-ui2'; import { misc } from '#preload'; import { Loader } from '/@/components/Loader'; import { useEditor } from '/@/hooks/useEditor'; @@ -51,6 +51,8 @@ export function Deploy(props: Props) { const [isDeploying, setIsDeploying] = useState(false); const [error, setError] = useState(null); const [isSuccessful, setIsSuccessful] = useState(false); + const [showWarning, setShowWarning] = useState(false); + const [dontShowAgain, setDontShowAgain] = useState(false); const url = useMemo(() => { const port = import.meta.env.VITE_CLI_DEPLOY_PORT || publishPort; @@ -59,6 +61,7 @@ export function Deploy(props: Props) { const handlePublish = useCallback(() => { if (!url) return; + setShowWarning(false); async function deploy(payload: { address: string; authChain: AuthChain; chainId: ChainId }) { setIsDeploying(true); setError(null); @@ -173,6 +176,51 @@ export function Deploy(props: Props) { {...props} >
+ {showWarning ? ( +
+
+
+
+ PLEASE READ CAREFULLY: +
+
    +
  • + After deployment, your scene will undergo processing before becoming available. +
  • +
  • This process may take 15 minutes on average.
  • +
  • + During this time, your scene will appear empty until it has been updated on the + client. +
  • +
+
+
+
+ + + + + +
+
+ ) : null} {loadingPublish ? ( ) : error ? ( @@ -255,7 +303,7 @@ export function Deploy(props: Props) { @@ -303,7 +324,7 @@ export function Deploy(props: Props) {
@@ -255,7 +248,9 @@ export function Deploy(props: Props) { <>
- {chainId === ChainId.ETHEREUM_MAINNET ? 'Mainnet' : 'Testnet'} + {chainId === ChainId.ETHEREUM_MAINNET + ? t('modal.publish_project.deploy.ethereum.mainnet') + : t('modal.publish_project.deploy.ethereum.testnet')}
{wallet ? (
@@ -297,10 +292,14 @@ export function Deploy(props: Props) { {!isSuccessful ? (
-
{files.length} files
+
+ {t('modal.publish_project.deploy.files.count', { count: files.length })} +
- Total Size:{' '} - {getSize(files.reduce((total, file) => total + file.size, 0))} + {t('modal.publish_project.deploy.files.size', { + size: getSize(files.reduce((total, file) => total + file.size, 0)), + b: (child: string) => {child}, + })}
@@ -326,7 +325,7 @@ export function Deploy(props: Props) { disabled={isDeploying || isSuccessful} onClick={() => (skipWarning ? handlePublish() : setShowWarning(true))} > - Publish + {t('modal.publish_project.deploy.files.publish')} {isDeploying ? : }
@@ -335,9 +334,17 @@ export function Deploy(props: Props) {
-
Your scene is successfully published
+
+ {t('modal.publish_project.deploy.success.message')} +
- +
{jumpInUrl} - Jump In + {t('modal.publish_project.deploy.success.jump_in')}
diff --git a/packages/renderer/src/modules/store/translation/locales/en.json b/packages/renderer/src/modules/store/translation/locales/en.json index c5a9c3a0..80802afa 100644 --- a/packages/renderer/src/modules/store/translation/locales/en.json +++ b/packages/renderer/src/modules/store/translation/locales/en.json @@ -149,6 +149,30 @@ "errors": { "url": "Invalid URL" } + }, + "deploy": { + "warning": { + "message": "PLEASE READ CAREFULLY:
  • After deployment, your scene will undergo processing before becoming available.
  • This process may take 15 minutes on average.
  • During this time, your scene will appear empty until it has been updated on the client.
", + "checkbox": "Don't show again", + "continue": "Continue", + "back": "Go back" + }, + "ethereum": { + "mainnet": "Mainnet", + "testnet": "Testnet" + }, + "files": { + "count": "{count} {count, plural, one {file} other {files}}", + "size": "Total Size {size}", + "publish": "Publish" + }, + "success": { + "message": "You scene is successfully published", + "url": "The URL to jump in your {target} is:", + "world": "World", + "land": "Land", + "jump_in": "Jump In" + } } }, "app_settings": { diff --git a/packages/renderer/src/modules/store/translation/locales/es.json b/packages/renderer/src/modules/store/translation/locales/es.json index d890f78a..df0668d7 100644 --- a/packages/renderer/src/modules/store/translation/locales/es.json +++ b/packages/renderer/src/modules/store/translation/locales/es.json @@ -147,6 +147,30 @@ "errors": { "url": "URL inválida" } + }, + "deploy": { + "warning": { + "message": "POR FAVOR LEA CUIDADOSAMENTE:
  • Después de la implementación, tu escena pasará por un proceso antes de estar disponible.
  • Este proceso puede tardar en promedio 15 minutos.
  • Durante este tiempo, tu escena aparecerá vacía hasta que se haya actualizado en el cliente.
", + "checkbox": "No mostrar de nuevo", + "continue": "Continuar", + "back": "Regresar" + }, + "ethereum": { + "mainnet": "Mainnet", + "testnet": "Testnet" + }, + "files": { + "count": "{count} {count, plural, one {archivo} other {archivos}}", + "size": "Tamaño Total {size}", + "publish": "Publicar" + }, + "success": { + "message": "Tu escena se ha publicado con éxito", + "url": "La URL para acceder a tu {target} es:", + "world": "Mundo", + "land": "Tierra", + "jump_in": "Entrar" + } } }, "app_settings": { diff --git a/packages/renderer/src/modules/store/translation/locales/zh.json b/packages/renderer/src/modules/store/translation/locales/zh.json index a976d984..5b36ccd9 100644 --- a/packages/renderer/src/modules/store/translation/locales/zh.json +++ b/packages/renderer/src/modules/store/translation/locales/zh.json @@ -147,6 +147,30 @@ "errors": { "url": "无效的网址" } + }, + "deploy": { + "warning": { + "message": "请仔细阅读:
  • 部署后,您的场景将进行处理,然后才能使用。
  • 此过程平均可能需要15分钟。
  • 在此期间,您的场景将显示为空,直到客户端更新。
", + "checkbox": "不再显示", + "continue": "继续", + "back": "返回" + }, + "ethereum": { + "mainnet": "主网", + "testnet": "测试网" + }, + "files": { + "count": "{count} {count, plural, one {文件} other {文件}}", + "size": "总大小 {size}", + "publish": "发布" + }, + "success": { + "message": "您的场景已成功发布", + "url": "跳转到您的 {target} 的URL是:", + "world": "世界", + "land": "土地", + "jump_in": "跳转" + } } }, "app_settings": { From 7240c229cec810cb7e56307bbaf37cf04b22748b Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Fri, 8 Nov 2024 17:17:49 -0300 Subject: [PATCH 19/22] chore: lint --- packages/main/src/modules/bin.ts | 7 ++++++- .../Modals/PublishProject/steps/Deploy/component.tsx | 8 ++++---- .../src/modules/store/translation/locales/en.json | 2 +- .../src/modules/store/translation/locales/es.json | 2 +- .../src/modules/store/translation/locales/zh.json | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/main/src/modules/bin.ts b/packages/main/src/modules/bin.ts index 1abbc6a0..158ef706 100644 --- a/packages/main/src/modules/bin.ts +++ b/packages/main/src/modules/bin.ts @@ -176,7 +176,12 @@ export async function install() { type Error = 'COMMAND_FAILED'; export class StreamError extends ErrorBase { - constructor(type: Error, message: string, public stdout: Buffer, public stderr: Buffer) { + constructor( + type: Error, + message: string, + public stdout: Buffer, + public stderr: Buffer, + ) { super(type, message); } } diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx index c55f3644..984e5d49 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx @@ -189,10 +189,10 @@ export function Deploy(props: Props) { ? 'Publish to your World' : 'Publish to your Land' : loadingPublish - ? 'Loading...' - : error - ? 'Error' - : '' + ? 'Loading...' + : error + ? 'Error' + : '' } size="large" {...props} diff --git a/packages/renderer/src/modules/store/translation/locales/en.json b/packages/renderer/src/modules/store/translation/locales/en.json index 80802afa..268fd6fb 100644 --- a/packages/renderer/src/modules/store/translation/locales/en.json +++ b/packages/renderer/src/modules/store/translation/locales/en.json @@ -309,4 +309,4 @@ "title": "Creator Docs" } } -} \ No newline at end of file +} diff --git a/packages/renderer/src/modules/store/translation/locales/es.json b/packages/renderer/src/modules/store/translation/locales/es.json index df0668d7..d97b50d0 100644 --- a/packages/renderer/src/modules/store/translation/locales/es.json +++ b/packages/renderer/src/modules/store/translation/locales/es.json @@ -307,4 +307,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/renderer/src/modules/store/translation/locales/zh.json b/packages/renderer/src/modules/store/translation/locales/zh.json index 5b36ccd9..fc3bcb80 100644 --- a/packages/renderer/src/modules/store/translation/locales/zh.json +++ b/packages/renderer/src/modules/store/translation/locales/zh.json @@ -307,4 +307,4 @@ } } } -} \ No newline at end of file +} From 9ed2bf90ab186870d12dbe96180c4e274cb52695 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Mon, 11 Nov 2024 11:17:00 -0300 Subject: [PATCH 20/22] chore: useMemo instead of useState --- .../Modals/PublishProject/component.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/renderer/src/components/Modals/PublishProject/component.tsx b/packages/renderer/src/components/Modals/PublishProject/component.tsx index 9c3ba121..7eed760e 100644 --- a/packages/renderer/src/components/Modals/PublishProject/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/component.tsx @@ -1,4 +1,4 @@ -import { useCallback, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { Initial } from './steps/Initial'; import { AlternativeServers } from './steps/AlternativeServers'; import { PublishToWorld } from './steps/PublishToWorld'; @@ -8,27 +8,26 @@ import { Deploy } from './steps/Deploy'; import type { Props, Step } from './types'; export function PublishProject({ open, project, onClose }: Omit) { - const [step, setStep] = useState('initial'); const [history, setHistory] = useState([]); + const step = useMemo( + () => (history.length > 0 ? history[history.length - 1] : 'initial'), + [history], + ); const handleClose = useCallback(() => { - setStep('initial'); setHistory([]); onClose(); - }, [setStep, setHistory, onClose]); + }, [setHistory, onClose]); const handleBack = useCallback(() => { - const prev = history.pop(); - setStep(prev || 'initial'); setHistory(history => (history.length > 0 ? history.slice(0, -1) : [])); - }, [history, setStep, setHistory]); + }, [history, setHistory]); const handleStep = useCallback( (newStep: Step) => { - setStep(newStep); - setHistory(history => [...history, step]); + setHistory(history => [...history, newStep]); }, - [step, setStep, setHistory], + [setHistory], ); const props: Props = { From bb4bb47ecbef5552c99e3e7a7e51c575ffe5f856 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Mon, 11 Nov 2024 11:19:51 -0300 Subject: [PATCH 21/22] chore: remove unnecessary duplicated handler --- packages/main/src/modules/bin.ts | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/main/src/modules/bin.ts b/packages/main/src/modules/bin.ts index 158ef706..6c2d974b 100644 --- a/packages/main/src/modules/bin.ts +++ b/packages/main/src/modules/bin.ts @@ -176,12 +176,7 @@ export async function install() { type Error = 'COMMAND_FAILED'; export class StreamError extends ErrorBase { - constructor( - type: Error, - message: string, - public stdout: Buffer, - public stderr: Buffer, - ) { + constructor(type: Error, message: string, public stdout: Buffer, public stderr: Buffer) { super(type, message); } } @@ -294,13 +289,6 @@ export function run(pkg: string, bin: string, options: RunOptions = {}): Child { } }); - function handleStream(stream: NodeJS.ReadableStream, type: StreamType) { - stream!.on('data', (data: Buffer) => handleData(data, matchers, type)); - } - - handleStream(forked.stdout!, 'stdout'); - handleStream(forked.stderr!, 'stderr'); - const child: Child = { pkg, bin, From 7ecddcc64ffc3c58fc9c569ed4e50de42d3631d2 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Mon, 11 Nov 2024 11:21:35 -0300 Subject: [PATCH 22/22] chore: linted --- packages/main/src/modules/bin.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/main/src/modules/bin.ts b/packages/main/src/modules/bin.ts index 6c2d974b..eb154cce 100644 --- a/packages/main/src/modules/bin.ts +++ b/packages/main/src/modules/bin.ts @@ -176,7 +176,12 @@ export async function install() { type Error = 'COMMAND_FAILED'; export class StreamError extends ErrorBase { - constructor(type: Error, message: string, public stdout: Buffer, public stderr: Buffer) { + constructor( + type: Error, + message: string, + public stdout: Buffer, + public stderr: Buffer, + ) { super(type, message); } }