mirror of
https://github.com/Heretek-AI/GDevelop-Heretek.git
synced 2026-10-12 02:02:42 +00:00
Add version in properties of a project
This commit is contained in:
@@ -61,6 +61,7 @@ Project::Project()
|
||||
:
|
||||
#if defined(GD_IDE_ONLY)
|
||||
name(_("Project")),
|
||||
version("1.0.0"),
|
||||
packageName("com.example.gamename"),
|
||||
orientation("landscape"),
|
||||
folderProject(false),
|
||||
@@ -524,6 +525,7 @@ void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
const SerializerElement& propElement =
|
||||
element.GetChild("properties", 0, "Info");
|
||||
SetName(propElement.GetChild("name", 0, "Nom").GetValue().GetString());
|
||||
SetVersion(propElement.GetStringAttribute("version", "1.0.0"));
|
||||
SetDefaultWidth(
|
||||
propElement.GetChild("windowWidth", 0, "WindowW").GetValue().GetInt());
|
||||
SetDefaultHeight(
|
||||
@@ -782,6 +784,7 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
|
||||
SerializerElement& propElement = element.AddChild("properties");
|
||||
propElement.AddChild("name").SetValue(GetName());
|
||||
propElement.SetAttribute("version", GetVersion());
|
||||
propElement.AddChild("author").SetValue(GetAuthor());
|
||||
propElement.AddChild("windowWidth").SetValue(GetMainWindowDefaultWidth());
|
||||
propElement.AddChild("windowHeight").SetValue(GetMainWindowDefaultHeight());
|
||||
@@ -1100,6 +1103,7 @@ Project& Project::operator=(const Project& other) {
|
||||
void Project::Init(const gd::Project& game) {
|
||||
// Some properties
|
||||
name = game.name;
|
||||
version = game.version;
|
||||
windowWidth = game.windowWidth;
|
||||
windowHeight = game.windowHeight;
|
||||
maxFPS = game.maxFPS;
|
||||
|
||||
@@ -21,44 +21,18 @@ class TiXmlElement;
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
namespace gd {
|
||||
class Platform;
|
||||
}
|
||||
namespace gd {
|
||||
class Layout;
|
||||
}
|
||||
namespace gd {
|
||||
class ExternalEvents;
|
||||
}
|
||||
namespace gd {
|
||||
class ResourcesManager;
|
||||
}
|
||||
namespace gd {
|
||||
class ExternalLayout;
|
||||
}
|
||||
namespace gd {
|
||||
class Object;
|
||||
}
|
||||
namespace gd {
|
||||
class VariablesContainer;
|
||||
}
|
||||
namespace gd {
|
||||
class ArbitraryResourceWorker;
|
||||
}
|
||||
namespace gd {
|
||||
class SourceFile;
|
||||
}
|
||||
namespace gd {
|
||||
class ImageManager;
|
||||
}
|
||||
namespace gd {
|
||||
class Behavior;
|
||||
}
|
||||
namespace gd {
|
||||
class BehaviorsSharedData;
|
||||
}
|
||||
namespace gd {
|
||||
class BaseEvent;
|
||||
}
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
}
|
||||
#undef GetObject // Disable an annoying macro
|
||||
@@ -92,6 +66,17 @@ class GD_CORE_API Project : public ClassWithObjects {
|
||||
*/
|
||||
const gd::String& GetName() const { return name; }
|
||||
|
||||
/**
|
||||
* \brief Change the version of the project.
|
||||
* This can be freely set, but should follow "X.Y.Z" format for compatibility with some exporters.
|
||||
*/
|
||||
void SetVersion(const gd::String& version_) { version = version_; };
|
||||
|
||||
/**
|
||||
* \brief Get project version.
|
||||
*/
|
||||
const gd::String& GetVersion() const { return version; }
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
/**
|
||||
* \brief Change the author of the project.
|
||||
@@ -877,6 +862,7 @@ class GD_CORE_API Project : public ClassWithObjects {
|
||||
void LoadProjectInformationFromXml(const TiXmlElement* elem);
|
||||
|
||||
gd::String name; ///< Game name
|
||||
gd::String version; ///< Game version number (used for some exports)
|
||||
unsigned int windowWidth; ///< Window default width
|
||||
unsigned int windowHeight; ///< Window default height
|
||||
int maxFPS; ///< Maximum Frame Per Seconds, -1 for unlimited
|
||||
|
||||
@@ -315,6 +315,7 @@ bool ExporterHelper::ExportElectronFiles(const gd::Project &project,
|
||||
project.GetName()) // TODO: JSON encode string
|
||||
.FindAndReplace("GDJS_GAME_AUTHOR",
|
||||
project.GetAuthor()) // TODO: JSON encode string
|
||||
.FindAndReplace("GDJS_GAME_VERSION", project.GetVersion())
|
||||
.FindAndReplace(
|
||||
"GDJS_GAME_MANGLED_NAME",
|
||||
project.GetName().LowerCase()); // TODO: JSON encode string
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"productName": "GDJS_GAME_NAME",
|
||||
"description": "GDJS_GAME_NAME",
|
||||
"author": "GDJS_GAME_AUTHOR",
|
||||
"version": "1.0.0",
|
||||
"version": "GDJS_GAME_VERSION",
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import Dialog from '../../UI/Dialog';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
@@ -9,9 +10,11 @@ import { findGDJS } from './LocalGDJSFinder';
|
||||
import localFileSystem from './LocalFileSystem';
|
||||
import LocalFolderPicker from '../../UI/LocalFolderPicker';
|
||||
import HelpButton from '../../UI/HelpButton';
|
||||
import { displaySanityCheck } from '../SanityChecker';
|
||||
import { getSanityMessages } from '../SanityChecker/ProjectSanityChecker';
|
||||
import { translate } from 'react-i18next';
|
||||
import {
|
||||
displayProjectErrorsBox,
|
||||
getErrors,
|
||||
} from '../../ProjectManager/ProjectErrorsChecker';
|
||||
import { translate, type TranslatorProps } from 'react-i18next';
|
||||
import assignIn from 'lodash/assignIn';
|
||||
import optionalRequire from '../../Utils/OptionalRequire';
|
||||
import Window from '../../Utils/Window';
|
||||
@@ -20,7 +23,16 @@ const shell = electron ? electron.shell : null;
|
||||
|
||||
const gd = global.gd;
|
||||
|
||||
class LocalCordovaExport extends Component {
|
||||
type Props = TranslatorProps & {|
|
||||
project: gdProject,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
outputDir: string,
|
||||
exportFinishedDialogOpen: boolean,
|
||||
|};
|
||||
|
||||
class LocalCordovaExport extends Component<Props, State> {
|
||||
state = {
|
||||
exportFinishedDialogOpen: false,
|
||||
outputDir: '',
|
||||
@@ -61,7 +73,7 @@ class LocalCordovaExport extends Component {
|
||||
|
||||
sendExportLaunched('local-cordova');
|
||||
|
||||
if (!displaySanityCheck(t, getSanityMessages(t, project))) return;
|
||||
if (!displayProjectErrorsBox(t, getErrors(t, project))) return;
|
||||
|
||||
const outputDir = this.state.outputDir;
|
||||
project.setLastCompilationDirectory(outputDir);
|
||||
@@ -70,14 +82,10 @@ class LocalCordovaExport extends Component {
|
||||
.then(({ exporter }) => {
|
||||
const exportOptions = new gd.MapStringBoolean();
|
||||
exportOptions.set('exportForCordova', true);
|
||||
exporter.exportWholePixiProject(
|
||||
project,
|
||||
outputDir,
|
||||
exportOptions
|
||||
);
|
||||
exporter.exportWholePixiProject(project, outputDir, exportOptions);
|
||||
exportOptions.delete();
|
||||
exporter.delete();
|
||||
|
||||
|
||||
this.setState({
|
||||
exportFinishedDialogOpen: true,
|
||||
});
|
||||
@@ -88,7 +96,7 @@ class LocalCordovaExport extends Component {
|
||||
};
|
||||
|
||||
openExportFolder = () => {
|
||||
shell.openItem(this.state.outputDir);
|
||||
if (shell) shell.openItem(this.state.outputDir);
|
||||
};
|
||||
|
||||
openPhoneGapBuild = () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import Dialog from '../../UI/Dialog';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
@@ -9,9 +10,8 @@ import { findGDJS } from './LocalGDJSFinder';
|
||||
import localFileSystem from './LocalFileSystem';
|
||||
import LocalFolderPicker from '../../UI/LocalFolderPicker';
|
||||
import HelpButton from '../../UI/HelpButton';
|
||||
import { displaySanityCheck } from '../SanityChecker';
|
||||
import { getSanityMessages } from '../SanityChecker/ProjectSanityChecker';
|
||||
import { translate } from 'react-i18next';
|
||||
import { displayProjectErrorsBox, getErrors } from '../../ProjectManager/ProjectErrorsChecker';
|
||||
import { translate, type TranslatorProps } from 'react-i18next';
|
||||
import assignIn from 'lodash/assignIn';
|
||||
import optionalRequire from '../../Utils/OptionalRequire';
|
||||
const electron = optionalRequire('electron');
|
||||
@@ -19,7 +19,16 @@ const shell = electron ? electron.shell : null;
|
||||
|
||||
const gd = global.gd;
|
||||
|
||||
class LocalElectronExport extends Component {
|
||||
type Props = TranslatorProps & {|
|
||||
project: gdProject,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
outputDir: string,
|
||||
exportFinishedDialogOpen: boolean,
|
||||
|};
|
||||
|
||||
class LocalElectronExport extends Component<Props, State> {
|
||||
state = {
|
||||
exportFinishedDialogOpen: false,
|
||||
outputDir: '',
|
||||
@@ -60,7 +69,7 @@ class LocalElectronExport extends Component {
|
||||
|
||||
sendExportLaunched('local-electron');
|
||||
|
||||
if (!displaySanityCheck(t, getSanityMessages(t, project))) return;
|
||||
if (!displayProjectErrorsBox(t, getErrors(t, project))) return;
|
||||
|
||||
const outputDir = this.state.outputDir;
|
||||
project.setLastCompilationDirectory(outputDir);
|
||||
@@ -87,7 +96,7 @@ class LocalElectronExport extends Component {
|
||||
};
|
||||
|
||||
openExportFolder = () => {
|
||||
shell.openItem(this.state.outputDir);
|
||||
if (shell) shell.openItem(this.state.outputDir);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
@@ -24,8 +24,7 @@ import Window from '../../../Utils/Window';
|
||||
import { delay } from '../../../Utils/Delay';
|
||||
import CreateProfile from '../../../Profile/CreateProfile';
|
||||
import LimitDisplayer from '../../../Profile/LimitDisplayer';
|
||||
import { displaySanityCheck } from '../../SanityChecker';
|
||||
import { getSanityMessages } from '../../SanityChecker/ProjectSanityChecker';
|
||||
import { displayProjectErrorsBox, getErrors } from '../../../ProjectManager/ProjectErrorsChecker';
|
||||
import { translate, type TranslatorProps } from 'react-i18next';
|
||||
import { type Limit } from '../../../Utils/GDevelopServices/Usage';
|
||||
const path = optionalRequire('path');
|
||||
@@ -209,7 +208,7 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
const { t, project } = this.props;
|
||||
sendExportLaunched('local-online-cordova');
|
||||
|
||||
if (!displaySanityCheck(t, getSanityMessages(t, project))) return;
|
||||
if (!displayProjectErrorsBox(t, getErrors(t, project))) return;
|
||||
|
||||
const handleError = (message: string) => err => {
|
||||
if (!this.state.errored) {
|
||||
|
||||
@@ -24,8 +24,7 @@ import Window from '../../../Utils/Window';
|
||||
import { delay } from '../../../Utils/Delay';
|
||||
import CreateProfile from '../../../Profile/CreateProfile';
|
||||
import LimitDisplayer from '../../../Profile/LimitDisplayer';
|
||||
import { displaySanityCheck } from '../../SanityChecker';
|
||||
import { getSanityMessages } from '../../SanityChecker/ProjectSanityChecker';
|
||||
import { displayProjectErrorsBox, getErrors } from '../../../ProjectManager/ProjectErrorsChecker';
|
||||
import { translate, type TranslatorProps } from 'react-i18next';
|
||||
import { type Limit } from '../../../Utils/GDevelopServices/Usage';
|
||||
const path = optionalRequire('path');
|
||||
@@ -209,7 +208,7 @@ class LocalOnlineElectronExport extends Component<Props, State> {
|
||||
const { t, project } = this.props;
|
||||
sendExportLaunched('local-online-electron');
|
||||
|
||||
if (!displaySanityCheck(t, getSanityMessages(t, project))) return;
|
||||
if (!displayProjectErrorsBox(t, getErrors(t, project))) return;
|
||||
|
||||
const handleError = (message: string) => err => {
|
||||
if (!this.state.errored) {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// @flow
|
||||
import { type TFunction } from 'react-i18next';
|
||||
import { type SanityMessages } from './index';
|
||||
|
||||
export const getSanityMessages = (t: TFunction, project: gdProject): SanityMessages => {
|
||||
let errors = [];
|
||||
let warnings = [];
|
||||
if (!project.getPackageName()) {
|
||||
errors.push(
|
||||
t(
|
||||
'The package name is empty. Choose and enter a package name in the game properties.'
|
||||
)
|
||||
);
|
||||
} else if (project.getPackageName().length >= 255) {
|
||||
errors.push(t('The package name is too long.'));
|
||||
}
|
||||
|
||||
if (!project.getName()) {
|
||||
errors.push(
|
||||
t(
|
||||
'The game name is empty. Choose and enter a name in the game properties.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
errors,
|
||||
warnings,
|
||||
};
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
// @flow
|
||||
import { type TFunction } from 'react-i18next';
|
||||
import { showErrorBox } from '../../UI/Messages/MessageBox';
|
||||
|
||||
export type SanityMessages = {
|
||||
errors: Array<string>,
|
||||
warnings: Array<string>,
|
||||
};
|
||||
|
||||
export const displaySanityCheck = (
|
||||
t: TFunction,
|
||||
messages: SanityMessages
|
||||
): boolean => {
|
||||
if (messages.errors.length) {
|
||||
showErrorBox(
|
||||
t(
|
||||
'Your game has some invalid elements, please fix these before continuing:'
|
||||
) +
|
||||
'\n\n' +
|
||||
messages.errors.map(message => `- ${message}`).join('\n')
|
||||
);
|
||||
}
|
||||
|
||||
return !messages.errors.length;
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
// @flow
|
||||
import { type TFunction } from 'react-i18next';
|
||||
import { showErrorBox } from '../UI/Messages/MessageBox';
|
||||
import values from 'lodash/values';
|
||||
|
||||
export type ProjectError = {
|
||||
type: 'error' | 'warning',
|
||||
message: string,
|
||||
extraExplanation: string,
|
||||
};
|
||||
|
||||
export type ProjectErrors = {
|
||||
[string]: Array<ProjectError>,
|
||||
};
|
||||
|
||||
export const getErrors = (t: TFunction, project: gdProject): ProjectErrors => {
|
||||
const errors: ProjectErrors = {};
|
||||
|
||||
const addError = (
|
||||
propertyName: string,
|
||||
type: 'error' | 'warning',
|
||||
message: string,
|
||||
extraExplanation: string = ''
|
||||
) => {
|
||||
if (!errors[propertyName]) errors[propertyName] = [];
|
||||
|
||||
errors[propertyName].push({
|
||||
type,
|
||||
message,
|
||||
extraExplanation,
|
||||
});
|
||||
};
|
||||
|
||||
if (!project.getPackageName()) {
|
||||
addError(
|
||||
'packageName',
|
||||
'error',
|
||||
t('The package name is empty.'),
|
||||
t('Choose and enter a package name in the game properties.')
|
||||
);
|
||||
} else if (project.getPackageName().length >= 255) {
|
||||
addError(
|
||||
'packageName',
|
||||
'error',
|
||||
t('The package name is too long.'),
|
||||
t('Change the package name in the game properties.')
|
||||
);
|
||||
}
|
||||
|
||||
if (!project.getName()) {
|
||||
addError(
|
||||
'name',
|
||||
'error',
|
||||
t('The name of your game is too long.'),
|
||||
t('Change the name in the game properties.')
|
||||
);
|
||||
}
|
||||
|
||||
if (!project.getVersion() || project.getVersion().split('.').length < 3) {
|
||||
addError(
|
||||
'packageName',
|
||||
'error',
|
||||
t("The version that you've set for the game is invalid."),
|
||||
t('Enter a version in the game properties.')
|
||||
);
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
|
||||
export const displayProjectErrorsBox = (
|
||||
t: TFunction,
|
||||
errors: ProjectErrors
|
||||
): boolean => {
|
||||
if (!Object.keys(errors).length) return true;
|
||||
|
||||
showErrorBox(
|
||||
t(
|
||||
'Your game has some invalid elements, please fix these before continuing:'
|
||||
) +
|
||||
'\n\n' +
|
||||
values(errors)
|
||||
.map(errors =>
|
||||
errors.map((error: ProjectError) => `- ${error.message}`).join('\n')
|
||||
)
|
||||
.join('\n')
|
||||
);
|
||||
|
||||
return false;
|
||||
};
|
||||
@@ -1,14 +1,16 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import Checkbox from 'material-ui/Checkbox';
|
||||
import SelectField from 'material-ui/SelectField';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import Dialog from '../UI/Dialog';
|
||||
import SemiControlledTextField from '../UI/SemiControlledTextField';
|
||||
import SubscriptionChecker from '../Profile/SubscriptionChecker';
|
||||
import { translate, type TranslatorProps } from 'react-i18next';
|
||||
import { getErrors, displayProjectErrorsBox } from './ProjectErrorsChecker';
|
||||
|
||||
type Props = {|
|
||||
type Props = TranslatorProps & {|
|
||||
project: gdProject,
|
||||
open: boolean,
|
||||
onClose: Function,
|
||||
@@ -21,13 +23,14 @@ type State = {|
|
||||
windowDefaultHeight: number,
|
||||
name: string,
|
||||
author: string,
|
||||
version: string,
|
||||
packageName: string,
|
||||
orientation: string,
|
||||
sizeOnStartupMode: string,
|
||||
showGDevelopSplash: boolean,
|
||||
|};
|
||||
|
||||
export default class ProjectPropertiesDialog extends React.Component<
|
||||
class ProjectPropertiesDialog extends React.Component<
|
||||
Props,
|
||||
State
|
||||
> {
|
||||
@@ -44,6 +47,7 @@ export default class ProjectPropertiesDialog extends React.Component<
|
||||
windowDefaultHeight: project.getMainWindowDefaultHeight(),
|
||||
name: project.getName(),
|
||||
author: project.getAuthor(),
|
||||
version: project.getVersion(),
|
||||
packageName: project.getPackageName(),
|
||||
orientation: project.getOrientation(),
|
||||
sizeOnStartupMode: project.getSizeOnStartupMode(),
|
||||
@@ -61,12 +65,13 @@ export default class ProjectPropertiesDialog extends React.Component<
|
||||
}
|
||||
|
||||
_onApply = () => {
|
||||
const { project } = this.props;
|
||||
const { project, t } = this.props;
|
||||
const {
|
||||
windowDefaultWidth,
|
||||
windowDefaultHeight,
|
||||
name,
|
||||
author,
|
||||
version,
|
||||
packageName,
|
||||
orientation,
|
||||
sizeOnStartupMode,
|
||||
@@ -76,11 +81,14 @@ export default class ProjectPropertiesDialog extends React.Component<
|
||||
project.setDefaultHeight(windowDefaultHeight);
|
||||
project.setName(name);
|
||||
project.setAuthor(author);
|
||||
project.setVersion(version);
|
||||
project.setPackageName(packageName);
|
||||
project.setOrientation(orientation);
|
||||
project.setSizeOnStartupMode(sizeOnStartupMode);
|
||||
project.getLoadingScreen().showGDevelopSplash(showGDevelopSplash);
|
||||
|
||||
if (!displayProjectErrorsBox(t, getErrors(t, project))) return;
|
||||
|
||||
this.props.onApply();
|
||||
};
|
||||
|
||||
@@ -103,6 +111,7 @@ export default class ProjectPropertiesDialog extends React.Component<
|
||||
windowDefaultWidth,
|
||||
windowDefaultHeight,
|
||||
author,
|
||||
version,
|
||||
packageName,
|
||||
orientation,
|
||||
sizeOnStartupMode,
|
||||
@@ -117,48 +126,56 @@ export default class ProjectPropertiesDialog extends React.Component<
|
||||
onRequestClose={this.props.onClose}
|
||||
autoScrollBodyContent={true}
|
||||
>
|
||||
<TextField
|
||||
<SemiControlledTextField
|
||||
floatingLabelText="Game name"
|
||||
fullWidth
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e, value) => this.setState({ name: value })}
|
||||
onChange={value => this.setState({ name: value })}
|
||||
/>
|
||||
<TextField
|
||||
<SemiControlledTextField
|
||||
floatingLabelText="Game's window width"
|
||||
fullWidth
|
||||
type="number"
|
||||
value={windowDefaultWidth}
|
||||
onChange={(e, value) =>
|
||||
value={'' + windowDefaultWidth}
|
||||
onChange={value =>
|
||||
this.setState({
|
||||
windowDefaultWidth: Math.max(0, parseInt(value, 10)),
|
||||
})}
|
||||
/>
|
||||
<TextField
|
||||
<SemiControlledTextField
|
||||
floatingLabelText="Game's window height"
|
||||
fullWidth
|
||||
type="number"
|
||||
value={windowDefaultHeight}
|
||||
onChange={(e, value) =>
|
||||
value={'' + windowDefaultHeight}
|
||||
onChange={value =>
|
||||
this.setState({
|
||||
windowDefaultHeight: Math.max(0, parseInt(value, 10)),
|
||||
})}
|
||||
/>
|
||||
<TextField
|
||||
<SemiControlledTextField
|
||||
floatingLabelText="Author name"
|
||||
fullWidth
|
||||
hintText="Your name"
|
||||
type="text"
|
||||
value={author}
|
||||
onChange={(e, value) => this.setState({ author: value })}
|
||||
onChange={value => this.setState({ author: value })}
|
||||
/>
|
||||
<TextField
|
||||
<SemiControlledTextField
|
||||
floatingLabelText="Version number (X.Y.Z)"
|
||||
fullWidth
|
||||
hintText="1.0.0"
|
||||
type="text"
|
||||
value={version}
|
||||
onChange={value => this.setState({ version: value })}
|
||||
/>
|
||||
<SemiControlledTextField
|
||||
floatingLabelText="Package name (for iOS and Android)"
|
||||
fullWidth
|
||||
hintText="com.example.mygame"
|
||||
type="text"
|
||||
value={packageName}
|
||||
onChange={(e, value) => this.setState({ packageName: value })}
|
||||
onChange={value => this.setState({ packageName: value })}
|
||||
/>
|
||||
<SelectField
|
||||
fullWidth
|
||||
@@ -219,3 +236,5 @@ export default class ProjectPropertiesDialog extends React.Component<
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate()(ProjectPropertiesDialog);
|
||||
Reference in New Issue
Block a user