mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
1 Commits
feature/em
...
fix/export
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b21a191d4c |
88
newIDE/app/src/Export/Builds/BuildsWatcherNew.js
Normal file
88
newIDE/app/src/Export/Builds/BuildsWatcherNew.js
Normal file
@@ -0,0 +1,88 @@
|
||||
// @flow
|
||||
import { type Build, getBuild } from '../../Utils/GDevelopServices/Build';
|
||||
import { delay } from '../../Utils/Delay';
|
||||
import { type AuthenticatedUser } from '../../Profile/AuthenticatedUserContext';
|
||||
|
||||
const waitTime = 1500;
|
||||
const bulkWaitTime = 5000;
|
||||
const maxTimeBeforeIgnoring = 12 * 60 * 60 * 1000; // 12 hours in milliseconds
|
||||
|
||||
export const startWatchingBuilds = (
|
||||
authenticatedUser: AuthenticatedUser,
|
||||
builds: Array<Build>,
|
||||
onBuildUpdated: (build: Build) => void,
|
||||
runningWatchers: { [string]: boolean } = {},
|
||||
setRunningWatchers: ({ [string]: boolean }) => void
|
||||
) => {
|
||||
console.log('starting watching build');
|
||||
stopWatchingBuilds(setRunningWatchers);
|
||||
builds.forEach(build => {
|
||||
if (build.status === 'pending') {
|
||||
if (
|
||||
(!build.createdAt ||
|
||||
build.createdAt < Date.now() - maxTimeBeforeIgnoring) &&
|
||||
(!build.updatedAt ||
|
||||
build.updatedAt < Date.now() - maxTimeBeforeIgnoring)
|
||||
) {
|
||||
console.info(
|
||||
"Ignoring a build for polling as it's too old and still pending",
|
||||
build
|
||||
);
|
||||
} else {
|
||||
pollBuild(
|
||||
build.id,
|
||||
builds.length > 1 ? bulkWaitTime : waitTime,
|
||||
runningWatchers,
|
||||
setRunningWatchers,
|
||||
authenticatedUser,
|
||||
onBuildUpdated
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const stopWatchingBuilds = (
|
||||
setRunningWatchers: ({ [string]: boolean }) => void
|
||||
) => {
|
||||
setRunningWatchers({});
|
||||
};
|
||||
|
||||
const pollBuild = async (
|
||||
buildId: string,
|
||||
waitTime: number,
|
||||
runningWatchers: { [string]: boolean },
|
||||
setRunningWatchers: ({ [string]: boolean }) => void,
|
||||
authenticatedUser: ?AuthenticatedUser,
|
||||
onBuildUpdated: ?(build: Build) => void
|
||||
) => {
|
||||
console.log('polling build');
|
||||
setRunningWatchers({
|
||||
...runningWatchers,
|
||||
[buildId]: true,
|
||||
});
|
||||
|
||||
let build = null;
|
||||
do {
|
||||
if (!authenticatedUser) return;
|
||||
|
||||
const { getAuthorizationHeader, firebaseUser } = authenticatedUser;
|
||||
if (!firebaseUser) return;
|
||||
|
||||
try {
|
||||
console.info(`Checking progress of build ${buildId}...`);
|
||||
build = await getBuild(getAuthorizationHeader, firebaseUser.uid, buildId);
|
||||
if (onBuildUpdated) onBuildUpdated(build);
|
||||
} catch (e) {
|
||||
console.warn('Error while watching build progress:', e);
|
||||
}
|
||||
|
||||
await delay(waitTime);
|
||||
if (!runningWatchers[buildId]) {
|
||||
console.info(`Stopping watch of build ${buildId}.`);
|
||||
return;
|
||||
}
|
||||
} while (build && build.status === 'pending');
|
||||
|
||||
console.info(`Watch of build ${buildId} finished.`);
|
||||
};
|
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import { I18n } from '@lingui/react';
|
||||
import { t, Trans } from '@lingui/macro';
|
||||
import RaisedButton from '../../UI/RaisedButton';
|
||||
@@ -20,8 +20,10 @@ import {
|
||||
displayProjectErrorsBox,
|
||||
getProjectPropertiesErrors,
|
||||
} from '../../Utils/ProjectErrorsChecker';
|
||||
import { type Limit } from '../../Utils/GDevelopServices/Usage';
|
||||
import BuildsWatcher from '../Builds/BuildsWatcher';
|
||||
import {
|
||||
startWatchingBuilds,
|
||||
stopWatchingBuilds,
|
||||
} from '../Builds/BuildsWatcherNew';
|
||||
import BuildStepsProgress, {
|
||||
type BuildStep,
|
||||
} from '../Builds/BuildStepsProgress';
|
||||
@@ -39,17 +41,6 @@ import {
|
||||
TRIVIAL_FIRST_WEB_EXPORT,
|
||||
} from '../../Utils/GDevelopServices/Badge';
|
||||
|
||||
type State = {|
|
||||
exportStep: BuildStep,
|
||||
compressionOutput: any,
|
||||
build: ?Build,
|
||||
stepCurrentProgress: number,
|
||||
stepMaxProgress: number,
|
||||
errored: boolean,
|
||||
exportState: any,
|
||||
doneFooterOpen: boolean,
|
||||
|};
|
||||
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
onChangeSubscription: () => void,
|
||||
@@ -61,78 +52,100 @@ type Props = {|
|
||||
* A generic UI to launch, monitor the progres and get the result
|
||||
* of an export.
|
||||
*/
|
||||
export default class ExportLauncher extends Component<Props, State> {
|
||||
state = {
|
||||
exportStep: '',
|
||||
build: null,
|
||||
compressionOutput: null,
|
||||
stepCurrentProgress: 0,
|
||||
stepMaxProgress: 0,
|
||||
doneFooterOpen: false,
|
||||
errored: false,
|
||||
exportState: this.props.exportPipeline.getInitialExportState(
|
||||
this.props.project
|
||||
),
|
||||
};
|
||||
buildsWatcher = new BuildsWatcher();
|
||||
launchWholeExport: () => Promise<void>;
|
||||
export default function ExportLauncher({
|
||||
project,
|
||||
onChangeSubscription,
|
||||
authenticatedUser,
|
||||
exportPipeline,
|
||||
}: Props) {
|
||||
const [exportStep, setExportStep] = React.useState<BuildStep>('');
|
||||
const [build, setBuild] = React.useState<?Build>(null);
|
||||
const [compressionOutput, setCompressionOutput] = React.useState(null);
|
||||
const [stepCurrentProgress, setStepCurrentProgress] = React.useState(0);
|
||||
const [stepMaxProgress, setStepMaxProgress] = React.useState(0);
|
||||
const [errored, setErrored] = React.useState(false);
|
||||
const [exportState, setExportState] = React.useState(
|
||||
exportPipeline.getInitialExportState(project)
|
||||
);
|
||||
const [doneFooterOpen, setDoneFooterOpen] = React.useState(false);
|
||||
const [buildWatchers, setBuildWatchers] = React.useState({});
|
||||
|
||||
componentWillUnmount() {
|
||||
this.buildsWatcher.stop();
|
||||
}
|
||||
// Cleanup
|
||||
React.useEffect(() => {
|
||||
return () => stopWatchingBuilds(setBuildWatchers);
|
||||
});
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this._setupAchievementHook();
|
||||
}
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
this._setupAchievementHook();
|
||||
}
|
||||
const buildLimit = React.useMemo(
|
||||
() =>
|
||||
authenticatedUser.limits && exportPipeline.onlineBuildType
|
||||
? authenticatedUser.limits[exportPipeline.onlineBuildType]
|
||||
: null,
|
||||
[authenticatedUser, exportPipeline.onlineBuildType]
|
||||
);
|
||||
|
||||
_setupAchievementHook = () => {
|
||||
if (
|
||||
ACHIEVEMENT_FEATURE_FLAG &&
|
||||
this.props.exportPipeline.name.includes('web')
|
||||
) {
|
||||
this.launchWholeExport = addCreateBadgePreHookIfNotClaimed(
|
||||
this.props.authenticatedUser,
|
||||
TRIVIAL_FIRST_WEB_EXPORT,
|
||||
this._launchWholeExport
|
||||
);
|
||||
} else {
|
||||
this.launchWholeExport = this._launchWholeExport;
|
||||
}
|
||||
};
|
||||
const canLaunchBuild = React.useMemo(
|
||||
() => {
|
||||
const buildPending =
|
||||
!errored && exportStep !== '' && exportStep !== 'done';
|
||||
const buildFinished = !errored && exportStep === 'done';
|
||||
if (buildPending || buildFinished) return false;
|
||||
|
||||
_updateStepProgress = (
|
||||
if (buildLimit && buildLimit.limitReached) return false;
|
||||
|
||||
return exportPipeline.canLaunchBuild(exportState);
|
||||
},
|
||||
[errored, exportStep, exportState, buildLimit, exportPipeline]
|
||||
);
|
||||
|
||||
const updateStepProgress = (
|
||||
stepCurrentProgress: number,
|
||||
stepMaxProgress: number
|
||||
) =>
|
||||
this.setState({
|
||||
stepCurrentProgress,
|
||||
stepMaxProgress,
|
||||
});
|
||||
|
||||
_startBuildWatch = (authenticatedUser: AuthenticatedUser) => {
|
||||
if (!this.state.build) return;
|
||||
|
||||
this.buildsWatcher.start({
|
||||
authenticatedUser,
|
||||
builds: [this.state.build],
|
||||
onBuildUpdated: (build: Build) => {
|
||||
this.setState({ build });
|
||||
authenticatedUser.onRefreshUserProfile();
|
||||
},
|
||||
});
|
||||
) => {
|
||||
setStepCurrentProgress(stepCurrentProgress);
|
||||
setStepMaxProgress(stepMaxProgress);
|
||||
};
|
||||
|
||||
registerAndUpdateGame = async () => {
|
||||
const profile = this.props.authenticatedUser.profile;
|
||||
const getAuthorizationHeader = this.props.authenticatedUser
|
||||
.getAuthorizationHeader;
|
||||
const gameId = this.props.project.getProjectUuid();
|
||||
const authorName = this.props.project.getAuthor() || 'Unspecified author';
|
||||
const gameName = this.props.project.getName() || 'Untitled game';
|
||||
const startBuildWatch = React.useCallback(
|
||||
() => {
|
||||
console.log('starting build watch', build);
|
||||
// Build is finished or already being watched.
|
||||
if (!build || build.status !== 'pending' || buildWatchers[build.id])
|
||||
return;
|
||||
console.log(build, buildWatchers);
|
||||
|
||||
console.log('properly starting build watch', build);
|
||||
|
||||
startWatchingBuilds(
|
||||
authenticatedUser,
|
||||
[build],
|
||||
build => {
|
||||
setBuild(build);
|
||||
if (build.status !== 'pending') {
|
||||
// Refresh user limits.
|
||||
authenticatedUser.onRefreshUserProfile();
|
||||
}
|
||||
},
|
||||
buildWatchers,
|
||||
setBuildWatchers
|
||||
);
|
||||
},
|
||||
[authenticatedUser, build, buildWatchers]
|
||||
);
|
||||
|
||||
React.useEffect(
|
||||
() => {
|
||||
if (!build) return;
|
||||
startBuildWatch();
|
||||
},
|
||||
[build, startBuildWatch]
|
||||
);
|
||||
|
||||
const registerAndUpdateGame = async () => {
|
||||
const profile = authenticatedUser.profile;
|
||||
const getAuthorizationHeader = authenticatedUser.getAuthorizationHeader;
|
||||
const gameId = project.getProjectUuid();
|
||||
const authorName = project.getAuthor() || 'Unspecified author';
|
||||
const gameName = project.getName() || 'Untitled game';
|
||||
if (profile) {
|
||||
const userId = profile.id;
|
||||
try {
|
||||
@@ -156,16 +169,15 @@ export default class ExportLauncher extends Component<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
_launchWholeExport = async () => {
|
||||
const _launchWholeExport = async () => {
|
||||
const t = str => str; //TODO;
|
||||
const { project, exportPipeline, authenticatedUser } = this.props;
|
||||
sendExportLaunched(exportPipeline.name);
|
||||
|
||||
if (!displayProjectErrorsBox(t, getProjectPropertiesErrors(t, project)))
|
||||
return;
|
||||
|
||||
const getErrorMessage = () => {
|
||||
switch (this.state.exportStep) {
|
||||
switch (exportStep) {
|
||||
case 'export':
|
||||
return t('Error while preparing the exporter.');
|
||||
case 'resources-download':
|
||||
@@ -190,16 +202,14 @@ export default class ExportLauncher extends Component<Props, State> {
|
||||
};
|
||||
|
||||
const handleError = (err: Error) => {
|
||||
if (!this.state.errored) {
|
||||
this.setState({
|
||||
errored: true,
|
||||
});
|
||||
if (!errored) {
|
||||
setErrored(true);
|
||||
showErrorBox({
|
||||
message:
|
||||
getErrorMessage() +
|
||||
(err.message ? `\n\nDetails of the error: ${err.message}` : ''),
|
||||
rawError: {
|
||||
exportStep: this.state.exportStep,
|
||||
exportStep: exportStep,
|
||||
rawError: err,
|
||||
},
|
||||
errorId: 'export-error',
|
||||
@@ -207,11 +217,9 @@ export default class ExportLauncher extends Component<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
const setStep = (step: BuildStep) => this.setState({ exportStep: step });
|
||||
|
||||
try {
|
||||
// We do not await for this call, allowing to start building the game in parallel.
|
||||
this.registerAndUpdateGame();
|
||||
registerAndUpdateGame();
|
||||
} catch {
|
||||
// Best effort call, we don't prevent building the game.
|
||||
console.warn('Error while registering the game - ignoring it.');
|
||||
@@ -220,16 +228,14 @@ export default class ExportLauncher extends Component<Props, State> {
|
||||
try {
|
||||
const exportPipelineContext = {
|
||||
project,
|
||||
updateStepProgress: this._updateStepProgress,
|
||||
exportState: this.state.exportState,
|
||||
updateStepProgress,
|
||||
exportState: exportState,
|
||||
};
|
||||
setStep('export');
|
||||
this.setState({
|
||||
stepCurrentProgress: 0,
|
||||
stepMaxProgress: 0,
|
||||
errored: false,
|
||||
build: null,
|
||||
});
|
||||
setExportStep('export');
|
||||
setStepCurrentProgress(0);
|
||||
setStepMaxProgress(0);
|
||||
setErrored(false);
|
||||
setBuild(null);
|
||||
const preparedExporter = await exportPipeline.prepareExporter(
|
||||
exportPipelineContext
|
||||
);
|
||||
@@ -237,184 +243,150 @@ export default class ExportLauncher extends Component<Props, State> {
|
||||
exportPipelineContext,
|
||||
preparedExporter
|
||||
);
|
||||
setStep('resources-download');
|
||||
setExportStep('resources-download');
|
||||
const resourcesDownloadOutput = await exportPipeline.launchResourcesDownload(
|
||||
exportPipelineContext,
|
||||
exportOutput
|
||||
);
|
||||
setStep('compress');
|
||||
setExportStep('compress');
|
||||
const compressionOutput = await exportPipeline.launchCompression(
|
||||
exportPipelineContext,
|
||||
resourcesDownloadOutput
|
||||
);
|
||||
const { launchUpload, launchOnlineBuild } = exportPipeline;
|
||||
if (!!launchUpload && !!launchOnlineBuild) {
|
||||
setStep('upload');
|
||||
setExportStep('upload');
|
||||
const uploadBucketKey = await launchUpload(
|
||||
exportPipelineContext,
|
||||
compressionOutput
|
||||
);
|
||||
setStep('waiting-for-build');
|
||||
setExportStep('waiting-for-build');
|
||||
const build = await launchOnlineBuild(
|
||||
this.state.exportState,
|
||||
exportState,
|
||||
authenticatedUser,
|
||||
uploadBucketKey
|
||||
);
|
||||
setStep('build');
|
||||
this.setState({ build }, () => {
|
||||
this._startBuildWatch(authenticatedUser);
|
||||
});
|
||||
setExportStep('build');
|
||||
setBuild(build);
|
||||
}
|
||||
setStep('done');
|
||||
this.setState({
|
||||
compressionOutput,
|
||||
doneFooterOpen: true,
|
||||
});
|
||||
setExportStep('done');
|
||||
setDoneFooterOpen(true);
|
||||
setCompressionOutput(compressionOutput);
|
||||
} catch (error) {
|
||||
console.error('An error happened during export:', error);
|
||||
handleError(error);
|
||||
}
|
||||
};
|
||||
|
||||
_downloadBuild = (key: BuildArtifactKeyName) => {
|
||||
const url = getBuildArtifactUrl(this.state.build, key);
|
||||
const launchWholeExport =
|
||||
ACHIEVEMENT_FEATURE_FLAG && exportPipeline.name.includes('web')
|
||||
? addCreateBadgePreHookIfNotClaimed(
|
||||
authenticatedUser,
|
||||
TRIVIAL_FIRST_WEB_EXPORT,
|
||||
_launchWholeExport
|
||||
)
|
||||
: _launchWholeExport;
|
||||
|
||||
const downloadBuild = (key: BuildArtifactKeyName) => {
|
||||
const url = getBuildArtifactUrl(build, key);
|
||||
if (url) Window.openExternalURL(url);
|
||||
};
|
||||
|
||||
_closeDoneFooter = () =>
|
||||
this.setState({
|
||||
doneFooterOpen: false,
|
||||
});
|
||||
|
||||
_updateExportState = (updater: any => any) => {
|
||||
this.setState(prevState => ({
|
||||
...prevState,
|
||||
exportState: updater(prevState.exportState),
|
||||
}));
|
||||
const updateExportState = (updater: any => any) => {
|
||||
setExportState(updater(exportState));
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
exportStep,
|
||||
compressionOutput,
|
||||
build,
|
||||
stepMaxProgress,
|
||||
stepCurrentProgress,
|
||||
errored,
|
||||
doneFooterOpen,
|
||||
exportState,
|
||||
} = this.state;
|
||||
const { project, authenticatedUser, exportPipeline } = this.props;
|
||||
if (!project) return null;
|
||||
if (!project) return null;
|
||||
|
||||
const getBuildLimit = (authenticatedUser: AuthenticatedUser): ?Limit =>
|
||||
authenticatedUser.limits && exportPipeline.onlineBuildType
|
||||
? authenticatedUser.limits[exportPipeline.onlineBuildType]
|
||||
: null;
|
||||
const canLaunchBuild = (authenticatedUser: AuthenticatedUser) => {
|
||||
const buildPending =
|
||||
!errored && exportStep !== '' && exportStep !== 'done';
|
||||
const buildFinished = !errored && exportStep === 'done';
|
||||
if (buildPending || buildFinished) return false;
|
||||
|
||||
const limit: ?Limit = getBuildLimit(authenticatedUser);
|
||||
if (limit && limit.limitReached) return false;
|
||||
|
||||
return exportPipeline.canLaunchBuild(exportState);
|
||||
};
|
||||
|
||||
return (
|
||||
<Column noMargin>
|
||||
{!!exportPipeline.packageNameWarningType &&
|
||||
project.getPackageName().indexOf('com.example') !== -1 && (
|
||||
<Line>
|
||||
<DismissableAlertMessage
|
||||
identifier="project-should-have-unique-package-name"
|
||||
kind="warning"
|
||||
>
|
||||
<I18n>
|
||||
{({ i18n }) =>
|
||||
i18n._(
|
||||
exportPipeline.packageNameWarningType === 'mobile'
|
||||
? t`The package name begins with com.example, make sure you replace it with an unique one to be able to publish your game on app stores.`
|
||||
: t`The package name begins with com.example, make sure you replace it with an unique one, else installing your game might overwrite other games.`
|
||||
)
|
||||
}
|
||||
</I18n>
|
||||
</DismissableAlertMessage>
|
||||
</Line>
|
||||
)}
|
||||
<Line>
|
||||
{exportPipeline.renderHeader({
|
||||
project,
|
||||
exportState,
|
||||
updateExportState: this._updateExportState,
|
||||
})}
|
||||
</Line>
|
||||
{(!exportPipeline.onlineBuildType ||
|
||||
authenticatedUser.authenticated) && (
|
||||
<Line justifyContent="center">
|
||||
<RaisedButton
|
||||
label={exportPipeline.renderLaunchButtonLabel()}
|
||||
primary
|
||||
onClick={this.launchWholeExport}
|
||||
disabled={!canLaunchBuild(authenticatedUser)}
|
||||
/>
|
||||
return (
|
||||
<Column noMargin>
|
||||
{!!exportPipeline.packageNameWarningType &&
|
||||
project.getPackageName().indexOf('com.example') !== -1 && (
|
||||
<Line>
|
||||
<DismissableAlertMessage
|
||||
identifier="project-should-have-unique-package-name"
|
||||
kind="warning"
|
||||
>
|
||||
<I18n>
|
||||
{({ i18n }) =>
|
||||
i18n._(
|
||||
exportPipeline.packageNameWarningType === 'mobile'
|
||||
? t`The package name begins with com.example, make sure you replace it with an unique one to be able to publish your game on app stores.`
|
||||
: t`The package name begins with com.example, make sure you replace it with an unique one, else installing your game might overwrite other games.`
|
||||
)
|
||||
}
|
||||
</I18n>
|
||||
</DismissableAlertMessage>
|
||||
</Line>
|
||||
)}
|
||||
<Spacer />
|
||||
{!!exportPipeline.onlineBuildType &&
|
||||
!authenticatedUser.authenticated && (
|
||||
<CreateProfile
|
||||
onLogin={authenticatedUser.onLogin}
|
||||
onCreateAccount={authenticatedUser.onCreateAccount}
|
||||
message={
|
||||
<Trans>
|
||||
Create an account or login first to publish your game.
|
||||
</Trans>
|
||||
}
|
||||
justifyContent="center"
|
||||
/>
|
||||
)}
|
||||
{authenticatedUser.authenticated &&
|
||||
(exportPipeline.renderCustomStepsProgress ? (
|
||||
exportPipeline.renderCustomStepsProgress(
|
||||
build,
|
||||
!!this.state.exportStep && this.state.exportStep !== 'done'
|
||||
)
|
||||
) : (
|
||||
<Line expand>
|
||||
<BuildStepsProgress
|
||||
exportStep={exportStep}
|
||||
hasBuildStep={!!exportPipeline.onlineBuildType}
|
||||
build={build}
|
||||
onDownload={this._downloadBuild}
|
||||
stepMaxProgress={stepMaxProgress}
|
||||
stepCurrentProgress={stepCurrentProgress}
|
||||
errored={errored}
|
||||
/>
|
||||
</Line>
|
||||
))}
|
||||
{!!exportPipeline.limitedBuilds && authenticatedUser.authenticated && (
|
||||
<LimitDisplayer
|
||||
subscription={authenticatedUser.subscription}
|
||||
limit={getBuildLimit(authenticatedUser)}
|
||||
onChangeSubscription={this.props.onChangeSubscription}
|
||||
<Line>
|
||||
{exportPipeline.renderHeader({
|
||||
project,
|
||||
exportState,
|
||||
updateExportState,
|
||||
})}
|
||||
</Line>
|
||||
{(!exportPipeline.onlineBuildType || authenticatedUser.authenticated) && (
|
||||
<Line justifyContent="center">
|
||||
<RaisedButton
|
||||
label={exportPipeline.renderLaunchButtonLabel()}
|
||||
primary
|
||||
onClick={launchWholeExport}
|
||||
disabled={!canLaunchBuild}
|
||||
/>
|
||||
)}
|
||||
{doneFooterOpen &&
|
||||
exportPipeline.renderDoneFooter &&
|
||||
exportPipeline.renderDoneFooter({
|
||||
compressionOutput,
|
||||
exportState,
|
||||
onClose: this._closeDoneFooter,
|
||||
})}
|
||||
{doneFooterOpen && (
|
||||
<Line justifyContent="center">
|
||||
<GameRegistration project={project} hideIfSubscribed hideLoader />
|
||||
</Line>
|
||||
)}
|
||||
<Spacer />
|
||||
{!!exportPipeline.onlineBuildType && !authenticatedUser.authenticated && (
|
||||
<CreateProfile
|
||||
onLogin={authenticatedUser.onLogin}
|
||||
onCreateAccount={authenticatedUser.onCreateAccount}
|
||||
message={
|
||||
<Trans>
|
||||
Create an account or login first to publish your game.
|
||||
</Trans>
|
||||
}
|
||||
justifyContent="center"
|
||||
/>
|
||||
)}
|
||||
{authenticatedUser.authenticated &&
|
||||
(exportPipeline.renderCustomStepsProgress ? (
|
||||
exportPipeline.renderCustomStepsProgress(
|
||||
build,
|
||||
!!exportStep && exportStep !== 'done'
|
||||
)
|
||||
) : (
|
||||
<Line expand>
|
||||
<BuildStepsProgress
|
||||
exportStep={exportStep}
|
||||
hasBuildStep={!!exportPipeline.onlineBuildType}
|
||||
build={build}
|
||||
onDownload={downloadBuild}
|
||||
stepMaxProgress={stepMaxProgress}
|
||||
stepCurrentProgress={stepCurrentProgress}
|
||||
errored={errored}
|
||||
/>
|
||||
</Line>
|
||||
)}
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
))}
|
||||
{!!exportPipeline.limitedBuilds && authenticatedUser.authenticated && (
|
||||
<LimitDisplayer
|
||||
subscription={authenticatedUser.subscription}
|
||||
limit={buildLimit}
|
||||
onChangeSubscription={onChangeSubscription}
|
||||
/>
|
||||
)}
|
||||
{doneFooterOpen &&
|
||||
exportPipeline.renderDoneFooter &&
|
||||
exportPipeline.renderDoneFooter({
|
||||
compressionOutput,
|
||||
exportState,
|
||||
onClose: () => setDoneFooterOpen(false),
|
||||
})}
|
||||
{doneFooterOpen && (
|
||||
<Line justifyContent="center">
|
||||
<GameRegistration project={project} hideIfSubscribed hideLoader />
|
||||
</Line>
|
||||
)}
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user