Refactor project opening/saving into ProjectsStorage with StorageProviders.

This commit is contained in:
Florian Rival
2019-10-09 23:24:28 +01:00
parent f0163fc1d1
commit 4319ddcd0f
14 changed files with 668 additions and 531 deletions

View File

@@ -36,15 +36,16 @@ const updateResources = (project, baseUrl) => {
project.exposeResources(worker);
};
const writeBrowserExampleFilesJsFile = exampleNames => {
const writeInternalExampleFilesJsFile = exampleNames => {
let importsCode = [];
let internalFilesObjectCode = [];
exampleNames.forEach((exampleName, index) => {
importsCode.push(`import exampleFile${index} from '../fixtures/${exampleName}/${exampleName}.json';`);
importsCode.push(`import exampleFile${index} from '../../fixtures/${exampleName}/${exampleName}.json';`);
internalFilesObjectCode.push(` 'example://${exampleName}': exampleFile${index},`);
});
const content = [
`// @flow`,
`// This file is generated by update-fixtures-from-resources-examples.js script`,
``,
importsCode.join('\n'),
@@ -58,7 +59,7 @@ const writeBrowserExampleFilesJsFile = exampleNames => {
return new Promise((resolve, reject) => {
fs.writeFile(
'../src/ProjectsStorage/BrowserExampleFiles.js',
'../src/ProjectsStorage/InternalFileStorageProvider/InternalExampleFiles.js',
content,
'utf8',
err => {
@@ -110,14 +111,14 @@ extensionsLoader
});
})
)
.then(() => writeBrowserExampleFilesJsFile(exampleNames))
.then(() => writeInternalExampleFilesJsFile(exampleNames))
.then(
() => {
console.error(`BrowserExampleFiles.js written.`);
console.error(`InternalExampleFiles.js written.`);
},
error => {
console.error(
`❌ Error caught while writing BrowserExampleFiles.js:`,
`❌ Error caught while writing InternalExampleFiles.js:`,
error
);
}

View File

@@ -1,5 +1,5 @@
// @flow
import React from 'react';
import * as React from 'react';
import MainFrame from './MainFrame';
import Window from './Utils/Window';
import ExportDialog from './Export/ExportDialog';
@@ -10,8 +10,6 @@ import './UI/iconmoon-font.css'; // Styles for Iconmoon font.
// Import for browser only IDE
import BrowserExamples from './ProjectCreation/BrowserExamples';
import BrowserStarters from './ProjectCreation/BrowserStarters';
import BrowserProjectOpener from './ProjectsStorage/BrowserProjectOpener';
import BrowserSaveDialog from './ProjectsStorage/BrowserSaveDialog';
import BrowserIntroDialog from './MainFrame/BrowserIntroDialog';
import browserResourceSources from './ResourcesList/BrowserResourceSources';
import browserResourceExternalEditors from './ResourcesList/BrowserResourceExternalEditors';
@@ -22,6 +20,8 @@ import ObjectsEditorService from './ObjectEditor/ObjectsEditorService';
import ObjectsRenderingService from './ObjectsRendering/ObjectsRenderingService';
import { makeBrowserS3EventsFunctionCodeWriter } from './EventsFunctionsExtensionsLoader/CodeWriters/BrowserS3EventsFunctionCodeWriter';
import Providers from './MainFrame/Providers';
import ProjectsStorage from './ProjectsStorage';
import InternalFileStorageProvider from './ProjectsStorage/InternalFileStorageProvider';
export const create = (authentification: Authentification) => {
Window.setUpContextMenu();
@@ -38,30 +38,38 @@ export const create = (authentification: Authentification) => {
eventsFunctionsExtensionOpener={null}
>
{({ i18n, eventsFunctionsExtensionsState }) => (
<MainFrame
i18n={i18n}
eventsFunctionsExtensionsState={eventsFunctionsExtensionsState}
previewLauncher={<BrowserS3PreviewLauncher />}
renderExportDialog={(props) => <ExportDialog {...props} exporters={getBrowserExporters()} />}
createDialog={
<CreateProjectDialog
examplesComponent={BrowserExamples}
startersComponent={BrowserStarters}
<ProjectsStorage
storageProviders={[InternalFileStorageProvider]}
defaultStorageProvider={InternalFileStorageProvider}
>
{projectsStorageProps => (
<MainFrame
i18n={i18n}
eventsFunctionsExtensionsState={eventsFunctionsExtensionsState}
previewLauncher={<BrowserS3PreviewLauncher />}
renderExportDialog={props => (
<ExportDialog {...props} exporters={getBrowserExporters()} />
)}
createDialog={
<CreateProjectDialog
examplesComponent={BrowserExamples}
startersComponent={BrowserStarters}
/>
}
introDialog={<BrowserIntroDialog />}
projectsStorage={projectsStorageProps}
resourceSources={browserResourceSources}
resourceExternalEditors={browserResourceExternalEditors}
authentification={authentification}
extensionsLoader={makeExtensionsLoader({
objectsEditorService: ObjectsEditorService,
objectsRenderingService: ObjectsRenderingService,
filterExamples: !Window.isDev(),
})}
initialPathsOrURLsToOpen={appArguments['_']}
/>
}
introDialog={<BrowserIntroDialog />}
saveDialog={<BrowserSaveDialog />}
onReadFromPathOrURL={BrowserProjectOpener.readInternalFile}
resourceSources={browserResourceSources}
resourceExternalEditors={browserResourceExternalEditors}
authentification={authentification}
extensionsLoader={makeExtensionsLoader({
objectsEditorService: ObjectsEditorService,
objectsRenderingService: ObjectsRenderingService,
filterExamples: !Window.isDev(),
})}
initialPathsOrURLsToOpen={appArguments['_']}
/>
)}
</ProjectsStorage>
)}
</Providers>
);

View File

@@ -13,8 +13,6 @@ import LocalExamples from './ProjectCreation/LocalExamples';
import LocalStarters from './ProjectCreation/LocalStarters';
import localResourceSources from './ResourcesList/LocalResourceSources';
import localResourceExternalEditors from './ResourcesList/LocalResourceExternalEditors';
import LocalProjectWriter from './ProjectsStorage/LocalProjectWriter';
import LocalProjectOpener from './ProjectsStorage/LocalProjectOpener';
import LocalPreviewLauncher from './Export/LocalExporters/LocalPreviewLauncher';
import { getLocalExporters } from './Export/LocalExporters';
import ElectronMainMenu from './MainFrame/ElectronMainMenu';
@@ -25,6 +23,8 @@ import ObjectsRenderingService from './ObjectsRendering/ObjectsRenderingService'
import Providers from './MainFrame/Providers';
import LocalEventsFunctionsExtensionWriter from './EventsFunctionsExtensionsLoader/Storage/LocalEventsFunctionsExtensionWriter';
import LocalEventsFunctionsExtensionOpener from './EventsFunctionsExtensionsLoader/Storage/LocalEventsFunctionsExtensionOpener';
import ProjectsStorage from './ProjectsStorage';
import LocalFileStorageProvider from './ProjectsStorage/LocalFileStorageProvider';
const gd = global.gd;
export const create = (authentification: Authentification) => {
@@ -43,24 +43,28 @@ export const create = (authentification: Authentification) => {
eventsFunctionsExtensionOpener={null}
>
{({ i18n, eventsFunctionsExtensionsState }) => (
<ExternalEditor
serverPort={appArguments['server-port']}
isIntegrated={appArguments['mode'] === 'integrated'}
editor={appArguments['editor']}
editedElementName={appArguments['edited-element-name']}
>
<MainFrame
i18n={i18n}
eventsFunctionsExtensionsState={eventsFunctionsExtensionsState}
resourceSources={localResourceSources}
authentification={authentification}
onReadFromPathOrURL={() =>
Promise.reject('Should never be called')
}
resourceExternalEditors={localResourceExternalEditors}
initialPathsOrURLsToOpen={[]}
/>
</ExternalEditor>
<ProjectsStorage storageProviders={[]}>
{projectsStorageProps => (
<ExternalEditor
serverPort={appArguments['server-port']}
isIntegrated={appArguments['mode'] === 'integrated'}
editor={appArguments['editor']}
editedElementName={appArguments['edited-element-name']}
>
<MainFrame
i18n={i18n}
eventsFunctionsExtensionsState={
eventsFunctionsExtensionsState
}
resourceSources={localResourceSources}
authentification={authentification}
projectsStorage={projectsStorageProps}
resourceExternalEditors={localResourceExternalEditors}
initialPathsOrURLsToOpen={[]}
/>
</ExternalEditor>
)}
</ProjectsStorage>
)}
</Providers>
);
@@ -74,36 +78,42 @@ export const create = (authentification: Authentification) => {
eventsFunctionsExtensionOpener={LocalEventsFunctionsExtensionOpener}
>
{({ i18n, eventsFunctionsExtensionsState }) => (
<ElectronMainMenu i18n={i18n}>
<MainFrame
i18n={i18n}
eventsFunctionsExtensionsState={eventsFunctionsExtensionsState}
previewLauncher={<LocalPreviewLauncher />}
renderExportDialog={(props) => <ExportDialog {...props} exporters={getLocalExporters()} />}
createDialog={
<CreateProjectDialog
examplesComponent={LocalExamples}
startersComponent={LocalStarters}
<ProjectsStorage
storageProviders={[LocalFileStorageProvider]}
defaultStorageProvider={LocalFileStorageProvider}
>
{projectsStorageProps => (
<ElectronMainMenu i18n={i18n}>
<MainFrame
i18n={i18n}
eventsFunctionsExtensionsState={
eventsFunctionsExtensionsState
}
previewLauncher={<LocalPreviewLauncher />}
renderExportDialog={props => (
<ExportDialog {...props} exporters={getLocalExporters()} />
)}
createDialog={
<CreateProjectDialog
examplesComponent={LocalExamples}
startersComponent={LocalStarters}
/>
}
projectsStorage={projectsStorageProps}
resourceSources={localResourceSources}
resourceExternalEditors={localResourceExternalEditors}
authentification={authentification}
extensionsLoader={makeExtensionsLoader({
gd,
objectsEditorService: ObjectsEditorService,
objectsRenderingService: ObjectsRenderingService,
filterExamples: !Window.isDev(),
})}
initialPathsOrURLsToOpen={appArguments['_']}
/>
}
onSaveProject={LocalProjectWriter.saveProject}
onSaveProjectAs={LocalProjectWriter.saveProjectAs}
onAutoSaveProject={LocalProjectWriter.autoSaveProject}
onChooseProject={LocalProjectOpener.chooseProjectFile}
onReadFromPathOrURL={LocalProjectOpener.readProjectFile}
shouldOpenAutosave={LocalProjectOpener.shouldOpenAutosave}
resourceSources={localResourceSources}
resourceExternalEditors={localResourceExternalEditors}
authentification={authentification}
extensionsLoader={makeExtensionsLoader({
gd,
objectsEditorService: ObjectsEditorService,
objectsRenderingService: ObjectsRenderingService,
filterExamples: !Window.isDev(),
})}
initialPathsOrURLsToOpen={appArguments['_']}
/>
</ElectronMainMenu>
</ElectronMainMenu>
)}
</ProjectsStorage>
)}
</Providers>
);

View File

@@ -84,6 +84,7 @@ import PreferencesContext from './Preferences/PreferencesContext';
import { getFunctionNameFromType } from '../EventsFunctionsExtensionsLoader';
import { type ExportDialogWithoutExportsProps } from '../Export/ExportDialog';
import { getStartupTimesSummary } from '../Utils/StartupTimes';
import { type ProjectsStorageProps } from '../ProjectsStorage';
const GD_STARTUP_TIMES = global.GD_STARTUP_TIMES || [];
const gd = global.gd;
@@ -101,7 +102,6 @@ type State = {|
createDialogOpen: boolean,
exportDialogOpen: boolean,
introDialogOpen: boolean,
saveDialogOpen: boolean,
genericDialogOpen: boolean,
loadingProject: boolean,
previewLoading: boolean,
@@ -125,21 +125,11 @@ type State = {|
type Props = {
integratedEditor?: boolean,
introDialog?: React.Element<*>,
onReadFromPathOrURL: (url: string) => Promise<any>,
previewLauncher?: React.Element<PreviewLauncher>,
onEditObject?: gdObject => void,
projectsStorage: ProjectsStorageProps,
resourceSources: Array<ResourceSource>,
resourceExternalEditors: Array<ResourceExternalEditor>,
onChooseProject?: () => Promise<?string>,
saveDialog?: React.Element<*>,
onSaveProject?: gdProject => Promise<any>,
onSaveProjectAs?: gdProject => Promise<any>,
onAutoSaveProject?: (project: gdProject) => void,
shouldOpenAutosave?: (
filePath: string,
autoSavePath: string,
compareLastModified: boolean
) => boolean,
loading?: boolean,
requestUpdate?: () => void,
renderExportDialog?: ExportDialogWithoutExportsProps => React.Node,
@@ -156,7 +146,6 @@ class MainFrame extends React.Component<Props, State> {
createDialogOpen: false,
exportDialogOpen: false,
introDialogOpen: false,
saveDialogOpen: false,
genericDialogOpen: false,
loadingProject: false,
previewLoading: false,
@@ -179,7 +168,6 @@ class MainFrame extends React.Component<Props, State> {
toolbar = null;
_resourceSourceDialogs = {};
_previewLauncher: ?PreviewLauncher = null;
_providers = null;
componentWillMount() {
if (!this.props.integratedEditor) this.openStartPage();
@@ -300,11 +288,14 @@ class MainFrame extends React.Component<Props, State> {
};
openFromPathOrURL = (url: string, cb: Function) => {
const { i18n, shouldOpenAutosave } = this.props;
const { i18n, projectsStorage } = this.props;
const projectFilePath = url;
const autoSavePath = url + '.autosave';
if (shouldOpenAutosave && shouldOpenAutosave(url, autoSavePath, true)) {
if (
projectsStorage.shouldOpenAutosave &&
projectsStorage.shouldOpenAutosave(url, autoSavePath, true)
) {
//eslint-disable-next-line
const answer = confirm(
i18n._(
@@ -314,7 +305,7 @@ class MainFrame extends React.Component<Props, State> {
if (answer) url = autoSavePath;
}
this.props.onReadFromPathOrURL(url).then(
projectsStorage.onOpen(url).then(
projectObject => {
this.setState({ loadingProject: true }, () =>
setTimeout(() => {
@@ -339,8 +330,12 @@ class MainFrame extends React.Component<Props, State> {
},
err => {
if (
shouldOpenAutosave &&
shouldOpenAutosave(projectFilePath, autoSavePath, false)
projectsStorage.shouldOpenAutosave &&
projectsStorage.shouldOpenAutosave(
projectFilePath,
autoSavePath,
false
)
) {
//eslint-disable-next-line
const answer = confirm(
@@ -811,7 +806,7 @@ class MainFrame extends React.Component<Props, State> {
openSceneEditor = true,
}: { openEventsEditor: boolean, openSceneEditor: boolean } = {}
) => {
const { i18n, onAutoSaveProject } = this.props;
const { i18n, projectsStorage } = this.props;
const sceneEditorOptions = {
name,
renderEditor: ({ isActive, editorRef }) => (
@@ -823,8 +818,11 @@ class MainFrame extends React.Component<Props, State> {
setToolbar={this.setEditorToolbar}
onPreview={(project, layout, options) => {
this._launchLayoutPreview(project, layout, options);
if (values.autosaveOnPreview && onAutoSaveProject) {
onAutoSaveProject(project);
if (
values.autosaveOnPreview &&
projectsStorage.onAutoSaveProject
) {
projectsStorage.onAutoSaveProject(project);
}
}}
showPreviewButton={!!this.props.previewLauncher}
@@ -856,8 +854,11 @@ class MainFrame extends React.Component<Props, State> {
setToolbar={this.setEditorToolbar}
onPreview={(project, layout, options) => {
this._launchLayoutPreview(project, layout, options);
if (values.autosaveOnPreview && onAutoSaveProject) {
onAutoSaveProject(project);
if (
values.autosaveOnPreview &&
projectsStorage.onAutoSaveProject
) {
projectsStorage.onAutoSaveProject(project);
}
}}
showPreviewButton={!!this.props.previewLauncher}
@@ -936,6 +937,7 @@ class MainFrame extends React.Component<Props, State> {
};
openExternalLayout = (name: string) => {
const { projectsStorage } = this.props;
this.setState(
{
editorTabs: openEditorTab(this.state.editorTabs, {
@@ -956,9 +958,9 @@ class MainFrame extends React.Component<Props, State> {
);
if (
values.autosaveOnPreview &&
this.props.onAutoSaveProject
projectsStorage.onAutoSaveProject
) {
this.props.onAutoSaveProject(project);
projectsStorage.onAutoSaveProject(project);
}
}}
showPreviewButton={!!this.props.previewLauncher}
@@ -1069,7 +1071,7 @@ class MainFrame extends React.Component<Props, State> {
<StartPage
project={this.state.currentProject}
setToolbar={this.setEditorToolbar}
canOpen={!!this.props.onChooseProject}
canOpen={!!this.props.projectsStorage.onOpenWithPicker}
onOpen={this.chooseProject}
onCreate={() => this.openCreateDialog()}
onOpenProjectManager={() => this.openProjectManager()}
@@ -1191,10 +1193,11 @@ class MainFrame extends React.Component<Props, State> {
};
chooseProject = () => {
if (!this.props.onChooseProject) return;
const { projectsStorage } = this.props;
if (!projectsStorage.onOpenWithPicker) return;
this.props
.onChooseProject()
projectsStorage
.onOpenWithPicker()
.then(filepath => {
if (!filepath) return;
@@ -1210,14 +1213,14 @@ class MainFrame extends React.Component<Props, State> {
const { currentProject } = this.state;
if (!currentProject) return;
const { i18n } = this.props;
const { i18n, projectsStorage } = this.props;
if (this.props.saveDialog) {
this._openSaveDialog();
} else if (this.props.onSaveProject) {
this.props.onSaveProject(currentProject).then(
() => {
this._showSnackMessage(i18n._(t`Project properly saved`));
if (projectsStorage.onSaveProject) {
projectsStorage.onSaveProject(currentProject).then(
(saveDone: boolean) => {
if (saveDone) {
this._showSnackMessage(i18n._(t`Project properly saved`));
}
},
err => {
showErrorBox(
@@ -1236,12 +1239,10 @@ class MainFrame extends React.Component<Props, State> {
const { currentProject } = this.state;
if (!currentProject) return;
const { i18n } = this.props;
const { i18n, projectsStorage } = this.props;
if (this.props.saveDialog) {
this._openSaveDialog();
} else if (this.props.onSaveProjectAs) {
this.props.onSaveProjectAs(currentProject).then(
if (projectsStorage.onSaveProjectAs) {
projectsStorage.onSaveProjectAs(currentProject).then(
saveDone => {
if (saveDone)
this._showSnackMessage(i18n._(t`Project properly saved`));
@@ -1300,12 +1301,6 @@ class MainFrame extends React.Component<Props, State> {
});
};
_openSaveDialog = (open: boolean = true) => {
this.setState({
saveDialogOpen: open,
});
};
_openGenericDialog = (open: boolean = true) => {
this.setState({
genericDialogOpen: open,
@@ -1476,7 +1471,6 @@ class MainFrame extends React.Component<Props, State> {
renderExportDialog,
createDialog,
introDialog,
saveDialog,
resourceSources,
authentification,
previewLauncher,
@@ -1639,12 +1633,6 @@ class MainFrame extends React.Component<Props, State> {
open: this.state.introDialogOpen,
onClose: () => this._openIntroDialog(false),
})}
{!!saveDialog &&
React.cloneElement(saveDialog, {
project: this.state.currentProject,
open: this.state.saveDialogOpen,
onClose: () => this._openSaveDialog(false),
})}
{!!this.state.currentProject &&
this.state.platformSpecificAssetsDialogOpen && (
<PlatformSpecificAssetsDialog

View File

@@ -1,220 +0,0 @@
// This file is generated by update-fixtures-from-resources-examples.js script
import exampleFile0 from '../fixtures/admob/admob.json';
import exampleFile1 from '../fixtures/advanced-shape-based-painter/advanced-shape-based-painter.json';
import exampleFile2 from '../fixtures/animation-speed-scale/animation-speed-scale.json';
import exampleFile3 from '../fixtures/asteroids/asteroids.json';
import exampleFile4 from '../fixtures/basic-ai-with-pathfinding/basic-ai-with-pathfinding.json';
import exampleFile5 from '../fixtures/basic-artificial-intelligence/basic-artificial-intelligence.json';
import exampleFile6 from '../fixtures/basic-topdown-car-driving/basic-topdown-car-driving.json';
import exampleFile7 from '../fixtures/betabox-basics-learning-experience/betabox-basics-learning-experience.json';
import exampleFile8 from '../fixtures/bomb-the-crate/bomb-the-crate.json';
import exampleFile9 from '../fixtures/bouncing-ball-and-rope/bouncing-ball-and-rope.json';
import exampleFile10 from '../fixtures/breakout/breakout.json';
import exampleFile11 from '../fixtures/buttons/buttons.json';
import exampleFile12 from '../fixtures/car-physics/car-physics.json';
import exampleFile13 from '../fixtures/center-object-within-another/center-object-within-another.json';
import exampleFile14 from '../fixtures/change-position-of-object/change-position-of-object.json';
import exampleFile15 from '../fixtures/change-scale-of-sprites/change-scale-of-sprites.json';
import exampleFile16 from '../fixtures/change-sprite-animation/change-sprite-animation.json';
import exampleFile17 from '../fixtures/change-sprite-color/change-sprite-color.json';
import exampleFile18 from '../fixtures/character-selection/character-selection.json';
import exampleFile19 from '../fixtures/create-object-with-mouseclick/create-object-with-mouseclick.json';
import exampleFile20 from '../fixtures/custom-font/custom-font.json';
import exampleFile21 from '../fixtures/custom-mouse-pointer/custom-mouse-pointer.json';
import exampleFile22 from '../fixtures/customize-keys-with-lastpressedkey/customize-keys-with-lastpressedkey.json';
import exampleFile23 from '../fixtures/device-orientation-ballgame/device-orientation-ballgame.json';
import exampleFile24 from '../fixtures/device-orientation-compass/device-orientation-compass.json';
import exampleFile25 from '../fixtures/device-vibration/device-vibration.json';
import exampleFile26 from '../fixtures/dialogue-tree-with-yarn/dialogue-tree-with-yarn.json';
import exampleFile27 from '../fixtures/downhill-bike-physics-demo/downhill-bike-physics-demo.json';
import exampleFile28 from '../fixtures/drag-camera-with-mouse/drag-camera-with-mouse.json';
import exampleFile29 from '../fixtures/drop-collect-items-from-storage/drop-collect-items-from-storage.json';
import exampleFile30 from '../fixtures/endless-up-runner/endless-up-runner.json';
import exampleFile31 from '../fixtures/exit-app/exit-app.json';
import exampleFile32 from '../fixtures/facebook-instant-game/facebook-instant-game.json';
import exampleFile33 from '../fixtures/filesystem-create-directory/filesystem-create-directory.json';
import exampleFile34 from '../fixtures/find-diagonals/find-diagonals.json';
import exampleFile35 from '../fixtures/geodash/geodash.json';
import exampleFile36 from '../fixtures/health-bar/health-bar.json';
import exampleFile37 from '../fixtures/infinite-scrolling-background/infinite-scrolling-background.json';
import exampleFile38 from '../fixtures/inventory-system/inventory-system.json';
import exampleFile39 from '../fixtures/isometric-game/isometric-game.json';
import exampleFile40 from '../fixtures/javascript-blocks-in-platformer/javascript-blocks-in-platformer.json';
import exampleFile41 from '../fixtures/keyboard-practice/keyboard-practice.json';
import exampleFile42 from '../fixtures/level-editor/level-editor.json';
import exampleFile43 from '../fixtures/load-image-from-url/load-image-from-url.json';
import exampleFile44 from '../fixtures/magnet/magnet.json';
import exampleFile45 from '../fixtures/manipulate-text-object/manipulate-text-object.json';
import exampleFile46 from '../fixtures/menu-with-functions-and-text-effects/menu-with-functions-and-text-effects.json';
import exampleFile47 from '../fixtures/move-camera-to-position/move-camera-to-position.json';
import exampleFile48 from '../fixtures/move-object-back-and-forth/move-object-back-and-forth.json';
import exampleFile49 from '../fixtures/move-object-in-circle/move-object-in-circle.json';
import exampleFile50 from '../fixtures/move-object-toward-position/move-object-toward-position.json';
import exampleFile51 from '../fixtures/move-object-with-mouse-joint/move-object-with-mouse-joint.json';
import exampleFile52 from '../fixtures/move-object-with-physics/move-object-with-physics.json';
import exampleFile53 from '../fixtures/multiplayer-platformer-with-gamepads/multiplayer-platformer-with-gamepads.json';
import exampleFile54 from '../fixtures/multitouch/multitouch.json';
import exampleFile55 from '../fixtures/object-gravity/object-gravity.json';
import exampleFile56 from '../fixtures/object-selection/object-selection.json';
import exampleFile57 from '../fixtures/objects-timers/objects-timers.json';
import exampleFile58 from '../fixtures/open-url-in-browser/open-url-in-browser.json';
import exampleFile59 from '../fixtures/pairs/pairs.json';
import exampleFile60 from '../fixtures/parallax/parallax.json';
import exampleFile61 from '../fixtures/parallax-scrolling/parallax-scrolling.json';
import exampleFile62 from '../fixtures/parse-json-from-api/parse-json-from-api.json';
import exampleFile63 from '../fixtures/parse-json-string/parse-json-string.json';
import exampleFile64 from '../fixtures/particles-explosions/particles-explosions.json';
import exampleFile65 from '../fixtures/particles-various-effects/particles-various-effects.json';
import exampleFile66 from '../fixtures/pathfinding/pathfinding.json';
import exampleFile67 from '../fixtures/pathfinding-basics/pathfinding-basics.json';
import exampleFile68 from '../fixtures/physics/physics.json';
import exampleFile69 from '../fixtures/physics-joints-demo/physics-joints-demo.json';
import exampleFile70 from '../fixtures/physics-joints-settings-demo/physics-joints-settings-demo.json';
import exampleFile71 from '../fixtures/pin-object-to-another/pin-object-to-another.json';
import exampleFile72 from '../fixtures/pin-object-to-another-multiple-parents/pin-object-to-another-multiple-parents.json';
import exampleFile73 from '../fixtures/pixel-perfect-platform-game/pixel-perfect-platform-game.json';
import exampleFile74 from '../fixtures/plane-and-clouds/plane-and-clouds.json';
import exampleFile75 from '../fixtures/platformer/platformer.json';
import exampleFile76 from '../fixtures/platformer-double-jump/platformer-double-jump.json';
import exampleFile77 from '../fixtures/play-music-on-mobile/play-music-on-mobile.json';
import exampleFile78 from '../fixtures/play-stop-sprite-animation/play-stop-sprite-animation.json';
import exampleFile79 from '../fixtures/racing-game/racing-game.json';
import exampleFile80 from '../fixtures/ragdoll/ragdoll.json';
import exampleFile81 from '../fixtures/rain/rain.json';
import exampleFile82 from '../fixtures/random-color-picker/random-color-picker.json';
import exampleFile83 from '../fixtures/rotate-toward-mouse/rotate-toward-mouse.json';
import exampleFile84 from '../fixtures/rotate-toward-position/rotate-toward-position.json';
import exampleFile85 from '../fixtures/rotate-with-keypress/rotate-with-keypress.json';
import exampleFile86 from '../fixtures/save-load/save-load.json';
import exampleFile87 from '../fixtures/screen-shake/screen-shake.json';
import exampleFile88 from '../fixtures/shoot-bullet-in-parabola/shoot-bullet-in-parabola.json';
import exampleFile89 from '../fixtures/shoot-bullets/shoot-bullets.json';
import exampleFile90 from '../fixtures/shooting-bullets-explanation/shooting-bullets-explanation.json';
import exampleFile91 from '../fixtures/simple-space-shooter/simple-space-shooter.json';
import exampleFile92 from '../fixtures/skeletal-animation-demo/skeletal-animation-demo.json';
import exampleFile93 from '../fixtures/snap-object-to-grid/snap-object-to-grid.json';
import exampleFile94 from '../fixtures/space-invaders/space-invaders.json';
import exampleFile95 from '../fixtures/space-shooter/space-shooter.json';
import exampleFile96 from '../fixtures/splash-screen/splash-screen.json';
import exampleFile97 from '../fixtures/sprite-fade-in-out/sprite-fade-in-out.json';
import exampleFile98 from '../fixtures/take-screenshot/take-screenshot.json';
import exampleFile99 from '../fixtures/text-entry-object/text-entry-object.json';
import exampleFile100 from '../fixtures/text-fade-in-out/text-fade-in-out.json';
import exampleFile101 from '../fixtures/text-to-speech/text-to-speech.json';
import exampleFile102 from '../fixtures/toggle-music-play-sound/toggle-music-play-sound.json';
import exampleFile103 from '../fixtures/type-on-text-effect/type-on-text-effect.json';
import exampleFile104 from '../fixtures/video-player/video-player.json';
import exampleFile105 from '../fixtures/z-depth/z-depth.json';
import exampleFile106 from '../fixtures/zombie-laser/zombie-laser.json';
// prettier-ignore
export default {
'example://admob': exampleFile0,
'example://advanced-shape-based-painter': exampleFile1,
'example://animation-speed-scale': exampleFile2,
'example://asteroids': exampleFile3,
'example://basic-ai-with-pathfinding': exampleFile4,
'example://basic-artificial-intelligence': exampleFile5,
'example://basic-topdown-car-driving': exampleFile6,
'example://betabox-basics-learning-experience': exampleFile7,
'example://bomb-the-crate': exampleFile8,
'example://bouncing-ball-and-rope': exampleFile9,
'example://breakout': exampleFile10,
'example://buttons': exampleFile11,
'example://car-physics': exampleFile12,
'example://center-object-within-another': exampleFile13,
'example://change-position-of-object': exampleFile14,
'example://change-scale-of-sprites': exampleFile15,
'example://change-sprite-animation': exampleFile16,
'example://change-sprite-color': exampleFile17,
'example://character-selection': exampleFile18,
'example://create-object-with-mouseclick': exampleFile19,
'example://custom-font': exampleFile20,
'example://custom-mouse-pointer': exampleFile21,
'example://customize-keys-with-lastpressedkey': exampleFile22,
'example://device-orientation-ballgame': exampleFile23,
'example://device-orientation-compass': exampleFile24,
'example://device-vibration': exampleFile25,
'example://dialogue-tree-with-yarn': exampleFile26,
'example://downhill-bike-physics-demo': exampleFile27,
'example://drag-camera-with-mouse': exampleFile28,
'example://drop-collect-items-from-storage': exampleFile29,
'example://endless-up-runner': exampleFile30,
'example://exit-app': exampleFile31,
'example://facebook-instant-game': exampleFile32,
'example://filesystem-create-directory': exampleFile33,
'example://find-diagonals': exampleFile34,
'example://geodash': exampleFile35,
'example://health-bar': exampleFile36,
'example://infinite-scrolling-background': exampleFile37,
'example://inventory-system': exampleFile38,
'example://isometric-game': exampleFile39,
'example://javascript-blocks-in-platformer': exampleFile40,
'example://keyboard-practice': exampleFile41,
'example://level-editor': exampleFile42,
'example://load-image-from-url': exampleFile43,
'example://magnet': exampleFile44,
'example://manipulate-text-object': exampleFile45,
'example://menu-with-functions-and-text-effects': exampleFile46,
'example://move-camera-to-position': exampleFile47,
'example://move-object-back-and-forth': exampleFile48,
'example://move-object-in-circle': exampleFile49,
'example://move-object-toward-position': exampleFile50,
'example://move-object-with-mouse-joint': exampleFile51,
'example://move-object-with-physics': exampleFile52,
'example://multiplayer-platformer-with-gamepads': exampleFile53,
'example://multitouch': exampleFile54,
'example://object-gravity': exampleFile55,
'example://object-selection': exampleFile56,
'example://objects-timers': exampleFile57,
'example://open-url-in-browser': exampleFile58,
'example://pairs': exampleFile59,
'example://parallax': exampleFile60,
'example://parallax-scrolling': exampleFile61,
'example://parse-json-from-api': exampleFile62,
'example://parse-json-string': exampleFile63,
'example://particles-explosions': exampleFile64,
'example://particles-various-effects': exampleFile65,
'example://pathfinding': exampleFile66,
'example://pathfinding-basics': exampleFile67,
'example://physics': exampleFile68,
'example://physics-joints-demo': exampleFile69,
'example://physics-joints-settings-demo': exampleFile70,
'example://pin-object-to-another': exampleFile71,
'example://pin-object-to-another-multiple-parents': exampleFile72,
'example://pixel-perfect-platform-game': exampleFile73,
'example://plane-and-clouds': exampleFile74,
'example://platformer': exampleFile75,
'example://platformer-double-jump': exampleFile76,
'example://play-music-on-mobile': exampleFile77,
'example://play-stop-sprite-animation': exampleFile78,
'example://racing-game': exampleFile79,
'example://ragdoll': exampleFile80,
'example://rain': exampleFile81,
'example://random-color-picker': exampleFile82,
'example://rotate-toward-mouse': exampleFile83,
'example://rotate-toward-position': exampleFile84,
'example://rotate-with-keypress': exampleFile85,
'example://save-load': exampleFile86,
'example://screen-shake': exampleFile87,
'example://shoot-bullet-in-parabola': exampleFile88,
'example://shoot-bullets': exampleFile89,
'example://shooting-bullets-explanation': exampleFile90,
'example://simple-space-shooter': exampleFile91,
'example://skeletal-animation-demo': exampleFile92,
'example://snap-object-to-grid': exampleFile93,
'example://space-invaders': exampleFile94,
'example://space-shooter': exampleFile95,
'example://splash-screen': exampleFile96,
'example://sprite-fade-in-out': exampleFile97,
'example://take-screenshot': exampleFile98,
'example://text-entry-object': exampleFile99,
'example://text-fade-in-out': exampleFile100,
'example://text-to-speech': exampleFile101,
'example://toggle-music-play-sound': exampleFile102,
'example://type-on-text-effect': exampleFile103,
'example://video-player': exampleFile104,
'example://z-depth': exampleFile105,
'example://zombie-laser': exampleFile106,
};

View File

@@ -1,10 +0,0 @@
import browserExampleFiles from './BrowserExampleFiles';
export default class BrowserProjectOpener {
static readInternalFile(url) {
if (browserExampleFiles[url])
return Promise.resolve(browserExampleFiles[url]);
return Promise.reject(`Unknown built-in game with URL ${url}`);
}
}

View File

@@ -1,15 +1,21 @@
// @flow
import { Trans } from '@lingui/macro';
import React, { Component } from 'react';
import Dialog from '../UI/Dialog';
import FlatButton from '../UI/FlatButton';
import RaisedButton from '../UI/RaisedButton';
import { Column, Line } from '../UI/Grid';
import Window from '../Utils/Window';
import { serializeToJSObject } from '../Utils/Serializer';
import { showErrorBox } from '../UI/Messages/MessageBox';
import Text from '../UI/Text';
import * as React from 'react';
import Dialog from '../../UI/Dialog';
import FlatButton from '../../UI/FlatButton';
import RaisedButton from '../../UI/RaisedButton';
import { Column, Line } from '../../UI/Grid';
import Window from '../../Utils/Window';
import { serializeToJSObject } from '../../Utils/Serializer';
import { showErrorBox } from '../../UI/Messages/MessageBox';
import Text from '../../UI/Text';
export default class BrowserSaveDialog extends Component {
type Props = {|
project: gdProject,
onDone: () => void,
|};
export default class DownloadSaveDialog extends React.Component<Props> {
_download = () => {
let content = '';
try {
@@ -24,14 +30,16 @@ export default class BrowserSaveDialog extends Component {
downloadLink.href = uri;
downloadLink.download = 'game.json';
document.body.appendChild(downloadLink);
const { body } = document;
if (!body) return;
body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
body.removeChild(downloadLink);
};
render() {
const { open, onClose, project } = this.props;
if (!open || !project) return null;
const { onDone } = this.props;
const actions = [
<FlatButton
@@ -44,12 +52,12 @@ export default class BrowserSaveDialog extends Component {
key="close"
label={<Trans>Close</Trans>}
primary={false}
onClick={onClose}
onClick={onDone}
/>,
];
return (
<Dialog actions={actions} open={open} onRequestClose={onClose}>
<Dialog actions={actions} open onRequestClose={onDone}>
<Column noMargin>
<Line>
<Text>

View File

@@ -0,0 +1,221 @@
// @flow
// This file is generated by update-fixtures-from-resources-examples.js script
import exampleFile0 from '../../fixtures/admob/admob.json';
import exampleFile1 from '../../fixtures/advanced-shape-based-painter/advanced-shape-based-painter.json';
import exampleFile2 from '../../fixtures/animation-speed-scale/animation-speed-scale.json';
import exampleFile3 from '../../fixtures/asteroids/asteroids.json';
import exampleFile4 from '../../fixtures/basic-ai-with-pathfinding/basic-ai-with-pathfinding.json';
import exampleFile5 from '../../fixtures/basic-artificial-intelligence/basic-artificial-intelligence.json';
import exampleFile6 from '../../fixtures/basic-topdown-car-driving/basic-topdown-car-driving.json';
import exampleFile7 from '../../fixtures/betabox-basics-learning-experience/betabox-basics-learning-experience.json';
import exampleFile8 from '../../fixtures/bomb-the-crate/bomb-the-crate.json';
import exampleFile9 from '../../fixtures/bouncing-ball-and-rope/bouncing-ball-and-rope.json';
import exampleFile10 from '../../fixtures/breakout/breakout.json';
import exampleFile11 from '../../fixtures/buttons/buttons.json';
import exampleFile12 from '../../fixtures/car-physics/car-physics.json';
import exampleFile13 from '../../fixtures/center-object-within-another/center-object-within-another.json';
import exampleFile14 from '../../fixtures/change-position-of-object/change-position-of-object.json';
import exampleFile15 from '../../fixtures/change-scale-of-sprites/change-scale-of-sprites.json';
import exampleFile16 from '../../fixtures/change-sprite-animation/change-sprite-animation.json';
import exampleFile17 from '../../fixtures/change-sprite-color/change-sprite-color.json';
import exampleFile18 from '../../fixtures/character-selection/character-selection.json';
import exampleFile19 from '../../fixtures/create-object-with-mouseclick/create-object-with-mouseclick.json';
import exampleFile20 from '../../fixtures/custom-font/custom-font.json';
import exampleFile21 from '../../fixtures/custom-mouse-pointer/custom-mouse-pointer.json';
import exampleFile22 from '../../fixtures/customize-keys-with-lastpressedkey/customize-keys-with-lastpressedkey.json';
import exampleFile23 from '../../fixtures/device-orientation-ballgame/device-orientation-ballgame.json';
import exampleFile24 from '../../fixtures/device-orientation-compass/device-orientation-compass.json';
import exampleFile25 from '../../fixtures/device-vibration/device-vibration.json';
import exampleFile26 from '../../fixtures/dialogue-tree-with-yarn/dialogue-tree-with-yarn.json';
import exampleFile27 from '../../fixtures/downhill-bike-physics-demo/downhill-bike-physics-demo.json';
import exampleFile28 from '../../fixtures/drag-camera-with-mouse/drag-camera-with-mouse.json';
import exampleFile29 from '../../fixtures/drop-collect-items-from-storage/drop-collect-items-from-storage.json';
import exampleFile30 from '../../fixtures/endless-up-runner/endless-up-runner.json';
import exampleFile31 from '../../fixtures/exit-app/exit-app.json';
import exampleFile32 from '../../fixtures/facebook-instant-game/facebook-instant-game.json';
import exampleFile33 from '../../fixtures/filesystem-create-directory/filesystem-create-directory.json';
import exampleFile34 from '../../fixtures/find-diagonals/find-diagonals.json';
import exampleFile35 from '../../fixtures/geodash/geodash.json';
import exampleFile36 from '../../fixtures/health-bar/health-bar.json';
import exampleFile37 from '../../fixtures/infinite-scrolling-background/infinite-scrolling-background.json';
import exampleFile38 from '../../fixtures/inventory-system/inventory-system.json';
import exampleFile39 from '../../fixtures/isometric-game/isometric-game.json';
import exampleFile40 from '../../fixtures/javascript-blocks-in-platformer/javascript-blocks-in-platformer.json';
import exampleFile41 from '../../fixtures/keyboard-practice/keyboard-practice.json';
import exampleFile42 from '../../fixtures/level-editor/level-editor.json';
import exampleFile43 from '../../fixtures/load-image-from-url/load-image-from-url.json';
import exampleFile44 from '../../fixtures/magnet/magnet.json';
import exampleFile45 from '../../fixtures/manipulate-text-object/manipulate-text-object.json';
import exampleFile46 from '../../fixtures/menu-with-functions-and-text-effects/menu-with-functions-and-text-effects.json';
import exampleFile47 from '../../fixtures/move-camera-to-position/move-camera-to-position.json';
import exampleFile48 from '../../fixtures/move-object-back-and-forth/move-object-back-and-forth.json';
import exampleFile49 from '../../fixtures/move-object-in-circle/move-object-in-circle.json';
import exampleFile50 from '../../fixtures/move-object-toward-position/move-object-toward-position.json';
import exampleFile51 from '../../fixtures/move-object-with-mouse-joint/move-object-with-mouse-joint.json';
import exampleFile52 from '../../fixtures/move-object-with-physics/move-object-with-physics.json';
import exampleFile53 from '../../fixtures/multiplayer-platformer-with-gamepads/multiplayer-platformer-with-gamepads.json';
import exampleFile54 from '../../fixtures/multitouch/multitouch.json';
import exampleFile55 from '../../fixtures/object-gravity/object-gravity.json';
import exampleFile56 from '../../fixtures/object-selection/object-selection.json';
import exampleFile57 from '../../fixtures/objects-timers/objects-timers.json';
import exampleFile58 from '../../fixtures/open-url-in-browser/open-url-in-browser.json';
import exampleFile59 from '../../fixtures/pairs/pairs.json';
import exampleFile60 from '../../fixtures/parallax/parallax.json';
import exampleFile61 from '../../fixtures/parallax-scrolling/parallax-scrolling.json';
import exampleFile62 from '../../fixtures/parse-json-from-api/parse-json-from-api.json';
import exampleFile63 from '../../fixtures/parse-json-string/parse-json-string.json';
import exampleFile64 from '../../fixtures/particles-explosions/particles-explosions.json';
import exampleFile65 from '../../fixtures/particles-various-effects/particles-various-effects.json';
import exampleFile66 from '../../fixtures/pathfinding/pathfinding.json';
import exampleFile67 from '../../fixtures/pathfinding-basics/pathfinding-basics.json';
import exampleFile68 from '../../fixtures/physics/physics.json';
import exampleFile69 from '../../fixtures/physics-joints-demo/physics-joints-demo.json';
import exampleFile70 from '../../fixtures/physics-joints-settings-demo/physics-joints-settings-demo.json';
import exampleFile71 from '../../fixtures/pin-object-to-another/pin-object-to-another.json';
import exampleFile72 from '../../fixtures/pin-object-to-another-multiple-parents/pin-object-to-another-multiple-parents.json';
import exampleFile73 from '../../fixtures/pixel-perfect-platform-game/pixel-perfect-platform-game.json';
import exampleFile74 from '../../fixtures/plane-and-clouds/plane-and-clouds.json';
import exampleFile75 from '../../fixtures/platformer/platformer.json';
import exampleFile76 from '../../fixtures/platformer-double-jump/platformer-double-jump.json';
import exampleFile77 from '../../fixtures/play-music-on-mobile/play-music-on-mobile.json';
import exampleFile78 from '../../fixtures/play-stop-sprite-animation/play-stop-sprite-animation.json';
import exampleFile79 from '../../fixtures/racing-game/racing-game.json';
import exampleFile80 from '../../fixtures/ragdoll/ragdoll.json';
import exampleFile81 from '../../fixtures/rain/rain.json';
import exampleFile82 from '../../fixtures/random-color-picker/random-color-picker.json';
import exampleFile83 from '../../fixtures/rotate-toward-mouse/rotate-toward-mouse.json';
import exampleFile84 from '../../fixtures/rotate-toward-position/rotate-toward-position.json';
import exampleFile85 from '../../fixtures/rotate-with-keypress/rotate-with-keypress.json';
import exampleFile86 from '../../fixtures/save-load/save-load.json';
import exampleFile87 from '../../fixtures/screen-shake/screen-shake.json';
import exampleFile88 from '../../fixtures/shoot-bullet-in-parabola/shoot-bullet-in-parabola.json';
import exampleFile89 from '../../fixtures/shoot-bullets/shoot-bullets.json';
import exampleFile90 from '../../fixtures/shooting-bullets-explanation/shooting-bullets-explanation.json';
import exampleFile91 from '../../fixtures/simple-space-shooter/simple-space-shooter.json';
import exampleFile92 from '../../fixtures/skeletal-animation-demo/skeletal-animation-demo.json';
import exampleFile93 from '../../fixtures/snap-object-to-grid/snap-object-to-grid.json';
import exampleFile94 from '../../fixtures/space-invaders/space-invaders.json';
import exampleFile95 from '../../fixtures/space-shooter/space-shooter.json';
import exampleFile96 from '../../fixtures/splash-screen/splash-screen.json';
import exampleFile97 from '../../fixtures/sprite-fade-in-out/sprite-fade-in-out.json';
import exampleFile98 from '../../fixtures/take-screenshot/take-screenshot.json';
import exampleFile99 from '../../fixtures/text-entry-object/text-entry-object.json';
import exampleFile100 from '../../fixtures/text-fade-in-out/text-fade-in-out.json';
import exampleFile101 from '../../fixtures/text-to-speech/text-to-speech.json';
import exampleFile102 from '../../fixtures/toggle-music-play-sound/toggle-music-play-sound.json';
import exampleFile103 from '../../fixtures/type-on-text-effect/type-on-text-effect.json';
import exampleFile104 from '../../fixtures/video-player/video-player.json';
import exampleFile105 from '../../fixtures/z-depth/z-depth.json';
import exampleFile106 from '../../fixtures/zombie-laser/zombie-laser.json';
// prettier-ignore
export default {
'example://admob': exampleFile0,
'example://advanced-shape-based-painter': exampleFile1,
'example://animation-speed-scale': exampleFile2,
'example://asteroids': exampleFile3,
'example://basic-ai-with-pathfinding': exampleFile4,
'example://basic-artificial-intelligence': exampleFile5,
'example://basic-topdown-car-driving': exampleFile6,
'example://betabox-basics-learning-experience': exampleFile7,
'example://bomb-the-crate': exampleFile8,
'example://bouncing-ball-and-rope': exampleFile9,
'example://breakout': exampleFile10,
'example://buttons': exampleFile11,
'example://car-physics': exampleFile12,
'example://center-object-within-another': exampleFile13,
'example://change-position-of-object': exampleFile14,
'example://change-scale-of-sprites': exampleFile15,
'example://change-sprite-animation': exampleFile16,
'example://change-sprite-color': exampleFile17,
'example://character-selection': exampleFile18,
'example://create-object-with-mouseclick': exampleFile19,
'example://custom-font': exampleFile20,
'example://custom-mouse-pointer': exampleFile21,
'example://customize-keys-with-lastpressedkey': exampleFile22,
'example://device-orientation-ballgame': exampleFile23,
'example://device-orientation-compass': exampleFile24,
'example://device-vibration': exampleFile25,
'example://dialogue-tree-with-yarn': exampleFile26,
'example://downhill-bike-physics-demo': exampleFile27,
'example://drag-camera-with-mouse': exampleFile28,
'example://drop-collect-items-from-storage': exampleFile29,
'example://endless-up-runner': exampleFile30,
'example://exit-app': exampleFile31,
'example://facebook-instant-game': exampleFile32,
'example://filesystem-create-directory': exampleFile33,
'example://find-diagonals': exampleFile34,
'example://geodash': exampleFile35,
'example://health-bar': exampleFile36,
'example://infinite-scrolling-background': exampleFile37,
'example://inventory-system': exampleFile38,
'example://isometric-game': exampleFile39,
'example://javascript-blocks-in-platformer': exampleFile40,
'example://keyboard-practice': exampleFile41,
'example://level-editor': exampleFile42,
'example://load-image-from-url': exampleFile43,
'example://magnet': exampleFile44,
'example://manipulate-text-object': exampleFile45,
'example://menu-with-functions-and-text-effects': exampleFile46,
'example://move-camera-to-position': exampleFile47,
'example://move-object-back-and-forth': exampleFile48,
'example://move-object-in-circle': exampleFile49,
'example://move-object-toward-position': exampleFile50,
'example://move-object-with-mouse-joint': exampleFile51,
'example://move-object-with-physics': exampleFile52,
'example://multiplayer-platformer-with-gamepads': exampleFile53,
'example://multitouch': exampleFile54,
'example://object-gravity': exampleFile55,
'example://object-selection': exampleFile56,
'example://objects-timers': exampleFile57,
'example://open-url-in-browser': exampleFile58,
'example://pairs': exampleFile59,
'example://parallax': exampleFile60,
'example://parallax-scrolling': exampleFile61,
'example://parse-json-from-api': exampleFile62,
'example://parse-json-string': exampleFile63,
'example://particles-explosions': exampleFile64,
'example://particles-various-effects': exampleFile65,
'example://pathfinding': exampleFile66,
'example://pathfinding-basics': exampleFile67,
'example://physics': exampleFile68,
'example://physics-joints-demo': exampleFile69,
'example://physics-joints-settings-demo': exampleFile70,
'example://pin-object-to-another': exampleFile71,
'example://pin-object-to-another-multiple-parents': exampleFile72,
'example://pixel-perfect-platform-game': exampleFile73,
'example://plane-and-clouds': exampleFile74,
'example://platformer': exampleFile75,
'example://platformer-double-jump': exampleFile76,
'example://play-music-on-mobile': exampleFile77,
'example://play-stop-sprite-animation': exampleFile78,
'example://racing-game': exampleFile79,
'example://ragdoll': exampleFile80,
'example://rain': exampleFile81,
'example://random-color-picker': exampleFile82,
'example://rotate-toward-mouse': exampleFile83,
'example://rotate-toward-position': exampleFile84,
'example://rotate-with-keypress': exampleFile85,
'example://save-load': exampleFile86,
'example://screen-shake': exampleFile87,
'example://shoot-bullet-in-parabola': exampleFile88,
'example://shoot-bullets': exampleFile89,
'example://shooting-bullets-explanation': exampleFile90,
'example://simple-space-shooter': exampleFile91,
'example://skeletal-animation-demo': exampleFile92,
'example://snap-object-to-grid': exampleFile93,
'example://space-invaders': exampleFile94,
'example://space-shooter': exampleFile95,
'example://splash-screen': exampleFile96,
'example://sprite-fade-in-out': exampleFile97,
'example://take-screenshot': exampleFile98,
'example://text-entry-object': exampleFile99,
'example://text-fade-in-out': exampleFile100,
'example://text-to-speech': exampleFile101,
'example://toggle-music-play-sound': exampleFile102,
'example://type-on-text-effect': exampleFile103,
'example://video-player': exampleFile104,
'example://z-depth': exampleFile105,
'example://zombie-laser': exampleFile106,
};

View File

@@ -0,0 +1,31 @@
// @flow
import * as React from 'react';
import { type StorageProvider } from '../index';
import internalExampleFiles from './InternalExampleFiles';
import DownloadSaveDialog from './DownloadSaveDialog';
/**
* "Internal" storage giving access to embedded examples.
* Used for the web-app.
*/
export default (({ setDialog, closeDialog }) => ({
onOpen: (url: string) => {
if (internalExampleFiles[url])
return Promise.resolve(internalExampleFiles[url]);
return Promise.reject(new Error(`Unknown built-in game with URL ${url}`));
},
onSaveProject: (project: gdProject) => {
return new Promise(resolve => {
setDialog(() => (
<DownloadSaveDialog
onDone={() => {
closeDialog();
resolve(false);
}}
project={project}
/>
));
});
},
}): StorageProvider);

View File

@@ -0,0 +1,89 @@
// @flow
import optionalRequire from '../../Utils/OptionalRequire.js';
import { unsplit } from '../../Utils/ObjectSplitter.js';
const fs = optionalRequire('fs');
const path = optionalRequire('path');
const electron = optionalRequire('electron');
const dialog = electron ? electron.remote.dialog : null;
const readJSONFile = (filepath: string): Promise<Object> => {
if (!fs) return Promise.reject('Filesystem is not supported.');
return new Promise((resolve, reject) => {
fs.readFile(filepath, { encoding: 'utf8' }, (err, data) => {
if (err) return reject(err);
try {
const dataObject = JSON.parse(data);
return resolve(dataObject);
} catch (ex) {
return reject(filepath + ' is a corrupted/malformed file.');
}
});
});
};
export const onOpenWithPicker = (): Promise<?string> => {
return new Promise((resolve, reject) => {
if (!dialog) return reject('Not supported');
const browserWindow = electron.remote.getCurrentWindow();
dialog.showOpenDialog(
browserWindow,
{
title: 'Open a project',
properties: ['openFile'],
message:
'If you want to open your GDevelop 4 project, be sure to save it as a .json file',
filters: [{ name: 'GDevelop 5 project', extensions: ['json'] }],
},
paths => {
if (!paths || !paths.length) return resolve(null);
return resolve(paths[0]);
}
);
});
};
export const onOpen = (filepath: string): Object => {
const projectPath = path.dirname(filepath);
return readJSONFile(filepath).then(object => {
return unsplit(object, {
getReferencePartialObject: referencePath => {
return readJSONFile(path.join(projectPath, referencePath) + '.json');
},
isReferenceMagicPropertyName: '__REFERENCE_TO_SPLIT_OBJECT',
// Limit unsplitting to depth 3 (which would allow properties of layouts/external layouts/external events
// to be un-splitted, but not the content of these properties), to avoid very slow processing
// of large game files.
maxUnsplitDepth: 3,
}).then(() => {
return object;
});
});
};
export const shouldOpenAutosave = (
filePath: string,
autoSavePath: string,
compareLastModified: boolean
): boolean => {
if (fs.existsSync(autoSavePath)) {
if (!compareLastModified) {
return true;
}
try {
const autoSavedTime = fs.statSync(autoSavePath).mtime.getTime();
const saveTime = fs.statSync(filePath).mtime.getTime();
if (autoSavedTime > saveTime) {
return true;
}
} catch (err) {
console.error('Unable to compare *.autosave to project', err);
return false;
}
return false;
}
return false;
};

View File

@@ -1,12 +1,12 @@
// @flow
import { serializeToJSObject } from '../Utils/Serializer';
import optionalRequire from '../Utils/OptionalRequire.js';
import { serializeToJSObject } from '../../Utils/Serializer';
import optionalRequire from '../../Utils/OptionalRequire.js';
import {
split,
splitPaths,
getSlugifiedUniqueNameFromProperty,
} from '../Utils/ObjectSplitter';
import localFileSystem from '../Export/LocalExporters/LocalFileSystem';
} from '../../Utils/ObjectSplitter';
import localFileSystem from '../../Export/LocalExporters/LocalFileSystem';
import assignIn from 'lodash/assignIn';
const gd = global.gd;
@@ -85,62 +85,60 @@ const writeProjectFiles = (
}
};
export default class LocalProjectWriter {
static saveProject = (project: gdProject): Promise<boolean> => {
const filePath = project.getProjectFile();
const projectPath = path.dirname(project.getProjectFile());
if (!filePath) {
return Promise.reject(
'Project file is empty, "Save as" should have been called?'
);
}
return writeProjectFiles(project, filePath, projectPath).then(() => {
return true; // Save was properly done
});
};
static saveProjectAs = (project: gdProject): Promise<boolean> => {
const defaultPath = project.getProjectFile();
const fileSystem = assignIn(new gd.AbstractFileSystemJS(), localFileSystem);
const browserWindow = electron.remote.getCurrentWindow();
const options = {
defaultPath,
filters: [{ name: 'GDevelop 5 project', extensions: ['json'] }],
};
if (!dialog) {
return Promise.reject('Unsupported');
}
const filePath = dialog.showSaveDialog(browserWindow, options);
if (!filePath) {
return Promise.resolve(false); // Nothing was saved.
}
const projectPath = path.dirname(filePath);
// TODO: Ideally, errors while copying resources should be reported.
gd.ProjectResourcesCopier.copyAllResourcesTo(
project,
fileSystem,
projectPath,
true, // Update the project with the new resource paths
false, // Don't move absolute files
true // Keep relative files folders structure.
export const onSaveProject = (project: gdProject): Promise<boolean> => {
const filePath = project.getProjectFile();
const projectPath = path.dirname(project.getProjectFile());
if (!filePath) {
return Promise.reject(
'Project file is empty, "Save as" should have been called?'
);
}
// Update the project with the new file path (resources have already been updated)
project.setProjectFile(filePath);
return writeProjectFiles(project, filePath, projectPath).then(() => {
return true; // Save was properly done
});
};
return writeProjectFiles(project, filePath, projectPath).then(() => {
return true; // Save was properly done
});
export const onSaveProjectAs = (project: gdProject): Promise<boolean> => {
const defaultPath = project.getProjectFile();
const fileSystem = assignIn(new gd.AbstractFileSystemJS(), localFileSystem);
const browserWindow = electron.remote.getCurrentWindow();
const options = {
defaultPath,
filters: [{ name: 'GDevelop 5 project', extensions: ['json'] }],
};
static autoSaveProject = (project: gdProject) => {
const autoSavePath = project.getProjectFile() + '.autosave';
writeJSONFile(serializeToJSObject(project), autoSavePath).catch(err => {
console.error(`Unable to write ${autoSavePath}:`, err);
throw err;
});
};
}
if (!dialog) {
return Promise.reject('Unsupported');
}
const filePath = dialog.showSaveDialog(browserWindow, options);
if (!filePath) {
return Promise.resolve(false); // Nothing was saved.
}
const projectPath = path.dirname(filePath);
// TODO: Ideally, errors while copying resources should be reported.
gd.ProjectResourcesCopier.copyAllResourcesTo(
project,
fileSystem,
projectPath,
true, // Update the project with the new resource paths
false, // Don't move absolute files
true // Keep relative files folders structure.
);
// Update the project with the new file path (resources have already been updated)
project.setProjectFile(filePath);
return writeProjectFiles(project, filePath, projectPath).then(() => {
return true; // Save was properly done
});
};
export const onAutoSaveProject = (project: gdProject) => {
const autoSavePath = project.getProjectFile() + '.autosave';
writeJSONFile(serializeToJSObject(project), autoSavePath).catch(err => {
console.error(`Unable to write ${autoSavePath}:`, err);
throw err;
});
};

View File

@@ -0,0 +1,25 @@
// @flow
import { type StorageProvider } from '../index';
import {
onOpenWithPicker,
onOpen,
shouldOpenAutosave,
} from './LocalProjectOpener';
import {
onSaveProject,
onSaveProjectAs,
onAutoSaveProject,
} from './LocalProjectWriter';
/**
* Use the Electron APIs to provide access to the native
* file system (with native save/open dialogs).
*/
export default (() => ({
onOpenWithPicker,
onOpen,
shouldOpenAutosave,
onSaveProject,
onSaveProjectAs,
onAutoSaveProject,
}): StorageProvider);

View File

@@ -1,91 +0,0 @@
// @flow
import optionalRequire from '../Utils/OptionalRequire.js';
import { unsplit } from '../Utils/ObjectSplitter.js';
const fs = optionalRequire('fs');
const path = optionalRequire('path');
const electron = optionalRequire('electron');
const dialog = electron ? electron.remote.dialog : null;
const readJSONFile = (filepath: string): Promise<Object> => {
if (!fs) return Promise.reject('Filesystem is not supported.');
return new Promise((resolve, reject) => {
fs.readFile(filepath, { encoding: 'utf8' }, (err, data) => {
if (err) return reject(err);
try {
const dataObject = JSON.parse(data);
return resolve(dataObject);
} catch (ex) {
return reject(filepath + ' is a corrupted/malformed file.');
}
});
});
};
export default class LocalProjectOpener {
static chooseProjectFile = (): Promise<?string> => {
return new Promise((resolve, reject) => {
if (!dialog) return reject('Not supported');
const browserWindow = electron.remote.getCurrentWindow();
dialog.showOpenDialog(
browserWindow,
{
title: 'Open a project',
properties: ['openFile'],
message:
'If you want to open your GDevelop 4 project, be sure to save it as a .json file',
filters: [{ name: 'GDevelop 5 project', extensions: ['json'] }],
},
paths => {
if (!paths || !paths.length) return resolve(null);
return resolve(paths[0]);
}
);
});
};
static readProjectFile = (filepath: string): Object => {
const projectPath = path.dirname(filepath);
return readJSONFile(filepath).then(object => {
return unsplit(object, {
getReferencePartialObject: referencePath => {
return readJSONFile(path.join(projectPath, referencePath) + '.json');
},
isReferenceMagicPropertyName: '__REFERENCE_TO_SPLIT_OBJECT',
// Limit unsplitting to depth 3 (which would allow properties of layouts/external layouts/external events
// to be un-splitted, but not the content of these properties), to avoid very slow processing
// of large game files.
maxUnsplitDepth: 3,
}).then(() => {
return object;
});
});
};
static shouldOpenAutosave = (
filePath: string,
autoSavePath: string,
compareLastModified: boolean
): boolean => {
if (fs.existsSync(autoSavePath)) {
if (!compareLastModified) {
return true;
}
try {
const autoSavedTime = fs.statSync(autoSavePath).mtime.getTime();
const saveTime = fs.statSync(filePath).mtime.getTime();
if (autoSavedTime > saveTime) {
return true;
}
} catch (err) {
console.error('Unable to compare *.autosave to project', err);
return false;
}
return false;
}
return false;
};
}

View File

@@ -0,0 +1,79 @@
// @flow
import * as React from 'react';
export type ProjectsStorageProps = {|
onOpenWithPicker?: () => Promise<?string>,
onOpen: (filepath: string) => Object,
onSaveProject: gdProject => Promise<boolean>,
onSaveProjectAs?: gdProject => Promise<boolean>,
onAutoSaveProject?: (project: gdProject) => void,
shouldOpenAutosave?: (
filePath: string,
autoSavePath: string,
compareLastModified: boolean
) => boolean,
|};
export type StorageProvider = ({
setDialog: (() => React.Node) => void,
closeDialog: () => void,
}) => ProjectsStorageProps;
const emptyStorageProvider: StorageProvider = () => ({
onOpenWithPicker: () => Promise.reject('No storage provider set up'),
onOpen: () => ({}),
shouldOpenAutosave: () => false,
onSaveProject: (project: gdProject) =>
Promise.reject('No storage provider set up'),
onSaveProjectAs: (project: gdProject) =>
Promise.reject('No storage provider set up'),
onAutoSaveProject: (project: gdProject) => {},
});
type Props = {|
storageProviders: Array<StorageProvider>,
defaultStorageProvider?: StorageProvider,
children: (storage: ProjectsStorageProps) => React.Node,
|};
type State = {|
currentStorageProvider: ?StorageProvider,
renderDialog: ?() => React.Node,
|};
export default class ProjectsStorage extends React.Component<Props, State> {
state = {
currentStorageProvider: this.props.defaultStorageProvider,
renderDialog: null,
};
_setDialog = (renderDialog: () => React.Node) => {
this.setState({
renderDialog,
});
};
_closeDialog = () => {
this.setState({
renderDialog: null,
});
};
render() {
const { children } = this.props;
const { renderDialog } = this.state;
const currentStorageProvider =
this.state.currentStorageProvider || emptyStorageProvider;
return (
<React.Fragment>
{children(
currentStorageProvider({
setDialog: this._setDialog,
closeDialog: this._closeDialog,
})
)}
{renderDialog && renderDialog()}
</React.Fragment>
);
}
}