Skip to content

Commit

Permalink
Allow to open projects from the game dashboard
Browse files Browse the repository at this point in the history
- [x] Display "Open project" button (with arrow if multiples)
- [x] Display cloud/local projects in game dashboard for a game
  - [/] Ensure we always save the game id
  - [x] Adapt the wording if nothing is found.
  • Loading branch information
4ian committed Nov 16, 2024
1 parent dda85cf commit b6cc647
Show file tree
Hide file tree
Showing 24 changed files with 658 additions and 351 deletions.
112 changes: 94 additions & 18 deletions newIDE/app/src/GameDashboard/GameCard.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
// @flow
import { Trans } from '@lingui/macro';
import { t, Trans } from '@lingui/macro';
import { I18n } from '@lingui/react';
import * as React from 'react';
import { type I18n as I18nType } from '@lingui/core';

import {
ColumnStackLayout,
LineStackLayout,
ResponsiveLineStackLayout,
} from '../UI/Layout';
import { type FileMetadataAndStorageProviderName } from '../ProjectsStorage';
import FlatButton from '../UI/FlatButton';
import Text from '../UI/Text';

import { GameThumbnail } from './GameThumbnail';

import {
getGameMainImageUrl,
getGameUrl,
Expand All @@ -28,26 +26,40 @@ import DollarCoin from '../UI/CustomSvgIcons/DollarCoin';
import Cross from '../UI/CustomSvgIcons/Cross';
import Messages from '../UI/CustomSvgIcons/Messages';
import GameLinkAndShareIcons from './GameLinkAndShareIcons';
import { useProjectsListFor } from '../MainFrame/EditorContainers/HomePage/CreateSection/utils';
import FlatButtonWithSplitMenu from '../UI/FlatButtonWithSplitMenu';
import useOnResize from '../Utils/UseOnResize';
import useForceUpdate from '../Utils/UseForceUpdate';

const styles = {
buttonsContainer: { display: 'flex', flexShrink: 0 },
iconAndText: { display: 'flex', gap: 2, alignItems: 'flex-start' },
};

type Props = {|
game: Game,
isCurrentGame: boolean,
onOpenGameManager: () => void,
onOpenProject: (file: FileMetadataAndStorageProviderName) => Promise<void>,
|};

export const GameCard = ({ game, isCurrentGame, onOpenGameManager }: Props) => {
export const GameCard = ({
game,
isCurrentGame,
onOpenGameManager,
onOpenProject,
}: Props) => {
useOnResize(useForceUpdate());
const projectsList = useProjectsListFor(game);
const isPublishedOnGdGames = !!game.publicWebBuildId;
const gameUrl = isPublishedOnGdGames ? getGameUrl(game) : null;

const gameThumbnailUrl = React.useMemo(() => getGameMainImageUrl(game), [
game,
]);

const { isMobile } = useResponsiveWindowSize();
const { isMobile, windowSize } = useResponsiveWindowSize();
const isWidthConstrained = windowSize === 'small' || windowSize === 'medium';
const gdevelopTheme = React.useContext(GDevelopThemeContext);

const renderPublicInfo = () => {
Expand Down Expand Up @@ -119,19 +131,83 @@ export const GameCard = ({ game, isCurrentGame, onOpenGameManager }: Props) => {
gameId={game.id}
thumbnailUrl={gameThumbnailUrl}
background="light"
width={
isMobile
? undefined
: // On medium/large screens, adapt the size to the width of the window.
Math.min(272, Math.max(130, window.innerWidth / 5))
}
/>
);

const renderButtons = () => (
<LineStackLayout noMargin>
<FlatButton
primary
fullWidth
label={<Trans>Manage game</Trans>}
onClick={onOpenGameManager}
/>
</LineStackLayout>
);
const renderButtons = (fullWidth: boolean) => {
return (
<div styles={styles.buttonsContainer}>
<LineStackLayout noMargin>
<FlatButton
primary
fullWidth={fullWidth}
label={
isWidthConstrained ? (
<Trans>Manage</Trans>
) : (
<Trans>Manage game</Trans>
)
}
onClick={onOpenGameManager}
/>
{projectsList.length === 0 ? null : projectsList.length === 1 ? (
<FlatButton
primary
fullWidth={fullWidth}
disabled={isCurrentGame}
label={
isCurrentGame ? (
<Trans>Opened</Trans>
) : isWidthConstrained ? (
<Trans>Open</Trans>
) : (
<Trans>Open project</Trans>
)
}
onClick={() => onOpenProject(projectsList[0])}
/>
) : (
<FlatButtonWithSplitMenu
primary
fullWidth={fullWidth}
disabled={isCurrentGame}
label={
isCurrentGame ? <Trans>Opened</Trans> : <Trans>Open</Trans>
}
onClick={() => onOpenProject(projectsList[0])}
buildMenuTemplate={i18n => [
...projectsList.map(fileMetadataAndStorageProviderName => {
const name =
fileMetadataAndStorageProviderName.fileMetadata.name || '-';
return {
label: i18n._(
t`${name} (${
// TODO: user friendly name
fileMetadataAndStorageProviderName.storageProviderName
})`
),
click: () =>
onOpenProject(fileMetadataAndStorageProviderName),
};
}),
{ type: 'separator' },
{
label: i18n._(t`See all in the game dashboard`),
click: onOpenGameManager,
},
]}
/>
)}
</LineStackLayout>
</div>
);
};

const renderShareUrl = (i18n: I18nType) =>
gameUrl ? <GameLinkAndShareIcons url={gameUrl} display="line" /> : null;
Expand All @@ -153,7 +229,7 @@ export const GameCard = ({ game, isCurrentGame, onOpenGameManager }: Props) => {
{renderPublicInfo()}
</LineStackLayout>
{renderShareUrl(i18n)}
{renderButtons()}
{renderButtons(/*fullWidth=*/ true)}
</ColumnStackLayout>
) : (
<LineStackLayout noMargin>
Expand All @@ -169,7 +245,7 @@ export const GameCard = ({ game, isCurrentGame, onOpenGameManager }: Props) => {
alignItems="flex-start"
>
{renderTitle(i18n)}
{renderButtons()}
{renderButtons(/*fullWidth=*/ false)}
</LineStackLayout>
{renderPublicInfo()}
{renderShareUrl(i18n)}
Expand Down
4 changes: 4 additions & 0 deletions newIDE/app/src/GameDashboard/GameHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import Edit from '../UI/CustomSvgIcons/Edit';
import GameLinkAndShareIcons from './GameLinkAndShareIcons';
import { CompactToggleField } from '../UI/CompactToggleField';
import { FixedHeightFlexContainer } from '../UI/Grid';
import useOnResize from '../Utils/UseOnResize';
import useForceUpdate from '../Utils/UseForceUpdate';

const styles = {
iconAndText: { display: 'flex', gap: 2, alignItems: 'flex-start' },
Expand All @@ -42,6 +44,7 @@ const GameHeader = ({
gameUrl,
onPublishOnGdGames,
}: Props) => {
useOnResize(useForceUpdate());
const { isMobile } = useResponsiveWindowSize();
const gdevelopTheme = React.useContext(GDevelopThemeContext);
const gameMainImageUrl = getGameMainImageUrl(game);
Expand Down Expand Up @@ -113,6 +116,7 @@ const GameHeader = ({
gameId={game.id}
thumbnailUrl={gameMainImageUrl}
background="medium"
width={Math.min(272, Math.max(150, window.innerWidth / 4.5))}
/>
);

Expand Down
27 changes: 11 additions & 16 deletions newIDE/app/src/GameDashboard/GameThumbnail.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,6 @@ const styles = {
aspectRatio: '16 / 9',
height: 'auto',
justifyContent: 'center',
overflow: 'hidden', // Keep the radius effect.
},
thumbnailContainer: {
height: 153,
width: 272,
overflow: 'hidden', // Keep the radius effect.
},
mobileThumbnailContainer: {
height: 84,
width: 150,
overflow: 'hidden', // Keep the radius effect.
},
thumbnail: {
aspectRatio: '16 / 9',
Expand All @@ -62,6 +51,7 @@ type Props = {|
onGameUpdated?: (updatedGame: Game) => void,
onUpdatingGame?: (isUpdatingGame: boolean) => void,
fullWidthOnMobile?: boolean,
width?: number,
|};

export const GameThumbnail = ({
Expand All @@ -74,6 +64,7 @@ export const GameThumbnail = ({
onUpdatingGame,
background = 'light',
fullWidthOnMobile,
width,
}: Props) => {
const { isMobile, isLandscape } = useResponsiveWindowSize();
const { profile, getAuthorizationHeader } = React.useContext(
Expand Down Expand Up @@ -168,15 +159,19 @@ export const GameThumbnail = ({
}
};

const thumbnailWidth = width ? width : isMobile && !isLandscape ? 150 : 272;
const thumbnailHeight = Math.floor(thumbnailWidth / (16 / 9));

return (
<ColumnStackLayout noMargin alignItems="center">
<Paper
style={{
...(isMobile && !isLandscape
? fullWidthOnMobile
? styles.fullWidthContainer
: styles.mobileThumbnailContainer
: styles.thumbnailContainer),
width: thumbnailWidth,
height: thumbnailHeight,
...(isMobile && !isLandscape && fullWidthOnMobile
? styles.fullWidthContainer
: undefined),
overflow: 'hidden', // Keep the radius effect.
whiteSpace: 'normal',
display: 'flex',
}}
Expand Down
4 changes: 3 additions & 1 deletion newIDE/app/src/GameDashboard/GamesList.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ type Props = {|
games: Array<Game>,
onRefreshGames: () => Promise<void>,
onOpenGameId: (gameId: ?string) => void,
onOpenProject: (file: FileMetadataAndStorageProviderName) => Promise<void>,
|};

const GamesList = ({ project, games, onRefreshGames, onOpenGameId }: Props) => {
const GamesList = ({ project, games, onRefreshGames, onOpenGameId, onOpenProject }: Props) => {
const { values, setGamesListOrderBy } = React.useContext(PreferencesContext);
const [searchText, setSearchText] = React.useState<string>('');
const [currentPage, setCurrentPage] = React.useState<number>(0);
Expand Down Expand Up @@ -217,6 +218,7 @@ const GamesList = ({ project, games, onRefreshGames, onOpenGameId }: Props) => {
onOpenGameManager={() => {
onOpenGameId(game.id);
}}
onOpenProject={onOpenProject}
/>
))
) : !!searchText ? (
Expand Down
36 changes: 36 additions & 0 deletions newIDE/app/src/GameDashboard/Widgets/ProjectsWidget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// @flow
import * as React from 'react';
import { I18n } from '@lingui/react';
import { Trans } from '@lingui/macro';
import { type Game } from '../../Utils/GDevelopServices/Game';
import {
type FileMetadataAndStorageProviderName,
type FileMetadata,
type StorageProvider,
} from '../../ProjectsStorage';
import DashboardWidget from './DashboardWidget';
import ProjectFileList from '../../MainFrame/EditorContainers/HomePage/CreateSection/ProjectFileList';

type Props = {|
game: Game,
onOpenRecentFile: (file: FileMetadataAndStorageProviderName) => Promise<void>,
storageProviders: Array<StorageProvider>,

project: ?gdProject,
currentFileMetadata: ?FileMetadata,
closeProject: () => Promise<void>,
|};

const ProjectsWidget = (props: Props) => {
return (
<I18n>
{({ i18n }) => (
<DashboardWidget gridSize={3} title={<Trans>Projects</Trans>}>
<ProjectFileList {...props} i18n={i18n} />
</DashboardWidget>
)}
</I18n>
);
};

export default ProjectsWidget;
Loading

0 comments on commit b6cc647

Please sign in to comment.