diff --git a/packages/main/src/modules/handle.ts b/packages/main/src/modules/handle.ts index bdae4010..9f55a631 100644 --- a/packages/main/src/modules/handle.ts +++ b/packages/main/src/modules/handle.ts @@ -10,7 +10,9 @@ export async function handle( ipcMain.handle(channel, async (event, ...args) => { try { log.info( - `[IPC] channel=${channel} ${args.map((arg, idx) => `args[${idx}]=${JSON.stringify(arg)}`).join(' ')}`.trim(), + `[IPC] channel=${channel} ${args + .map((arg, idx) => `args[${idx}]=${JSON.stringify(arg)}`) + .join(' ')}`.trim(), ); const value = await handler(event, ...(args as Parameters)); const result: IpcResult = { diff --git a/packages/renderer/src/components/AuthProvider/component.tsx b/packages/renderer/src/components/AuthProvider/component.tsx index c7a3e6c8..b597496b 100644 --- a/packages/renderer/src/components/AuthProvider/component.tsx +++ b/packages/renderer/src/components/AuthProvider/component.tsx @@ -26,7 +26,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => const [wallet, setWallet] = useState(); const [avatar, setAvatar] = useState(); const [isSignedIn, setIsSignedIn] = useState(false); - const [chainId, setChainId] = useState(ChainId.ETHEREUM_SEPOLIA); + const [chainId, setChainId] = useState(ChainId.ETHEREUM_MAINNET); const isSigningIn = !!initSignInResultRef?.current; diff --git a/packages/renderer/src/components/Button/styles.css b/packages/renderer/src/components/Button/styles.css index 5d944558..5862e56b 100644 --- a/packages/renderer/src/components/Button/styles.css +++ b/packages/renderer/src/components/Button/styles.css @@ -47,5 +47,13 @@ } .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/EditorPage/component.tsx b/packages/renderer/src/components/EditorPage/component.tsx index 2a770cd7..a28cec57 100644 --- a/packages/renderer/src/components/EditorPage/component.tsx +++ b/packages/renderer/src/components/EditorPage/component.tsx @@ -107,7 +107,9 @@ export function EditorPage() { if (import.meta.env.VITE_ASSET_PACKS_JS_PORT && import.meta.env.VITE_ASSET_PACKS_JS_PATH) { // this is for local development of the asset-packs repo const b64 = btoa(import.meta.env.VITE_ASSET_PACKS_JS_PATH); - binIndexJsUrl = `http://localhost:${import.meta.env.VITE_ASSET_PACKS_JS_PORT}/content/contents/b64-${b64}`; + binIndexJsUrl = `http://localhost:${ + import.meta.env.VITE_ASSET_PACKS_JS_PORT + }/content/contents/b64-${b64}`; } // this is the asset-packs javascript file 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 79bac8db..e76ac0fd 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/component.tsx @@ -3,52 +3,27 @@ import { type AuthChain, Authenticator } from '@dcl/crypto'; import { localStorageGetIdentity } from '@dcl/single-sign-on-client'; import { ChainId } from '@dcl/schemas'; import { Typography, Checkbox } from 'decentraland-ui2'; -import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; - import { misc, workspace } from '#preload'; - import type { IFileSystemStorage } from '/shared/types/storage'; - -import { useAuth } from '/@/hooks/useAuth'; -import { useEditor } from '/@/hooks/useEditor'; -import { useIsMounted } from '/@/hooks/useIsMounted'; - import { t } from '/@/modules/store/translation/utils'; import { Loader } from '/@/components/Loader'; +import { useEditor } from '/@/hooks/useEditor'; +import { useIsMounted } from '/@/hooks/useIsMounted'; +import { useAuth } from '/@/hooks/useAuth'; import { addBase64ImagePrefix } from '/@/modules/image'; - import { PublishModal, onBackNoop } from '../../PublishModal'; -import { ConnectedSteps } from '../../../../Step'; import { Button } from '../../../../Button'; - -import type { Step } from '../../../../Step/types'; import { type Props } from '../../types'; - -import { - getInitialDeploymentStatus, - retryDelayInMs, - maxRetries, - checkDeploymentStatus, - fetchDeploymentStatus, - deriveOverallStatus, - cleanPendingsFromDeploymentStatus, - isDeployFinishing, -} from './utils'; -import { - type File, - type Info, - type DeploymentStatus, - type Status, - isDeploymentError, -} from './types'; - +import type { File, Info } from './types'; import './styles.css'; const MAX_FILE_PATH_LENGTH = 50; 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.slice(0, MAX_FILE_PATH_LENGTH / 2)}...${filename.slice( + -(MAX_FILE_PATH_LENGTH / 2), + )}` : filename; } @@ -76,8 +51,9 @@ export function Deploy(props: Props) { const [info, setInfo] = useState(null); const { loadingPublish, publishPort, project, publishError } = useEditor(); const isMounted = useIsMounted(); - const [deployStatus, setDeployStatus] = useState('idle'); + const [isDeploying, setIsDeploying] = useState(false); const [error, setError] = useState(null); + const [isSuccessful, setIsSuccessful] = useState(false); const [showWarning, setShowWarning] = useState(false); const [skipWarning, setSkipWarning] = useState(false); @@ -102,7 +78,7 @@ export function Deploy(props: Props) { if (!url) return; setShowWarning(false); async function deploy(payload: { address: string; authChain: AuthChain; chainId: ChainId }) { - setDeployStatus('pending'); + setIsDeploying(true); setError(null); const resp = await fetch(`${url}/deploy`, { method: 'post', @@ -128,12 +104,14 @@ export function Deploy(props: Props) { const identity = localStorageGetIdentity(wallet); if (identity && chainId) { const authChain = Authenticator.signPayload(identity, info.rootCID); - void deploy({ address: wallet, authChain, chainId: ChainId.ETHEREUM_SEPOLIA }) + void deploy({ address: wallet, authChain, chainId }) .then(() => { if (!isMounted()) return; + setIsDeploying(false); + setIsSuccessful(true); }) .catch(error => { - setDeployStatus('failed'); + setIsDeploying(false); setError(error.message); }); } else { @@ -150,16 +128,16 @@ export function Deploy(props: Props) { }, []); useEffect(() => { - if (!url || deployStatus !== 'idle') return; + if (!url || isSuccessful) return; async function fetchFiles() { const resp = await fetch(`${url}/files`); - const files = (await resp.json()) as File[]; - return files; + const files = await resp.json(); + return files as File[]; } async function fetchInfo() { const resp = await fetch(`${url}/info`); - const info = (await resp.json()) as Info; - return info; + const info = await resp.json(); + return info as Info; } void Promise.all([fetchFiles(), fetchInfo()]) .then(([files, info]) => { @@ -168,12 +146,11 @@ export function Deploy(props: Props) { setInfo(info); }) .catch(); - }, [url, deployStatus]); + }, [url, isSuccessful]); // set publish error useEffect(() => { if (publishError) { - // TODO: JSON.parse(publishError) if possible setError(publishError); } }, [publishError, setError]); @@ -198,14 +175,6 @@ export function Deploy(props: Props) { } }, [jumpInUrl]); - const handleDeploySuccess = useCallback(() => { - setDeployStatus('complete'); - }, []); - - const handleDeployRetry = useCallback(() => { - props.onBack && props.onBack(); - }, []); - return (
{showWarning ? ( @@ -245,7 +214,6 @@ export function Deploy(props: Props) {
- {deployStatus === 'idle' && ( - (skipWarning ? handlePublish() : setShowWarning(true))} - /> - )} - {deployStatus === 'pending' && ( - - )} - {deployStatus === 'complete' && ( - + {!isSuccessful ? ( +
+
+
+ {t('modal.publish_project.deploy.files.count', { count: files.length })} +
+
+ {t('modal.publish_project.deploy.files.size', { + size: getSize(files.reduce((total, file) => total + file.size, 0)), + b: (child: string) => {child}, + })} +
+
+
+ {files.map(file => ( +
+
+ {getPath(file.name)} +
+
{getSize(file.size)}
+
+ ))} +
+
+

{error}

+ +
+
+ ) : ( +
+
+ +
+ {t('modal.publish_project.deploy.success.message')} +
+
+ +
+ {jumpInUrl} + jumpInUrl && misc.copyToClipboard(jumpInUrl)} + /> +
+
+
+
+ +
+
)} @@ -346,286 +368,3 @@ export function Deploy(props: Props) {
); } - -type IdleProps = { - files: File[]; - error: string | null; - onClick: () => void; -}; - -function Idle({ files, error, onClick }: IdleProps) { - return ( -
-
-
- {t('modal.publish_project.deploy.files.count', { count: files.length })} -
-
- {t('modal.publish_project.deploy.files.size', { - size: getSize(files.reduce((total, file) => total + file.size, 0)), - b: (child: string) => {child}, - })} -
-
-
- {files.map(file => ( -
-
- {getPath(file.name)} -
-
{getSize(file.size)}
-
- ))} -
-
-

{error}

- -
-
- ); -} - -type DeployingProps = { - info: Info; - url: string | null; - onSuccess: () => void; - onClick: () => void; - onRetry: () => void; -}; - -function Deploying({ info, url, onSuccess, onClick, onRetry }: DeployingProps) { - const { wallet } = useAuth(); - const [deployState, setDeployState] = useState(getInitialDeploymentStatus()); - - const getDeploymentStatus = useCallback((): Promise => { - if (!wallet) throw new Error('No wallet provided'); - const identity = localStorageGetIdentity(wallet); - if (!identity) throw new Error(`No identity found for wallet ${wallet}`); - return fetchDeploymentStatus(info.rootCID, identity); - }, [wallet, info]); - - const onReportIssue = useCallback(() => { - void misc.openExternal('https://decentraland.canny.io'); - }, []); - - useEffect( - () => { - let isCancelled = false; - - const handleUpdate = (status: DeploymentStatus) => { - if (!isCancelled) { - if (deriveOverallStatus(status) === 'failed') { - isCancelled = true; - setDeployState(cleanPendingsFromDeploymentStatus(status)); - } else { - setDeployState(status); - } - } - }; - - const handleSuccess = () => { - if (!isCancelled) onSuccess(); - }; - - const handleFailure = (error: any) => { - if (!isCancelled) { - // info: if we know the error, we can translate it - if (isDeploymentError(error, 'MAX_RETRIES')) { - setDeployState(cleanPendingsFromDeploymentStatus(error.status)); - } - } - }; - - const shouldAbort = () => isCancelled; - - checkDeploymentStatus( - maxRetries, - retryDelayInMs, - getDeploymentStatus, - handleUpdate, - shouldAbort, - deployState, - ) - .then(handleSuccess) - .catch(handleFailure); - - // cleanup function to cancel retries if the component unmounts - return () => { - isCancelled = true; - }; - }, - [] /* no deps, want this to run ONLY on mount */, - ); - - const getStepDescription = useCallback((status: Status) => { - switch (status) { - case 'pending': - return t('modal.publish_project.deploy.deploying.step.loading'); - case 'failed': - return t('modal.publish_project.deploy.deploying.step.failed'); - default: - return undefined; - } - }, []); - - const steps: Step[] = useMemo(() => { - const { catalyst, assetBundle, lods } = deployState; - return [ - { - bulletText: '1', - name: t('modal.publish_project.deploy.deploying.step.catalyst'), - description: getStepDescription(catalyst), - state: catalyst, - }, - { - bulletText: '2', - name: t('modal.publish_project.deploy.deploying.step.asset_bundle'), - description: getStepDescription(assetBundle), - state: assetBundle, - }, - { - bulletText: '3', - name: t('modal.publish_project.deploy.deploying.step.lods'), - description: getStepDescription(lods), - state: lods, - }, - ]; - }, [deployState, getStepDescription]); - - const isFinishing = useMemo(() => isDeployFinishing(deployState), [deployState]); - const overallStatus = useMemo(() => deriveOverallStatus(deployState), [deployState]); - const title = useMemo(() => { - if (overallStatus === 'failed') return t('modal.publish_project.deploy.deploying.failed'); - if (isFinishing) return t('modal.publish_project.deploy.deploying.finishing'); - return t('modal.publish_project.deploy.deploying.publish'); - }, [overallStatus, isFinishing]); - - return ( -
-
-
- {overallStatus === 'failed' ?
: } - {title} -
- {overallStatus === 'failed' && ( - {t('modal.publish_project.deploy.deploying.try_again')} - )} -
- - {overallStatus === 'failed' ? ( -
- - -
- ) : isFinishing ? ( - <> -
- -
-
- -
- - ) : ( -
- - {t('modal.publish_project.deploy.deploying.info')} -
- )} -
- ); -} - -type SuccessProps = { - info: Info; - url: string | null; - onClick: () => void; -}; - -function Success({ info, url, onClick }: SuccessProps) { - return ( -
-
- -
{t('modal.publish_project.deploy.success.message')}
- -
-
- -
-
- ); -} - -function JumpUrl({ - inProgress, - info, - url, -}: { - inProgress?: boolean; - info: Info; - url: string | null; -}) { - return ( -
- {inProgress && } - -
- {url} - url && misc.copyToClipboard(url)} - /> -
-
- ); -} 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 144d8265..84fcb8cd 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/styles.css @@ -171,7 +171,7 @@ background: center no-repeat url(images/warning.svg); } -.Deploy .scene .Success { +.Deploy .scene .success { flex: 1 1 auto; display: flex; flex-direction: column; @@ -179,7 +179,7 @@ border-left: 1px solid var(--dark-gray); } -.Deploy .scene .Success .content { +.Deploy .scene .success .content { flex: 1 1 auto; display: flex; flex-direction: column; @@ -190,24 +190,20 @@ padding-bottom: 80px; } -.Deploy .scene .Success .content .success-icon { +.Deploy .scene .success .content .success-icon { width: 80px; height: 80px; background: center no-repeat url(images/success.svg); } -.Deploy .scene .Success .content .message { +.Deploy .scene .success .content .message { color: var(--dcl-silver); font-size: 20px; font-weight: 500; text-transform: uppercase; } -.Deploy .scene .jump-in-url { - margin-bottom: 20px; -} - -.Deploy .scene .jump-in-url .url { +.Deploy .scene .success .content .url { color: var(--light-gray); border: 1px solid var(--dark-gray); font-size: 13px; @@ -218,63 +214,11 @@ border-radius: 10px; display: flex; align-items: center; - width: fit-content; - margin-top: 5px; -} - -.Deploy .scene .Deploying { - display: flex; - flex-direction: column; - width: 100%; - padding: 10px; } -.Deploy .scene .Deploying .header { +.Deploy .scene .success .content .jump-in-url label { color: var(--dcl-silver); - display: flex; - justify-content: flex-start; - margin-bottom: 20px; - flex-direction: column; -} - -.Deploy .scene .Deploying .header .title { - display: flex; - align-items: center; - text-transform: uppercase; -} - -.Deploy .scene .Deploying .header .Loader { - width: inherit; - margin-right: 12px; -} - -.Deploy .scene .Deploying .header .Warning { - background-size: contain; - width: 40px; - margin-right: 12px; -} - -.Deploy .scene .Deploying .ConnectedSteps { - margin-bottom: 30px; -} - -.Deploy .scene .Deploying .info { - display: flex; - flex-direction: row; - background-color: #1764C03D; - border-radius: 8px; - padding: 20px; - width: 450px; - color: var(--dcl-silver); -} - -.Deploy .scene .Deploying .info svg { - color: var(--twitter); -} - -.Deploy .scene .jump-in-url label { - color: var(--dcl-silver); - line-height: 20px; + line-height: 24px; font-size: 14px; letter-spacing: 0.17px; font-weight: 500; @@ -282,7 +226,7 @@ display: block; } -.Deploy .scene .jump-in-url .copy-icon { +.Deploy .scene .success .content .jump-in-url .copy-icon { width: 18px; height: 18px; background: center no-repeat url(images/copy.svg); @@ -291,13 +235,13 @@ cursor: pointer; } -.Deploy .scene .actions { +.Deploy .scene .success .actions { flex: none; display: flex; justify-content: flex-end; } -.Deploy .scene .Button .jump-in-icon { +.Deploy .scene .success .Button .jump-in-icon { width: 24px; height: 24px; background: center no-repeat url(images/jump-in.svg); diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/types.ts b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/types.ts index 410c24f5..ec6eb7b7 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/types.ts +++ b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/types.ts @@ -1,5 +1,3 @@ -import { ErrorBase } from '/shared/types/error'; - export type File = { name: string; size: number; @@ -16,43 +14,3 @@ export type Info = { skipValidations: boolean; title: string; }; - -export const STATUS_VALUES = ['idle', 'pending', 'complete', 'failed'] as const; - -export type Status = (typeof STATUS_VALUES)[number]; - -export type DeploymentStatus = { - catalyst: Status; - assetBundle: Status; - lods: Status; -}; - -export type AssetBundleRegistryResponse = { - complete: boolean; - catalyst: string; - assetBundles: { - mac: string; - windows: string; - }; - lods: { - mac: string; - windows: string; - }; -}; - -export type Error = 'MAX_RETRIES' | 'FETCH'; - -export class DeploymentError extends ErrorBase { - constructor( - public name: Error, - public message: string = '', - public status: DeploymentStatus, - public cause?: any, - ) { - super(name, message, cause); - this.status = status; - } -} - -export const isDeploymentError = (error: unknown, type: Error): error is DeploymentError => - error instanceof DeploymentError && error.name === type; diff --git a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/utils.ts b/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/utils.ts deleted file mode 100644 index 64a7e6d3..00000000 --- a/packages/renderer/src/components/Modals/PublishProject/steps/Deploy/utils.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { minutes, seconds } from '/shared/time'; -import equal from 'fast-deep-equal'; -import type { AuthIdentity } from 'decentraland-crypto-fetch'; -import { type AuthChain, Authenticator } from '@dcl/crypto'; - -import { delay } from '/shared/utils'; - -import { - type AssetBundleRegistryResponse, - type Status, - STATUS_VALUES, - type DeploymentStatus, - DeploymentError, -} from './types'; - -export const getInitialDeploymentStatus = (): DeploymentStatus => ({ - catalyst: 'idle', - assetBundle: 'idle', - lods: 'idle', -}); - -export const retryDelayInMs = seconds(10); -// Estimated time as of 5/12/2024 for a full deployment is around 60 minutes. -export const maxRetries = minutes(60) / retryDelayInMs; // Total number of retries calculated based on a X-minute retry window - -export const AUTH_CHAIN_HEADER_PREFIX = 'x-identity-auth-chain-'; -export const AUTH_TIMESTAMP_HEADER = 'x-identity-timestamp'; -export const AUTH_METADATA_HEADER = 'x-identity-metadata'; - -export function getAuthHeaders( - method: string, - path: string, - chainProvider: (payload: string) => AuthChain, -): Record { - const timestamp = Date.now().toString(); - const metadata = JSON.stringify({}); // needed for the fetch to work... - const payloadToSign = `${method.toLowerCase()}:${path.toLowerCase()}:${timestamp}:${metadata}`; - - const chain = chainProvider(payloadToSign); - const headers = chain.reduce>((acc, link, index) => { - acc[`${AUTH_CHAIN_HEADER_PREFIX}${index}`] = JSON.stringify(link); - return acc; - }, {}); - - return { - ...headers, - [AUTH_TIMESTAMP_HEADER]: timestamp, - [AUTH_METADATA_HEADER]: metadata, - }; -} - -/** - * Validates a status string against known values. - * - * @param status - The status string to validate. - * @returns A valid `Status` or `failed` if invalid. - */ -export function validateStatus(status: string): Status { - return STATUS_VALUES.includes(status as Status) ? (status as Status) : 'failed'; -} - -/** - * Derives an overall deployment status from different statuses. - * - * @param statuses - The deployment statuses. - * @returns An overall `Status`. - */ -export function deriveOverallStatus(statuses: Record): Status { - const _statuses: Set = new Set(Object.values(statuses) as Status[]); - if (_statuses.has('failed')) return 'failed'; - if (_statuses.has('pending')) return 'pending'; - if (_statuses.has('complete')) return 'complete'; - return 'idle'; -} - -/** - * Cleans up a `DeploymentStatus` object by resetting any 'pending' statuses to 'idle'. - * - * This function ensures that any deployment step stuck in a 'pending' state is treated - * as 'idle' to indicate that it hasn't started or needs to be retried. - * - * @param status - The `DeploymentStatus` object containing the current statuses of deployment steps. - * @returns A new `DeploymentStatus` object where all 'pending' statuses are replaced with 'idle'. - */ -export function cleanPendingsFromDeploymentStatus(status: DeploymentStatus): DeploymentStatus { - return Object.fromEntries( - Object.entries(status).map(([step, currentStatus]) => [ - step, - currentStatus === 'pending' ? 'idle' : currentStatus, - ]), - ) as DeploymentStatus; -} - -/** - * Fetches the deployment status for a given scene. - * - * @param sceneId - The unique identifier of the scene. - * @param identity - The authentication identity for signing requests. - * @returns A promise resolving to the deployment status. - */ -export async function fetchDeploymentStatus( - sceneId: string, - identity: AuthIdentity, -): Promise { - const method = 'get'; - const path = `/entities/status/${sceneId}`; - const url = new URL(path, 'https://asset-bundle-registry.decentraland.zone'); - const headers = getAuthHeaders(method, url.pathname, payload => - Authenticator.signPayload(identity, payload), - ); - - const response = await fetch(url, { method, headers }); - - if (!response.ok) throw new Error(`Error fetching deployment status: ${response.status}`); - - const json = (await response.json()) as AssetBundleRegistryResponse; - - return { - catalyst: validateStatus(json.catalyst), - assetBundle: deriveOverallStatus(json.assetBundles), - lods: deriveOverallStatus(json.lods), - }; -} - -/** - * Periodically checks the deployment status and updates the caller with changes. - * Retries the status check up to a maximum number of attempts or until the deployment succeeds. - * - * @param maxRetries - The maximum number of retries before considering the deployment a failure. - * @param retryDelayInMs - The delay in milliseconds between consecutive retries. - * @param fetchStatus - A promise function that resolves to a DeploymentStatus triggered on every retry. - * @param onChange - A callback function triggered whenever the deployment status changes. - * @param abort - A function that returns `true` if the status check should be aborted. - * @param initialStatus - The initial deployment status to start with (defaults to 'idle' for every step). - * @returns A promise resolving to the deployment status. - * @throws {DeploymentError} Throws an error if the maximum retries are reached without success. - */ -export async function checkDeploymentStatus( - maxRetries: number, - retryDelayInMs: number, - fetchStatus: () => Promise, - onChange: (status: DeploymentStatus) => void, - abort: () => boolean, - initialStatus: DeploymentStatus = getInitialDeploymentStatus(), -): Promise { - let currentStatus = initialStatus; - let retries = 0; - let error: Error | undefined = undefined; - - function _onChange(status: DeploymentStatus) { - onChange(status); - currentStatus = status; - } - - while (retries < maxRetries) { - try { - if (abort()) { - console.log('Deployment status check aborted...'); - return currentStatus; - } - const status = await fetchStatus(); - if (!equal(currentStatus, status)) _onChange(status); - } catch (e: any) { - error = new DeploymentError('FETCH', 'Fetch deployment status failed.', e); - console.error(error); - } - - retries++; - - // return if all components of the deployment are successful - const allSuccessful = Object.values(currentStatus).every($ => $ === 'complete'); - if (allSuccessful) { - console.log('Deployment success!'); - return currentStatus; - } - - if (retries < maxRetries) { - console.log( - `Attempt ${retries + 1}/${maxRetries} failed. Retrying in ${retryDelayInMs}ms...`, - ); - await delay(retryDelayInMs); - } - } - - // if maximum retries are reached, log the error and throw - const maxRetriesError = new DeploymentError( - 'MAX_RETRIES', - 'Max retries reached. Deployment failed.', - currentStatus, - error, - ); - console.error(maxRetriesError); - throw maxRetriesError; -} - -/** - * Checks if the deployment is nearing completion based on a given percentage threshold. - * - * This function evaluates the `DeploymentStatus` object to determine whether the proportion - * of steps with a 'complete' status meets or exceeds the specified threshold (default: 60%). - * - * @param status - The `DeploymentStatus` object containing the current statuses of deployment steps. - * @param percentage - The completion threshold as a decimal (e.g., `0.6` for 60%). Defaults to 0.6. - * @returns `true` if the proportion of completed steps is greater than or equal to the threshold; otherwise, `false`. - */ -export function isDeployFinishing(status: DeploymentStatus, percentage: number = 0.6): boolean { - const statuses = Object.values(status); - const total = statuses.length; - if (total === 0) return false; - const completedCount = statuses.filter(value => value === 'complete').length; - return completedCount / total >= percentage; -} 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 3edb4f7e..65501ba3 100644 --- a/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx +++ b/packages/renderer/src/components/Modals/PublishProject/steps/PublishToLand/component.tsx @@ -63,7 +63,7 @@ export function PublishToLand(props: Props) { updatedAt: Date.now(), }); void publishScene({ - target: DEPLOY_URLS.DEV_CATALYST_SERVER, + target: import.meta.env.VITE_CATALYST_SERVER || DEPLOY_URLS.CATALYST_SERVER, }); props.onStep('deploy'); }, [placement, props.onStep]); diff --git a/packages/renderer/src/components/Navbar/styles.css b/packages/renderer/src/components/Navbar/styles.css index b7036fd5..bda59d1c 100644 --- a/packages/renderer/src/components/Navbar/styles.css +++ b/packages/renderer/src/components/Navbar/styles.css @@ -34,6 +34,17 @@ border-bottom: 2px solid white; } +.Navbar .feedback { + border-radius: 6px; + padding: 8px; + text-transform: uppercase; + border: 1px solid var(--text); + font-size: 12px; + font-weight: 600; + text-align: center; + cursor: pointer; +} + .Navbar .actions { display: flex; align-items: center; diff --git a/packages/renderer/src/components/Step/component.tsx b/packages/renderer/src/components/Step/component.tsx deleted file mode 100644 index 793ac51f..00000000 --- a/packages/renderer/src/components/Step/component.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useMemo } from 'react'; -import cx from 'classnames'; -import CheckIcon from '@mui/icons-material/Check'; -import CloseIcon from '@mui/icons-material/Close'; - -import type { Step } from './types'; - -import './styles.css'; - -export function Step({ bulletText, name, description, state = 'idle' }: Step) { - const bullet = useMemo(() => { - if (state === 'complete') return ; - if (state === 'failed') return ; - return bulletText; - }, [state, bulletText]); - - return ( -
-
{bullet}
-
-

{name}

- {description} -
-
- ); -} - -export function ConnectedSteps({ steps }: { steps: Step[] }) { - return ( -
- {steps.map(($, idx) => ( - - ))} -
- ); -} diff --git a/packages/renderer/src/components/Step/index.ts b/packages/renderer/src/components/Step/index.ts deleted file mode 100644 index b037a72a..00000000 --- a/packages/renderer/src/components/Step/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Step, ConnectedSteps } from './component'; diff --git a/packages/renderer/src/components/Step/styles.css b/packages/renderer/src/components/Step/styles.css deleted file mode 100644 index 1a7c54ae..00000000 --- a/packages/renderer/src/components/Step/styles.css +++ /dev/null @@ -1,75 +0,0 @@ -.Step { - display: flex; - align-items: center; - color: var(--white); -} - -.Step .bullet, -.Step.idle .bullet { - background-color: var(--light-gray); - border-radius: 50%; - color: var(--darker-gray); - width: 30px; - height: 30px; - display: flex; - align-items: center; - justify-content: center; - margin-right: 10px; -} - -.Step.pending .bullet, -.Step.complete .bullet, -.Step.failed .bullet { - background-color: var(--primary); -} - -.Step.idle { - color: var(--dcl-silver); -} - -.Step.failed { - color: var(--primary); -} - -.Step.complete .bullet, -.Step.failed .bullet { - color: var(--white); -} - -.Step .body { - display: flex; - flex-direction: column; -} - -.Step .body h4 { - margin: 0; - line-height: 20px; - font-weight: 700; -} - -.Step.pending .body h4 { - font-weight: 700; -} - -.Step .body span { - font-size: 14px; -} - -.ConnectedSteps { - display: flex; - align-items: center; - justify-content: space-between; -} - -.ConnectedSteps .Step { - position: relative; -} - -.ConnectedSteps .Step:not(:last-child)::after { - content: ''; - background-color: var(--dcl-silver); - width: 81px; - height: 1px; - position: absolute; - left: 110%; -} diff --git a/packages/renderer/src/components/Step/types.ts b/packages/renderer/src/components/Step/types.ts deleted file mode 100644 index 71a0b7e0..00000000 --- a/packages/renderer/src/components/Step/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { ReactNode } from 'react'; - -export type Step = { - bulletText: ReactNode; - name: string; - description?: string; - state?: 'idle' | 'pending' | 'complete' | 'failed'; -}; diff --git a/packages/renderer/src/lib/profile.ts b/packages/renderer/src/lib/profile.ts index fdd1a823..279064d9 100644 --- a/packages/renderer/src/lib/profile.ts +++ b/packages/renderer/src/lib/profile.ts @@ -7,7 +7,7 @@ class Profiles { try { const response = await fetch(`${PEER_URL}/lambdas/profiles/${address}`); const profile = (await response.json()) as Profile; - return profile.avatars[0]; + return profile.avatars[0] as Avatar; } catch (error) { console.error(error); return undefined; diff --git a/packages/renderer/src/modules/store/translation/locales/en.json b/packages/renderer/src/modules/store/translation/locales/en.json index 1cf09de0..32d799ee 100644 --- a/packages/renderer/src/modules/store/translation/locales/en.json +++ b/packages/renderer/src/modules/store/translation/locales/en.json @@ -166,35 +166,12 @@ "size": "Total Size {size}", "publish": "Publish" }, - "deploying": { - "publish": "Publishing...", - "finishing": "Finishing up...", - "failed": "Publishing failed", - "try_again": "Please try again", - "info": "This process may take 15 minutes on average. During this time, your scene may appear empty until it has been updated in the client.", - "step": { - "catalyst": "Catalyst", - "asset_bundle": "Asset Bundle", - "lods": "LODs", - "loading": "Loading...", - "failed": "Error" - }, - "errors": { - "catalyst_deploy": "Catalyst deployment failed. Try again later...", - "max_retries": "Max retries reached. Deployment failed.

Here is the error: {error}" - }, - "actions": { - "report_issue": "Report an issue", - "retry": "Retry" - } - }, "success": { "message": "You scene is successfully published", "url": "The URL to jump in your {target} is:", "world": "World", "land": "Land", - "jump_in": "Jump In", - "in_progress": "You can now access your scene even while it's still processing." + "jump_in": "Jump In" } } }, diff --git a/packages/renderer/src/modules/store/translation/locales/es.json b/packages/renderer/src/modules/store/translation/locales/es.json index 2504bac4..cd578551 100644 --- a/packages/renderer/src/modules/store/translation/locales/es.json +++ b/packages/renderer/src/modules/store/translation/locales/es.json @@ -74,7 +74,6 @@ "more": "Más" }, "feedback": "Darnos feedback", - "report_an_issue": "Informar un problema", "user_menu": { "sign_in": "Iniciar Sesión", "sign_out": "Cerrar Sesión" @@ -135,8 +134,7 @@ } }, "alternative_servers": { - "title": "Publicar en un Servidor Alternativo", - "list": "Servidores Alternativos", + "title": "Servidores Alternativos", "options": { "test_server": "Servidor de Prueba", "custom_server": "Servidor Personalizado" @@ -166,35 +164,12 @@ "size": "Tamaño Total {size}", "publish": "Publicar" }, - "deploying": { - "publish": "Publicando...", - "finishing": "Terminando...", - "failed": "Error al publicar", - "try_again": "Por favor inténtalo de nuevo", - "info": "Este proceso puede tardar una media de 15 minutos. Durante este tiempo, su escena puede aparecer vacía hasta que se actualice en el cliente.", - "step": { - "catalyst": "Catalyst", - "asset_bundle": "Asset Bundle", - "lods": "LODs", - "loading": "Cargando...", - "failed": "Error" - }, - "errors": { - "catalyst_deploy": "Falló la actualización del Catalyst. Vuelve a intentarlo más tarde...", - "max_retries": "Se alcanzó el número máximo de reintentos. Error en la actualización.

Este es el error: {error}" - }, - "actions": { - "report_issue": "Informar un problema", - "retry": "Reintentar" - } - }, "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", - "in_progress": "Ahora puedes acceder a tu escena incluso mientras aún se está procesando." + "jump_in": "Entrar" } } }, diff --git a/packages/renderer/src/modules/store/translation/locales/zh.json b/packages/renderer/src/modules/store/translation/locales/zh.json index 4a39c86b..ea596f3e 100644 --- a/packages/renderer/src/modules/store/translation/locales/zh.json +++ b/packages/renderer/src/modules/store/translation/locales/zh.json @@ -74,7 +74,6 @@ "more": "更多" }, "feedback": "给我们反馈", - "report_an_issue": "报告问题", "user_menu": { "sign_in": "登录", "sign_out": "登出" @@ -135,8 +134,7 @@ } }, "alternative_servers": { - "title": "发布到不同的服务器", - "list": "备用服务器", + "title": "替代服务器", "options": { "test_server": "测试服务器", "custom_server": "定制服务器" @@ -166,35 +164,12 @@ "size": "总大小 {size}", "publish": "发布" }, - "deploying": { - "publish": "出版...", - "finishing": "整理完毕...", - "failed": "发布失败", - "try_again": "请重试", - "info": "此过程平均可能需要 15 分钟。在此期间,您的场景可能会显示为空,直到在客户端中更新为止。", - "step": { - "catalyst": "Catalyst", - "asset_bundle": "资产包", - "lods": "LODs", - "loading": "加载中...", - "failed": "失败的" - }, - "errors": { - "catalyst_deploy": "部分部署失败。稍后再试...", - "max_retries": "已达到最大重试次数。部署失败。

错误如下:{error}" - }, - "actions": { - "report_issue": "报告问题", - "retry": "重试" - } - }, "success": { "message": "您的场景已成功发布", "url": "跳转到您的 {target} 的URL是:", "world": "世界", "land": "土地", - "jump_in": "跳转", - "in_progress": "现在,即使场景仍在处理中,您也可以访问该场景。" + "jump_in": "跳转" } } }, diff --git a/packages/shared/utils.ts b/packages/shared/utils.ts index b3a0512d..41d0ef16 100644 --- a/packages/shared/utils.ts +++ b/packages/shared/utils.ts @@ -42,5 +42,3 @@ export const throttle = ( }, ]; }; - -export const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));