mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
1 Commits
skybox
...
game-manag
Author | SHA1 | Date | |
---|---|---|---|
![]() |
959988d147 |
@@ -185,6 +185,7 @@ export const GameCard = ({
|
||||
<>
|
||||
<Card
|
||||
key={game.id}
|
||||
background={isCurrentGame ? 'dark' : 'medium'}
|
||||
cardCornerAction={
|
||||
<ElementWithMenu
|
||||
element={
|
||||
@@ -249,6 +250,7 @@ export const GameCard = ({
|
||||
<GameThumbnail
|
||||
gameName={game.gameName}
|
||||
thumbnailUrl={game.thumbnailUrl}
|
||||
background={isCurrentGame ? 'medium' : 'light'}
|
||||
/>
|
||||
</Column>
|
||||
<Spacer />
|
||||
|
@@ -257,9 +257,8 @@ export const GameRegistrationWidget = ({
|
||||
)}
|
||||
>
|
||||
<Trans>
|
||||
This project is not registered online. Register it now to get access
|
||||
to metrics collected anonymously, like the number of daily players and
|
||||
retention of the players after a few days.
|
||||
The project currently opened is not registered online. Register it now
|
||||
to get access to leaderboards, player accounts, analytics and more!
|
||||
</Trans>
|
||||
</AlertMessage>
|
||||
);
|
||||
@@ -269,9 +268,9 @@ export const GameRegistrationWidget = ({
|
||||
return (
|
||||
<AlertMessage kind="error">
|
||||
<Trans>
|
||||
This project is registered online but you don't have access to it. Ask
|
||||
the original owner of the game to share it with you to get access to
|
||||
the game metrics.
|
||||
The project currently opened is registered online but you don't have
|
||||
access to it. Ask the original owner of the game to share it with you
|
||||
to be able to manage it.
|
||||
</Trans>
|
||||
</AlertMessage>
|
||||
);
|
||||
|
@@ -19,9 +19,14 @@ const styles = {
|
||||
type Props = {|
|
||||
thumbnailUrl?: string,
|
||||
gameName: string,
|
||||
background?: 'light' | 'medium' | 'dark',
|
||||
|};
|
||||
|
||||
export const GameThumbnail = ({ thumbnailUrl, gameName }: Props) =>
|
||||
export const GameThumbnail = ({
|
||||
thumbnailUrl,
|
||||
gameName,
|
||||
background = 'light',
|
||||
}: Props) =>
|
||||
thumbnailUrl ? (
|
||||
<img
|
||||
src={thumbnailUrl}
|
||||
@@ -40,7 +45,7 @@ export const GameThumbnail = ({ thumbnailUrl, gameName }: Props) =>
|
||||
whiteSpace: 'normal',
|
||||
display: 'flex',
|
||||
}}
|
||||
background="light"
|
||||
background={background}
|
||||
>
|
||||
<EmptyMessage>
|
||||
<Trans>No thumbnail set</Trans>
|
||||
|
@@ -1,14 +1,19 @@
|
||||
// @flow
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { t, Trans } from '@lingui/macro';
|
||||
import * as React from 'react';
|
||||
import AuthenticatedUserContext from '../Profile/AuthenticatedUserContext';
|
||||
import PlaceholderLoader from '../UI/PlaceholderLoader';
|
||||
import PlaceholderError from '../UI/PlaceholderError';
|
||||
import { type Game, getGames } from '../Utils/GDevelopServices/Game';
|
||||
import {
|
||||
type Game,
|
||||
getGames,
|
||||
registerGame,
|
||||
} from '../Utils/GDevelopServices/Game';
|
||||
import { GameCard } from './GameCard';
|
||||
import { ColumnStackLayout } from '../UI/Layout';
|
||||
import { GameRegistration } from './GameRegistration';
|
||||
import { GameDetailsDialog, type GameDetailsTab } from './GameDetailsDialog';
|
||||
import useAlertDialog from '../UI/Alert/useAlertDialog';
|
||||
|
||||
type Props = {|
|
||||
project: ?gdProject,
|
||||
@@ -29,12 +34,15 @@ export const GamesList = ({
|
||||
authenticated,
|
||||
firebaseUser,
|
||||
getAuthorizationHeader,
|
||||
profile,
|
||||
} = React.useContext(AuthenticatedUserContext);
|
||||
const [openedGame, setOpenedGame] = React.useState<?Game>(null);
|
||||
const [
|
||||
openedGameInitialTab,
|
||||
setOpenedGameInitialTab,
|
||||
] = React.useState<GameDetailsTab>(initialTab || 'details');
|
||||
const { showAlert, showConfirmation } = useAlertDialog();
|
||||
const [isGameRegistering, setIsGameRegistering] = React.useState(false);
|
||||
|
||||
const loadGames = React.useCallback(
|
||||
async () => {
|
||||
@@ -44,19 +52,82 @@ export const GamesList = ({
|
||||
setError(null);
|
||||
const games = await getGames(getAuthorizationHeader, firebaseUser.uid);
|
||||
setGames(games);
|
||||
// If a game id was passed, open it.
|
||||
if (initialGameId) {
|
||||
const game = games.find(game => game.id === initialGameId);
|
||||
if (game) {
|
||||
setOpenedGame(game);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error while loading user games.', error);
|
||||
setError(error);
|
||||
}
|
||||
},
|
||||
[authenticated, firebaseUser, getAuthorizationHeader, initialGameId]
|
||||
[authenticated, firebaseUser, getAuthorizationHeader]
|
||||
);
|
||||
|
||||
const onRegisterGame = React.useCallback(
|
||||
async () => {
|
||||
if (!profile || !project) return;
|
||||
|
||||
const { id } = profile;
|
||||
try {
|
||||
setIsGameRegistering(true);
|
||||
await registerGame(getAuthorizationHeader, id, {
|
||||
gameId: project.getProjectUuid(),
|
||||
authorName: project.getAuthor() || 'Unspecified publisher',
|
||||
gameName: project.getName() || 'Untitled game',
|
||||
templateSlug: project.getTemplateSlug(),
|
||||
});
|
||||
await loadGames();
|
||||
} catch (error) {
|
||||
console.error('Unable to register the game', error);
|
||||
if (error.response && error.response.status === 403) {
|
||||
await showAlert({
|
||||
title: t`Game already registered`,
|
||||
message: t`The project currently opened is registered online but you don't have
|
||||
access to it. Ask the original owner of the game to share it with you
|
||||
to be able to manage it.`,
|
||||
});
|
||||
} else {
|
||||
await showAlert({
|
||||
title: t`Unable to register the game`,
|
||||
message: t`An error happened while registering the game. Verify your internet connection
|
||||
or retry later.`,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
setIsGameRegistering(false);
|
||||
}
|
||||
},
|
||||
[getAuthorizationHeader, profile, project, showAlert, loadGames]
|
||||
);
|
||||
|
||||
React.useEffect(
|
||||
() => {
|
||||
const loadInitialGame = async () => {
|
||||
// When games are loaded and we have an initial game id, open it.
|
||||
if (games && initialGameId) {
|
||||
const game = games.find(game => game.id === initialGameId);
|
||||
if (game) {
|
||||
setOpenedGame(game);
|
||||
} else {
|
||||
onGameDetailsDialogClose(); // Ensure we reset initial props.
|
||||
const answer = await showConfirmation({
|
||||
title: t`Game not found`,
|
||||
message: t`This project is not registered online. Register it now
|
||||
to get access to leaderboards, player accounts, analytics and more!`,
|
||||
confirmButtonLabel: t`Register`,
|
||||
});
|
||||
if (!answer) return;
|
||||
|
||||
await onRegisterGame();
|
||||
}
|
||||
}
|
||||
};
|
||||
loadInitialGame();
|
||||
},
|
||||
[
|
||||
games,
|
||||
initialGameId,
|
||||
onRegisterGame,
|
||||
onGameDetailsDialogClose,
|
||||
showConfirmation,
|
||||
]
|
||||
);
|
||||
|
||||
React.useEffect(
|
||||
@@ -97,12 +168,14 @@ export const GamesList = ({
|
||||
|
||||
return (
|
||||
<ColumnStackLayout noMargin>
|
||||
<GameRegistration
|
||||
project={project}
|
||||
hideIfRegistered
|
||||
hideLoader
|
||||
onGameRegistered={loadGames}
|
||||
/>
|
||||
{!isGameRegistering && (
|
||||
<GameRegistration
|
||||
project={project}
|
||||
hideIfRegistered
|
||||
hideLoader
|
||||
onGameRegistered={loadGames}
|
||||
/>
|
||||
)}
|
||||
{displayedGames.map(game => (
|
||||
<GameCard
|
||||
key={game.id}
|
||||
|
@@ -66,6 +66,7 @@ export type RenderEditorContainerProps = {|
|
||||
onOpenRecentFile: (file: FileMetadataAndStorageProviderName) => void,
|
||||
onOpenProjectManager: () => void,
|
||||
onCloseProject: () => Promise<boolean>,
|
||||
onOpenGamesDashboard: (gameId: string) => void,
|
||||
|
||||
// Other dialogs opening:
|
||||
onCreateProject: (?ExampleShortHeader) => void,
|
||||
|
@@ -53,6 +53,9 @@ import BackgroundText from '../../../../UI/BackgroundText';
|
||||
import Paper from '../../../../UI/Paper';
|
||||
import PlaceholderError from '../../../../UI/PlaceholderError';
|
||||
import AlertMessage from '../../../../UI/AlertMessage';
|
||||
import { ListItemSecondaryAction } from '@material-ui/core';
|
||||
import IconButton from '../../../../UI/IconButton';
|
||||
import ThreeDotsMenu from '../../../../UI/CustomSvgIcons/ThreeDotsMenu';
|
||||
const electron = optionalRequire('electron');
|
||||
const path = optionalRequire('path');
|
||||
|
||||
@@ -94,6 +97,7 @@ type Props = {|
|
||||
onOpenNewProjectSetupDialog: (?ExampleShortHeader) => void,
|
||||
onShowAllExamples: () => void,
|
||||
onSelectExample: (exampleShortHeader: ExampleShortHeader) => void,
|
||||
onOpenGamesDashboard: (gameId: string) => void,
|
||||
storageProviders: Array<StorageProvider>,
|
||||
|};
|
||||
|
||||
@@ -142,6 +146,7 @@ const BuildSection = React.forwardRef<Props, BuildSectionInterface>(
|
||||
onShowAllExamples,
|
||||
onSelectExample,
|
||||
onOpenRecentFile,
|
||||
onOpenGamesDashboard,
|
||||
storageProviders,
|
||||
},
|
||||
ref
|
||||
@@ -187,6 +192,7 @@ const BuildSection = React.forwardRef<Props, BuildSectionInterface>(
|
||||
fileIdentifier: cloudProject.id,
|
||||
lastModifiedDate: Date.parse(cloudProject.lastModifiedAt),
|
||||
name: cloudProject.name,
|
||||
gameId: cloudProject.gameId,
|
||||
},
|
||||
};
|
||||
return file;
|
||||
@@ -268,7 +274,6 @@ const BuildSection = React.forwardRef<Props, BuildSectionInterface>(
|
||||
];
|
||||
if (file.storageProviderName === 'Cloud') {
|
||||
actions = actions.concat([
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: i18n._(t`Delete`),
|
||||
click: () => onDeleteCloudProject(i18n, file),
|
||||
@@ -280,7 +285,6 @@ const BuildSection = React.forwardRef<Props, BuildSectionInterface>(
|
||||
label: i18n._(t`Show in local folder`),
|
||||
click: () => locateProjectFile(file),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: i18n._(t`Remove from list`),
|
||||
click: () => onRemoveFromRecentFiles(file),
|
||||
@@ -288,13 +292,24 @@ const BuildSection = React.forwardRef<Props, BuildSectionInterface>(
|
||||
]);
|
||||
} else {
|
||||
actions = actions.concat([
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: i18n._(t`Remove from list`),
|
||||
click: () => onRemoveFromRecentFiles(file),
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
const gameId = file.fileMetadata.gameId;
|
||||
if (gameId) {
|
||||
actions = actions.concat([
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: i18n._(t`Manage game`),
|
||||
click: () => onOpenGamesDashboard(gameId),
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
return actions;
|
||||
};
|
||||
|
||||
@@ -522,6 +537,20 @@ const BuildSection = React.forwardRef<Props, BuildSectionInterface>(
|
||||
</Text>
|
||||
)}
|
||||
</Column>
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton
|
||||
size="small"
|
||||
edge="end"
|
||||
aria-label="menu"
|
||||
onClick={event => {
|
||||
// prevent trigerring the click on the list item.
|
||||
event.stopPropagation();
|
||||
openContextMenu(event, file);
|
||||
}}
|
||||
>
|
||||
<ThreeDotsMenu />
|
||||
</IconButton>
|
||||
</ListItemSecondaryAction>
|
||||
</LineStackLayout>
|
||||
) : (
|
||||
<Column expand>
|
||||
|
@@ -40,6 +40,7 @@ type Props = {|
|
||||
onOpenRecentFile: (file: FileMetadataAndStorageProviderName) => void,
|
||||
onCreateProject: (ExampleShortHeader | null) => void,
|
||||
onOpenProjectManager: () => void,
|
||||
onOpenGamesDashboard: (gameId: string) => void,
|
||||
|
||||
// Other dialogs opening:
|
||||
onOpenHelpFinder: () => void,
|
||||
@@ -70,6 +71,7 @@ export const HomePage = React.memo<Props>(
|
||||
canOpen,
|
||||
onChooseProject,
|
||||
onOpenRecentFile,
|
||||
onOpenGamesDashboard,
|
||||
onOpenNewProjectSetupDialog,
|
||||
onCreateProject,
|
||||
onOpenProjectManager,
|
||||
@@ -214,6 +216,7 @@ export const HomePage = React.memo<Props>(
|
||||
onCreateProject(exampleShortHeader)
|
||||
}
|
||||
onOpenRecentFile={onOpenRecentFile}
|
||||
onOpenGamesDashboard={onOpenGamesDashboard}
|
||||
storageProviders={storageProviders}
|
||||
/>
|
||||
)}
|
||||
@@ -261,6 +264,7 @@ export const renderHomePageContainer = (
|
||||
canOpen={props.canOpen}
|
||||
onChooseProject={props.onChooseProject}
|
||||
onOpenRecentFile={props.onOpenRecentFile}
|
||||
onOpenGamesDashboard={props.onOpenGamesDashboard}
|
||||
onCreateProject={props.onCreateProject}
|
||||
onOpenNewProjectSetupDialog={props.onOpenNewProjectSetupDialog}
|
||||
onOpenProjectManager={props.onOpenProjectManager}
|
||||
|
@@ -550,6 +550,7 @@ const MainFrame = (props: Props) => {
|
||||
setGamesDashboardInitialGameId,
|
||||
gamesDashboardInitialTab,
|
||||
setGamesDashboardInitialTab,
|
||||
openGameDashboard,
|
||||
} = useOpenInitialDialog({
|
||||
parameters: {
|
||||
initialDialog,
|
||||
@@ -741,9 +742,12 @@ const MainFrame = (props: Props) => {
|
||||
// (like locally or on Google Drive).
|
||||
if (onSaveProject) {
|
||||
preferences.insertRecentProjectFile({
|
||||
fileMetadata: fileMetadata.name
|
||||
? fileMetadata
|
||||
: { ...fileMetadata, name: project.getName() },
|
||||
fileMetadata: {
|
||||
...fileMetadata,
|
||||
name: project.getName(),
|
||||
gameId: project.getProjectUuid(),
|
||||
lastModifiedDate: Date.now(),
|
||||
},
|
||||
storageProviderName: storageProvider.internalName,
|
||||
});
|
||||
}
|
||||
@@ -1953,7 +1957,6 @@ const MainFrame = (props: Props) => {
|
||||
// At the end of the promise below, currentProject and storageProvider
|
||||
// may have changed (if the user opened another project). So we read and
|
||||
// store their values in variables now.
|
||||
const projectName = currentProject.getName();
|
||||
const storageProviderInternalName = newStorageProvider.internalName;
|
||||
|
||||
try {
|
||||
@@ -2004,11 +2007,8 @@ const MainFrame = (props: Props) => {
|
||||
|
||||
// Save was done on a new file/location, so save it in the
|
||||
// recent projects and in the state.
|
||||
const enrichedFileMetadata = fileMetadata.name
|
||||
? fileMetadata
|
||||
: { ...fileMetadata, name: projectName };
|
||||
const fileMetadataAndStorageProviderName = {
|
||||
fileMetadata: enrichedFileMetadata,
|
||||
fileMetadata,
|
||||
storageProviderName: storageProviderInternalName,
|
||||
};
|
||||
preferences.insertRecentProjectFile(fileMetadataAndStorageProviderName);
|
||||
@@ -2039,7 +2039,7 @@ const MainFrame = (props: Props) => {
|
||||
// can happen if another project was loaded in the meantime.
|
||||
setState(state => ({
|
||||
...state,
|
||||
currentFileMetadata: enrichedFileMetadata,
|
||||
currentFileMetadata: fileMetadata,
|
||||
}));
|
||||
}
|
||||
} catch (rawError) {
|
||||
@@ -2131,7 +2131,6 @@ const MainFrame = (props: Props) => {
|
||||
// At the end of the promise below, currentProject and storageProvider
|
||||
// may have changed (if the user opened another project). So we read and
|
||||
// store their values in variables now.
|
||||
const projectName = currentProject.getName();
|
||||
const storageProviderInternalName = getStorageProvider().internalName;
|
||||
|
||||
const { wasSaved, fileMetadata } = await onSaveProject(
|
||||
@@ -2143,12 +2142,9 @@ const MainFrame = (props: Props) => {
|
||||
console.info(
|
||||
`Project saved in ${performance.now() - saveStartTime}ms.`
|
||||
);
|
||||
const enrichedFileMetadata = fileMetadata.name
|
||||
? fileMetadata
|
||||
: { ...fileMetadata, name: projectName };
|
||||
|
||||
const fileMetadataAndStorageProviderName = {
|
||||
fileMetadata: enrichedFileMetadata,
|
||||
fileMetadata: fileMetadata,
|
||||
storageProviderName: storageProviderInternalName,
|
||||
};
|
||||
preferences.insertRecentProjectFile(
|
||||
@@ -2173,7 +2169,7 @@ const MainFrame = (props: Props) => {
|
||||
// can happen if another project was loaded in the meantime.
|
||||
setState(state => ({
|
||||
...state,
|
||||
currentFileMetadata: enrichedFileMetadata,
|
||||
currentFileMetadata: fileMetadata,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -2889,6 +2885,8 @@ const MainFrame = (props: Props) => {
|
||||
canInstallPrivateAsset,
|
||||
onChooseProject: () => openOpenFromStorageProviderDialog(),
|
||||
onOpenRecentFile: openFromFileMetadataWithStorageProvider,
|
||||
onOpenGamesDashboard: gameId =>
|
||||
openGameDashboard({ gameId, tab: 'details' }),
|
||||
onOpenNewProjectSetupDialog: exampleShortHeader => {
|
||||
setSelectedExampleShortHeader(exampleShortHeader);
|
||||
setNewProjectSetupDialogOpen(true);
|
||||
|
@@ -59,15 +59,30 @@ const zipProjectAndCommitVersion = async ({
|
||||
export const generateOnSaveProject = (
|
||||
authenticatedUser: AuthenticatedUser
|
||||
) => async (project: gdProject, fileMetadata: FileMetadata) => {
|
||||
if (!fileMetadata.gameId) {
|
||||
console.info('Game id was never set, updating the cloud project.');
|
||||
try {
|
||||
await updateCloudProject(authenticatedUser, fileMetadata.fileIdentifier, {
|
||||
gameId: project.getProjectUuid(),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Could not update cloud project with gameId', error);
|
||||
// Do not throw, as this is not a blocking error.
|
||||
}
|
||||
}
|
||||
const newFileMetadata = {
|
||||
...fileMetadata,
|
||||
gameId: project.getProjectUuid(),
|
||||
};
|
||||
const newVersion = await zipProjectAndCommitVersion({
|
||||
authenticatedUser,
|
||||
project,
|
||||
cloudProjectId: fileMetadata.fileIdentifier,
|
||||
cloudProjectId: newFileMetadata.fileIdentifier,
|
||||
});
|
||||
if (!newVersion) return { wasSaved: false, fileMetadata };
|
||||
if (!newVersion) return { wasSaved: false, fileMetadata: newFileMetadata };
|
||||
return {
|
||||
wasSaved: true,
|
||||
fileMetadata,
|
||||
fileMetadata: newFileMetadata,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -76,7 +91,7 @@ export const generateOnChangeProjectProperty = (
|
||||
) => async (
|
||||
project: gdProject,
|
||||
fileMetadata: FileMetadata,
|
||||
properties: { name: string }
|
||||
properties: {| name?: string, gameId?: string |}
|
||||
): Promise<boolean> => {
|
||||
if (!authenticatedUser.authenticated) return false;
|
||||
try {
|
||||
@@ -180,7 +195,7 @@ export const generateOnSaveProjectAs = (
|
||||
) => {
|
||||
if (!saveAsLocation)
|
||||
throw new Error('A location was not chosen before saving as.');
|
||||
const { name } = saveAsLocation;
|
||||
const { name, gameId } = saveAsLocation;
|
||||
if (!name) throw new Error('A name was not chosen before saving as.');
|
||||
if (!authenticatedUser.authenticated) {
|
||||
return { wasSaved: false, fileMetadata: null };
|
||||
@@ -191,12 +206,14 @@ export const generateOnSaveProjectAs = (
|
||||
// Create a new cloud project.
|
||||
const cloudProject = await createCloudProject(authenticatedUser, {
|
||||
name,
|
||||
gameId,
|
||||
});
|
||||
if (!cloudProject)
|
||||
throw new Error('No cloud project was returned from creation api call.');
|
||||
|
||||
const fileMetadata = {
|
||||
fileIdentifier: cloudProject.id,
|
||||
gameId,
|
||||
};
|
||||
|
||||
// Move the resources to the new project.
|
||||
|
@@ -130,8 +130,11 @@ export const onSaveProject = (
|
||||
'Project file is empty, "Save as" should have been called?'
|
||||
);
|
||||
}
|
||||
// Ensure we always pick the latest name and gameId.
|
||||
const newFileMetadata = {
|
||||
...fileMetadata,
|
||||
name: project.getName(),
|
||||
gameId: project.getProjectUuid(),
|
||||
lastModifiedDate: now,
|
||||
};
|
||||
|
||||
@@ -192,7 +195,13 @@ export const onSaveProjectAs = async (
|
||||
throw new Error('A file path was not chosen before saving as.');
|
||||
|
||||
options.onStartSaving();
|
||||
const newFileMetadata = { fileIdentifier: filePath };
|
||||
// Ensure we always pick the latest name and gameId.
|
||||
const newFileMetadata = {
|
||||
fileIdentifier: filePath,
|
||||
name: project.getName(),
|
||||
gameId: project.getProjectUuid(),
|
||||
lastModifiedDate: Date.now(),
|
||||
};
|
||||
|
||||
// Move (copy or download, etc...) the resources first.
|
||||
await options.onMoveResources({ newFileMetadata });
|
||||
|
@@ -13,6 +13,7 @@ export type FileMetadata = {|
|
||||
fileIdentifier: string,
|
||||
lastModifiedDate?: number,
|
||||
name?: string,
|
||||
gameId?: string,
|
||||
|};
|
||||
|
||||
/**
|
||||
@@ -30,6 +31,10 @@ export type SaveAsLocation = {|
|
||||
* (for example, a local file path is stored only in `fileIdentifier`).
|
||||
*/
|
||||
name?: string,
|
||||
/**
|
||||
* The id of the game. Might be null if unused
|
||||
*/
|
||||
gameId?: string,
|
||||
|
||||
// New fields can be added if a storage provider needs other things to identify
|
||||
// a new location where to save a project to.
|
||||
@@ -97,7 +102,7 @@ export type StorageProviderOperations = {|
|
||||
onChangeProjectProperty?: (
|
||||
project: gdProject,
|
||||
fileMetadata: FileMetadata,
|
||||
properties: { name: string } // In order to synchronize project and cloud project names.
|
||||
properties: {| name?: string, gameId?: string |} // In order to synchronize project and cloud project names.
|
||||
) => Promise<boolean>,
|
||||
|
||||
// Project auto saving:
|
||||
|
@@ -40,6 +40,7 @@ export type UploadedProjectResourceFiles = Array<{|
|
||||
type CloudProject = {|
|
||||
id: string,
|
||||
name: string,
|
||||
gameId?: string,
|
||||
createdAt: string,
|
||||
currentVersion?: string,
|
||||
deletedAt?: string,
|
||||
@@ -109,7 +110,7 @@ export const clearCloudProjectCredentials = async (): Promise<void> => {
|
||||
|
||||
export const createCloudProject = async (
|
||||
authenticatedUser: AuthenticatedUser,
|
||||
cloudProjectCreationPayload: { name: string }
|
||||
cloudProjectCreationPayload: {| name: string, gameId?: string |}
|
||||
): Promise<?CloudProject> => {
|
||||
const { getAuthorizationHeader, firebaseUser } = authenticatedUser;
|
||||
if (!firebaseUser) return null;
|
||||
@@ -251,13 +252,16 @@ export const getCloudProject = async (
|
||||
export const updateCloudProject = async (
|
||||
authenticatedUser: AuthenticatedUser,
|
||||
cloudProjectId: string,
|
||||
attributes: { name: string }
|
||||
attributes: {| name?: string, gameId?: string |}
|
||||
): Promise<?CloudProject> => {
|
||||
const { getAuthorizationHeader, firebaseUser } = authenticatedUser;
|
||||
if (!firebaseUser) return;
|
||||
|
||||
const cleanedAttributes = {
|
||||
name: attributes.name.slice(0, CLOUD_PROJECT_NAME_MAX_LENGTH),
|
||||
name: attributes.name
|
||||
? attributes.name.slice(0, CLOUD_PROJECT_NAME_MAX_LENGTH)
|
||||
: undefined,
|
||||
gameId: attributes.gameId,
|
||||
};
|
||||
|
||||
const { uid: userId } = firebaseUser;
|
||||
|
@@ -117,5 +117,6 @@ export const useOpenInitialDialog = ({ parameters, actions }: Props) => {
|
||||
setGamesDashboardInitialGameId,
|
||||
gamesDashboardInitialTab,
|
||||
setGamesDashboardInitialTab,
|
||||
openGameDashboard,
|
||||
};
|
||||
};
|
||||
|
@@ -119,6 +119,7 @@ const WrappedHomePage = ({
|
||||
storageProviders={[CloudStorageProvider]}
|
||||
onChooseProject={() => action('onChooseProject')()}
|
||||
onOpenRecentFile={() => action('onOpenRecentFile')()}
|
||||
onOpenGamesDashboard={() => action('onOpenGamesDashboard')()}
|
||||
onCreateProject={() => action('onCreateProject')()}
|
||||
onOpenProjectManager={() => action('onOpenProjectManager')()}
|
||||
onOpenHelpFinder={() => action('onOpenHelpFinder')()}
|
||||
|
Reference in New Issue
Block a user