Skip to content

Commit 35833e6

Browse files
Allow adding default player authentication when creating a project (#4714)
* New checkbox in the Project Creation dialog, which will apply best practices for player authentication on the empty project
1 parent 7436032 commit 35833e6

File tree

8 files changed

+125
-14
lines changed

8 files changed

+125
-14
lines changed

Extensions/PlayerAuthentication/playerauthenticationcomponents.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ namespace gdjs {
1919
'If the window did not open, please check your pop-up blocker and click the button below to try again.',
2020
}
2121
: {
22-
title: 'Your game is not registered!',
22+
title: 'Publish your game!',
2323
text1:
24-
'In order to use player authentication, this game must be registered with GDevelop Services first.',
25-
text2: 'Head to your Game Dashboard, then try again.',
24+
"GDevelop's player accounts are only available for published games.",
25+
text2:
26+
'Click the button below to learn how to publish your game then try again.',
2627
};
2728

2829
/**
@@ -119,7 +120,8 @@ namespace gdjs {
119120
export const addAuthenticationTextsToLoadingContainer = (
120121
loaderContainer: HTMLDivElement,
121122
platform,
122-
isGameRegistered
123+
isGameRegistered,
124+
wikiOpenAction
123125
) => {
124126
const textContainer: HTMLDivElement = document.createElement('div');
125127
textContainer.id = 'authentication-container-texts';
@@ -151,8 +153,20 @@ namespace gdjs {
151153
textContainer.appendChild(text2);
152154

153155
if (!isGameRegistered) {
154-
// Remove the loader.
156+
// Remove the loader and add the wiki link.
155157
loaderContainer.innerHTML = '';
158+
159+
if (wikiOpenAction) {
160+
const link = document.createElement('a');
161+
addTouchAndClickEventListeners(link, wikiOpenAction);
162+
link.innerText = 'How to publish my game';
163+
link.style.color = '#0078d4';
164+
link.style.textDecoration = 'none';
165+
link.style.textDecoration = 'underline';
166+
link.style.cursor = 'pointer';
167+
168+
textContainer.appendChild(link);
169+
}
156170
}
157171

158172
loaderContainer.prepend(textContainer);
@@ -170,7 +184,7 @@ namespace gdjs {
170184
) => {
171185
const link = document.createElement('a');
172186
addTouchAndClickEventListeners(link, onClick);
173-
link.innerText = 'Click here to authenticate';
187+
link.innerText = 'Try again';
174188
link.style.color = '#0078d4';
175189
link.style.textDecoration = 'none';
176190
link.style.textDecoration = 'underline';

Extensions/PlayerAuthentication/playerauthenticationtools.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,10 +622,19 @@ namespace gdjs {
622622
checkIfGameIsRegistered(runtimeScene.getGame(), _gameId)
623623
.then((isGameRegistered) => {
624624
if (_authenticationLoaderContainer) {
625+
const electron = runtimeScene.getGame().getRenderer().getElectron();
626+
const wikiOpenAction = electron
627+
? () =>
628+
electron.shell.openExternal(
629+
'https://wiki.gdevelop.io/gdevelop5/publishing/web'
630+
)
631+
: null; // Only show a link if we're on electron.
632+
625633
_authenticationTextContainer = authComponents.addAuthenticationTextsToLoadingContainer(
626634
_authenticationLoaderContainer,
627635
platform,
628-
isGameRegistered
636+
isGameRegistered,
637+
wikiOpenAction
629638
);
630639
}
631640
if (isGameRegistered) {

newIDE/app/src/MainFrame/index.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ import PreferencesContext, {
8585
import { getFunctionNameFromType } from '../EventsFunctionsExtensionsLoader';
8686
import { type ExportDialogWithoutExportsProps } from '../Export/ExportDialog';
8787
import CreateProjectDialog, {
88+
createNewProjectWithDefaultLogin,
8889
type NewProjectSetup,
8990
} from '../ProjectCreation/CreateProjectDialog';
9091
import {
@@ -162,6 +163,7 @@ import { type InAppTutorialOrchestratorInterface } from '../InAppTutorial/InAppT
162163
import useInAppTutorialOrchestrator from '../InAppTutorial/useInAppTutorialOrchestrator';
163164
import { FLING_GAME_IN_APP_TUTORIAL_ID } from '../InAppTutorial/InAppTutorialProvider';
164165
import TabsTitlebar from './TabsTitlebar';
166+
import { registerGame } from '../Utils/GDevelopServices/Game';
165167

166168
const GD_STARTUP_TIMES = global.GD_STARTUP_TIMES || [];
167169

@@ -2460,6 +2462,8 @@ const MainFrame = (props: Props) => {
24602462
i18n,
24612463
exampleShortHeader: selectedExampleShortHeader,
24622464
})
2465+
: newProjectSetup.allowPlayersToLogIn
2466+
? await createNewProjectWithDefaultLogin()
24632467
: await createNewProject();
24642468

24652469
if (!source) return; // New project creation aborted.
@@ -2512,6 +2516,30 @@ const MainFrame = (props: Props) => {
25122516
currentProject.setName(newProjectSetup.projectName);
25132517
}
25142518

2519+
if (authenticatedUser.profile) {
2520+
// if the user is connected, try to register the game to avoid
2521+
// any gdevelop services to ask the user to register the game.
2522+
// (for instance, leaderboards, player authentication, ...)
2523+
try {
2524+
await registerGame(
2525+
authenticatedUser.getAuthorizationHeader,
2526+
authenticatedUser.profile.id,
2527+
{
2528+
gameId: currentProject.getProjectUuid(),
2529+
authorName: currentProject.getAuthor() || 'Unspecified publisher',
2530+
gameName: currentProject.getName() || 'Untitled game',
2531+
templateSlug: currentProject.getTemplateSlug(),
2532+
}
2533+
);
2534+
} catch (error) {
2535+
// Do not prevent the user from opening the game if the registration failed.
2536+
console.error(
2537+
'Unable to register the game to the user profile, the game will not be listed in the user profile.',
2538+
error
2539+
);
2540+
}
2541+
}
2542+
25152543
const destinationStorageProviderOperations = getStorageProviderOperations(
25162544
newProjectSetup.storageProvider
25172545
);

newIDE/app/src/Profile/CreateProfile.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { Trans } from '@lingui/macro';
44
import * as React from 'react';
55
import FlatButton from '../UI/FlatButton';
66
import RaisedButton from '../UI/RaisedButton';
7-
import { Column, LargeSpacer } from '../UI/Grid';
7+
import { Column } from '../UI/Grid';
88
import Text from '../UI/Text';
9-
import { ResponsiveLineStackLayout } from '../UI/Layout';
9+
import { ColumnStackLayout, ResponsiveLineStackLayout } from '../UI/Layout';
1010

1111
const styles = {
1212
container: {
@@ -30,8 +30,8 @@ const CreateProfile = ({
3030
}: Props) => (
3131
<Column alignItems="center">
3232
<div style={styles.container}>
33-
<Column>
34-
<Text>
33+
<ColumnStackLayout>
34+
<Text noMargin>
3535
{message || (
3636
<Trans>
3737
You are not connected. Create an account to build your game for
@@ -40,7 +40,6 @@ const CreateProfile = ({
4040
</Trans>
4141
)}
4242
</Text>
43-
<LargeSpacer />
4443
<ResponsiveLineStackLayout justifyContent="center" noMargin>
4544
<RaisedButton
4645
id="create-account-button"
@@ -50,7 +49,7 @@ const CreateProfile = ({
5049
/>
5150
<FlatButton label={<Trans>Login</Trans>} onClick={onLogin} />
5251
</ResponsiveLineStackLayout>
53-
</Column>
52+
</ColumnStackLayout>
5453
</div>
5554
</Column>
5655
);

newIDE/app/src/ProjectCreation/CreateProjectDialog.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export type NewProjectSetup = {|
3737
width: number,
3838
orientation: 'landscape' | 'portrait' | 'default',
3939
optimizeForPixelArt: boolean,
40+
allowPlayersToLogIn: boolean,
4041
|};
4142

4243
export type NewProjectSource = {|
@@ -56,6 +57,22 @@ export const createNewProject = async (): Promise<?NewProjectSource> => {
5657
};
5758
};
5859

60+
export const createNewProjectWithDefaultLogin = async (): Promise<?NewProjectSource> => {
61+
const url =
62+
'https://resources.gdevelop.io/examples-database/login-template.json';
63+
sendNewGameCreated({
64+
exampleUrl: url,
65+
exampleSlug: 'login-template',
66+
});
67+
return {
68+
project: null,
69+
storageProvider: UrlStorageProvider,
70+
fileMetadata: {
71+
fileIdentifier: url,
72+
},
73+
};
74+
};
75+
5976
export const createNewProjectFromExampleShortHeader = async ({
6077
i18n,
6178
exampleShortHeader,

newIDE/app/src/ProjectCreation/NewProjectSetupDialog.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import SelectField from '../UI/SelectField';
1818
import SelectOption from '../UI/SelectOption';
1919
import CreateProfile from '../Profile/CreateProfile';
2020
import Paper from '../UI/Paper';
21-
import { Line } from '../UI/Grid';
21+
import { Line, Spacer } from '../UI/Grid';
2222
import LeftLoader from '../UI/LeftLoader';
2323
import {
2424
checkIfHasTooManyCloudProjects,
@@ -28,6 +28,8 @@ import { SubscriptionSuggestionContext } from '../Profile/Subscription/Subscript
2828
import optionalRequire from '../Utils/OptionalRequire';
2929
import PreferencesContext from '../MainFrame/Preferences/PreferencesContext';
3030
import Checkbox from '../UI/Checkbox';
31+
import Link from '../UI/Link';
32+
import Window from '../Utils/Window';
3133

3234
const electron = optionalRequire('electron');
3335
const remote = optionalRequire('@electron/remote');
@@ -106,6 +108,9 @@ const NewProjectSetupDialog = ({
106108
const [optimizeForPixelArt, setOptimizeForPixelArt] = React.useState<boolean>(
107109
false
108110
);
111+
const [allowPlayersToLogIn, setAllowPlayersToLogIn] = React.useState<boolean>(
112+
false
113+
);
109114
const newProjectsDefaultFolder = app
110115
? findEmptyPathInWorkspaceFolder(app, values.newProjectsDefaultFolder || '')
111116
: '';
@@ -170,6 +175,7 @@ const NewProjectSetupDialog = ({
170175
width,
171176
orientation,
172177
optimizeForPixelArt,
178+
allowPlayersToLogIn,
173179
});
174180
},
175181
[
@@ -181,6 +187,7 @@ const NewProjectSetupDialog = ({
181187
saveAsLocation,
182188
resolutionOption,
183189
optimizeForPixelArt,
190+
allowPlayersToLogIn,
184191
]
185192
);
186193

@@ -332,6 +339,31 @@ const NewProjectSetupDialog = ({
332339
}}
333340
disabled={isOpening}
334341
/>
342+
<Checkbox
343+
checked={allowPlayersToLogIn}
344+
label={<Trans>Allow players to authenticate in-game</Trans>}
345+
onCheck={(e, checked) => {
346+
setAllowPlayersToLogIn(checked);
347+
}}
348+
disabled={isOpening}
349+
tooltipOrHelperText={
350+
<Line noMargin>
351+
<Trans>Learn more about</Trans>
352+
<Spacer />
353+
<Link
354+
href="https://wiki.gdevelop.io/gdevelop5/all-features/player-authentication"
355+
onClick={() =>
356+
Window.openExternalURL(
357+
'https://wiki.gdevelop.io/gdevelop5/all-features/player-authentication'
358+
)
359+
}
360+
>
361+
<Trans>player authentication</Trans>
362+
</Link>
363+
.
364+
</Line>
365+
}
366+
/>
335367
</ColumnStackLayout>
336368
)}
337369
{limits && hasTooManyCloudProjects ? (

newIDE/app/src/UI/Checkbox.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { makeStyles } from '@material-ui/core/styles';
44
import FormControlLabel from '@material-ui/core/FormControlLabel';
55
import FormGroup from '@material-ui/core/FormGroup';
66
import MUICheckbox from '@material-ui/core/Checkbox';
7+
import { FormHelperText } from '@material-ui/core';
78

89
// Reduce checkbox size to avoid overlapping with other checkboxes.
910
const useStyles = makeStyles({
@@ -28,6 +29,7 @@ type Props = {|
2829
label?: ?React.Node,
2930
checked: boolean,
3031
onCheck?: (e: {||}, checked: boolean) => void | Promise<void>,
32+
tooltipOrHelperText?: React.Node,
3133
checkedIcon?: React.Node,
3234
uncheckedIcon?: React.Node,
3335
disabled?: boolean,
@@ -69,6 +71,9 @@ const Checkbox = (props: Props) => {
6971
cursor: 'default',
7072
}}
7173
/>
74+
{props.tooltipOrHelperText && (
75+
<FormHelperText>{props.tooltipOrHelperText}</FormHelperText>
76+
)}
7277
</FormGroup>
7378
) : (
7479
checkbox

newIDE/app/src/stories/componentStories/UI/Checkboxes.stories.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ export const Default = () => {
4646
checkedIcon={<Visibility />}
4747
uncheckedIcon={<VisibilityOff />}
4848
/>
49+
<LargeSpacer />
50+
<Checkbox
51+
checked={true}
52+
onCheck={(e, value) => {}}
53+
label="With some helper text"
54+
tooltipOrHelperText="This is some helper text"
55+
/>
4956
</Column>
5057
<Column alignItems="flex-start" expand>
5158
<Text size="block-title">Inline checkboxes</Text>

0 commit comments

Comments
 (0)