Store an achievement when finishing a tutorial (#6431)

This commit is contained in:
Florian Rival
2024-03-07 16:18:50 +01:00
committed by GitHub
parent 713d437b70
commit ca3bb40e96
2 changed files with 80 additions and 10 deletions

View File

@@ -13,6 +13,10 @@ import {
type EditorIdentifier,
isMiniTutorial,
} from '../Utils/GDevelopServices/InAppTutorial';
import {
createOrEnsureBadgeForUser,
getTutorialCompletedAchievementId,
} from '../Utils/GDevelopServices/Badge';
import InAppTutorialDialog from './InAppTutorialDialog';
import InAppTutorialStepDisplayer from './InAppTutorialStepDisplayer';
import { selectMessageByLocale } from '../Utils/i18n/MessageByLocale';
@@ -24,8 +28,11 @@ import {
isMuiCheckbox,
} from '../UI/MaterialUISpecificUtil';
import PreferencesContext from '../MainFrame/Preferences/PreferencesContext';
import AuthenticatedUserContext from '../Profile/AuthenticatedUserContext';
import AuthenticatedUserContext, {
type AuthenticatedUser,
} from '../Profile/AuthenticatedUserContext';
import { useScreenType } from '../UI/Responsive/ScreenTypeMeasurer';
import { retryIfFailed } from '../Utils/RetryIfFailed';
const textInterpolationProjectDataAccessors = {
instancesCount: 'instancesCount:',
@@ -356,6 +363,56 @@ const gatherProjectDataOnMultipleSteps = ({
return newData;
};
const useGiveTrivialBadgeWhenTutorialIsFinished = ({
authenticatedUser,
displayEndDialog,
tutorial,
}: {
authenticatedUser: AuthenticatedUser,
displayEndDialog: boolean,
tutorial: InAppTutorial,
}) => {
// Destructure the user data to avoid the effect to run at every change of the user
// which is unrelated to badges or the user profile.
const {
badges,
onBadgesChanged,
profile,
getAuthorizationHeader,
} = authenticatedUser;
React.useEffect(
() => {
(async () => {
if (!profile || !displayEndDialog) return;
try {
// Give a (trivial) badge when a tutorial is finished.
await retryIfFailed({ times: 3 }, () =>
createOrEnsureBadgeForUser(
{ badges, onBadgesChanged, profile, getAuthorizationHeader },
getTutorialCompletedAchievementId(tutorial.id)
)
);
} catch (error) {
console.error(
`Couldn't create completion badge for tutorial ${tutorial.id}.`,
error
);
}
})();
},
[
displayEndDialog,
badges,
onBadgesChanged,
profile,
getAuthorizationHeader,
tutorial.id,
]
);
};
type Props = {|
tutorial: InAppTutorial,
startStepIndex: number,
@@ -1065,6 +1122,12 @@ const InAppTutorialOrchestrator = React.forwardRef<
[checkIfWrongEditor, currentEditor, currentSceneName]
);
useGiveTrivialBadgeWhenTutorialIsFinished({
authenticatedUser,
tutorial,
displayEndDialog,
});
return (
<I18n>
{({ i18n }) => (

View File

@@ -1,6 +1,7 @@
// @flow
import axios from 'axios';
import { GDevelopUserApi } from './ApiConfigs';
import { type Profile } from './Authentication';
import { type AuthenticatedUser } from '../../Profile/AuthenticatedUserContext';
import { extractGDevelopApiErrorStatusAndCode } from './Errors';
@@ -12,6 +13,8 @@ export const TRIVIAL_FIRST_WEB_EXPORT = 'trivial_first-web-export';
export const TRIVIAL_FIRST_EXTENSION = 'trivial_first-extension';
export const TRIVIAL_FIRST_EFFECT = 'trivial_first-effect';
export const TRIVIAL_FIRST_DEBUG = 'trivial_first-debug';
export const getTutorialCompletedAchievementId = (tutorialId: string) =>
'trivial_in-app-tutorial-completed_' + tutorialId;
export type Badge = {|
seen: boolean,
@@ -40,22 +43,26 @@ const isAchievementAlreadyClaimed = (
return badges.map(badge => badge.achievementId).includes(achievementId);
};
const createOrEnsureBadgeForUser = async (
authenticatedUser: AuthenticatedUser,
achievementId: string
): Promise<?Badge> => {
const {
export const createOrEnsureBadgeForUser = async (
{
badges,
firebaseUser,
profile,
getAuthorizationHeader,
onBadgesChanged,
} = authenticatedUser;
if (!badges || !firebaseUser) return null;
}: {
badges: ?Array<Badge>,
profile: ?Profile,
getAuthorizationHeader: () => Promise<string>,
onBadgesChanged: () => Promise<void>,
},
achievementId: string
): Promise<?Badge> => {
if (!badges || !profile) return null;
if (isAchievementAlreadyClaimed(badges, achievementId)) {
return null;
}
const userId = firebaseUser.uid;
const userId = profile.id;
try {
const authorizationHeader = await getAuthorizationHeader();
const response = await axios.post(