diff --git a/newIDE/app/src/AssetStore/PrivateAssets/PrivateAssetPackInformationPage.js b/newIDE/app/src/AssetStore/PrivateAssets/PrivateAssetPackInformationPage.js index 097fec257137..09f26460956a 100644 --- a/newIDE/app/src/AssetStore/PrivateAssets/PrivateAssetPackInformationPage.js +++ b/newIDE/app/src/AssetStore/PrivateAssets/PrivateAssetPackInformationPage.js @@ -41,6 +41,7 @@ import { formatProductPrice } from '../ProductPriceTag'; import AuthenticatedUserContext from '../../Profile/AuthenticatedUserContext'; import { PrivateAssetPackTile, PromoBundleAssetPackCard } from '../ShopTiles'; import { AssetStoreContext } from '../AssetStoreContext'; +import { extractGDevelopApiErrorStatusAndCode } from '../../Utils/GDevelopServices/Errors'; const cellSpacing = 8; @@ -290,7 +291,10 @@ const PrivateAssetPackInformationPage = ({ setAssetPack(assetPack); setSellerPublicProfile(profile); } catch (error) { - if (error.response && error.response.status === 404) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); + if (extractedStatusAndCode && extractedStatusAndCode.status === 404) { setErrorText( Asset pack not found - An error occurred, please try again diff --git a/newIDE/app/src/AssetStore/PrivateAssets/PrivateAssetPackPurchaseDialog.js b/newIDE/app/src/AssetStore/PrivateAssets/PrivateAssetPackPurchaseDialog.js index daebbc79028f..48a2071e4f49 100644 --- a/newIDE/app/src/AssetStore/PrivateAssets/PrivateAssetPackPurchaseDialog.js +++ b/newIDE/app/src/AssetStore/PrivateAssets/PrivateAssetPackPurchaseDialog.js @@ -25,6 +25,7 @@ import { purchaseAppStoreProduct, } from '../../Utils/AppStorePurchases'; import Form from '../../UI/Form'; +import { extractGDevelopApiErrorStatusAndCode } from '../../Utils/GDevelopServices/Errors'; const PasswordPromptDialog = (props: { passwordValue: string, @@ -131,10 +132,13 @@ const PrivateAssetPackPurchaseDialog = ({ }); Window.openExternalURL(checkoutUrl); } catch (error) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); if ( - error.response && - error.response.status === 403 && - error.response.data.code === 'auth/wrong-password' + extractedStatusAndCode && + extractedStatusAndCode.status === 403 && + extractedStatusAndCode.code === 'auth/wrong-password' ) { await showAlert({ title: t`Operation not allowed`, diff --git a/newIDE/app/src/AssetStore/PrivateAssets/PrivateAssetsAuthorizationProvider.js b/newIDE/app/src/AssetStore/PrivateAssets/PrivateAssetsAuthorizationProvider.js index 3e644740ee25..c929c2bb129f 100644 --- a/newIDE/app/src/AssetStore/PrivateAssets/PrivateAssetsAuthorizationProvider.js +++ b/newIDE/app/src/AssetStore/PrivateAssets/PrivateAssetsAuthorizationProvider.js @@ -18,6 +18,7 @@ import { getAuthorizationTokenForPrivateAssets, } from '../../Utils/GDevelopServices/Shop'; import PrivateAssetsAuthorizationContext from './PrivateAssetsAuthorizationContext'; +import { extractGDevelopApiErrorStatusAndCode } from '../../Utils/GDevelopServices/Errors'; type Props = {| children: React.Node |}; @@ -94,7 +95,10 @@ const PrivateAssetsAuthorizationProvider = ({ children }: Props) => { }); return asset; } catch (error) { - if (error.response && error.response.status === 404) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); + if (extractedStatusAndCode && extractedStatusAndCode.status === 404) { // If the token is expired, fetch a new one and try again. token = await fetchAuthorizationToken(userId); const asset = await getPrivateAsset(assetShortHeader, token, { diff --git a/newIDE/app/src/AssetStore/PrivateGameTemplates/PrivateGameTemplateInformationPage.js b/newIDE/app/src/AssetStore/PrivateGameTemplates/PrivateGameTemplateInformationPage.js index 7fcc48899855..886becb81894 100644 --- a/newIDE/app/src/AssetStore/PrivateGameTemplates/PrivateGameTemplateInformationPage.js +++ b/newIDE/app/src/AssetStore/PrivateGameTemplates/PrivateGameTemplateInformationPage.js @@ -37,6 +37,7 @@ import { formatProductPrice } from '../ProductPriceTag'; import AuthenticatedUserContext from '../../Profile/AuthenticatedUserContext'; import { capitalize } from 'lodash'; import FlatButton from '../../UI/FlatButton'; +import { extractGDevelopApiErrorStatusAndCode } from '../../Utils/GDevelopServices/Errors'; const styles = { disabledText: { opacity: 0.6 }, @@ -123,7 +124,10 @@ const PrivateGameTemplateInformationPage = ({ setGameTemplate(gameTemplate); setSellerPublicProfile(profile); } catch (error) { - if (error.response && error.response.status === 404) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); + if (extractedStatusAndCode && extractedStatusAndCode.status === 404) { setErrorText( Game template not found - An error occurred, please try again diff --git a/newIDE/app/src/AssetStore/PrivateGameTemplates/PrivateGameTemplatePurchaseDialog.js b/newIDE/app/src/AssetStore/PrivateGameTemplates/PrivateGameTemplatePurchaseDialog.js index c0dd5774a9cd..d213f16dc294 100644 --- a/newIDE/app/src/AssetStore/PrivateGameTemplates/PrivateGameTemplatePurchaseDialog.js +++ b/newIDE/app/src/AssetStore/PrivateGameTemplates/PrivateGameTemplatePurchaseDialog.js @@ -25,6 +25,7 @@ import { purchaseAppStoreProduct, } from '../../Utils/AppStorePurchases'; import Form from '../../UI/Form'; +import { extractGDevelopApiErrorStatusAndCode } from '../../Utils/GDevelopServices/Errors'; const PasswordPromptDialog = (props: { passwordValue: string, @@ -131,10 +132,13 @@ const PrivateGameTemplatePurchaseDialog = ({ }); Window.openExternalURL(checkoutUrl); } catch (error) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); if ( - error.response && - error.response.status === 403 && - error.response.data.code === 'auth/wrong-password' + extractedStatusAndCode && + extractedStatusAndCode.status === 403 && + extractedStatusAndCode.code === 'auth/wrong-password' ) { await showAlert({ title: t`Operation not allowed`, diff --git a/newIDE/app/src/ExportAndShare/ShareDialog/ExportLauncher.js b/newIDE/app/src/ExportAndShare/ShareDialog/ExportLauncher.js index 5c66a47772a2..4898cd4d3a31 100644 --- a/newIDE/app/src/ExportAndShare/ShareDialog/ExportLauncher.js +++ b/newIDE/app/src/ExportAndShare/ShareDialog/ExportLauncher.js @@ -35,6 +35,7 @@ import { addCreateBadgePreHookIfNotClaimed, TRIVIAL_FIRST_WEB_EXPORT, } from '../../Utils/GDevelopServices/Badge'; +import { extractGDevelopApiErrorStatusAndCode } from '../../Utils/GDevelopServices/Errors'; type State = {| exportStep: BuildStep, @@ -169,8 +170,11 @@ export default class ExportLauncher extends Component { try { // Try to fetch the game to see if it's registered but do not do anything with it. await getGame(getAuthorizationHeader, userId, gameId); - } catch (err) { - if (err.response.status === 404) { + } catch (error) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); + if (extractedStatusAndCode && extractedStatusAndCode.code === 404) { // If the game is not registered, register it before launching the export. const authorName = this.props.project.getAuthor() || 'Unspecified publisher'; diff --git a/newIDE/app/src/ExportAndShare/ShareDialog/InviteHome.js b/newIDE/app/src/ExportAndShare/ShareDialog/InviteHome.js index e328911503f4..b67621f88a48 100644 --- a/newIDE/app/src/ExportAndShare/ShareDialog/InviteHome.js +++ b/newIDE/app/src/ExportAndShare/ShareDialog/InviteHome.js @@ -33,6 +33,7 @@ import useAlertDialog from '../../UI/Alert/useAlertDialog'; import SelectField from '../../UI/SelectField'; import SelectOption from '../../UI/SelectOption'; import Form from '../../UI/Form'; +import { extractGDevelopApiErrorStatusAndCode } from '../../Utils/GDevelopServices/Errors'; export const emailRegex = /^(.+)@(.+)$/; @@ -177,11 +178,14 @@ const InviteHome = ({ cloudProjectId }: Props) => { setProjectUserAcls(collaboratorProjectUserAcls); } catch (error) { console.error('Unable to fetch the project user acls', error); - if (error.response && error.response.status === 404) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); + if (extractedStatusAndCode && extractedStatusAndCode.status === 404) { setFetchError('project-not-found'); return; } - if (error.response && error.response.status === 403) { + if (extractedStatusAndCode && extractedStatusAndCode.status === 403) { setFetchError('project-not-owned'); return; } @@ -263,35 +267,39 @@ const InviteHome = ({ cloudProjectId }: Props) => { await fetchProjectUserAcls(); } catch (error) { console.error('Unable to add collaborator', error); - if (error.response && error.response.status === 400) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); + if (extractedStatusAndCode && extractedStatusAndCode.status === 400) { if ( - error.response.data.code === + extractedStatusAndCode.code === 'project-user-acl-creation/user-already-added' ) { setAddError('user-already-added'); return; } if ( - error.response.data.code === 'project-user-acl-creation/user-is-owner' + extractedStatusAndCode.code === + 'project-user-acl-creation/user-is-owner' ) { setAddError('user-owner'); return; } } - if (error.response && error.response.status === 404) { + if (extractedStatusAndCode && extractedStatusAndCode.status === 404) { setAddError('user-not-found'); return; } - if (error.response && error.response.status === 403) { + if (extractedStatusAndCode && extractedStatusAndCode.status === 403) { if ( - error.response.data.code === + extractedStatusAndCode.code === 'project-user-acl-creation/collaborators-not-allowed' ) { setAddError('not-allowed'); return; } if ( - error.response.data.code === + extractedStatusAndCode.code === 'project-user-acl-creation/too-many-guest-collaborators-on-project' ) { setAddError('max-guest-collaborators'); diff --git a/newIDE/app/src/ExportAndShare/ShareDialog/index.js b/newIDE/app/src/ExportAndShare/ShareDialog/index.js index 2d6f3befeba6..9ac05095b7df 100644 --- a/newIDE/app/src/ExportAndShare/ShareDialog/index.js +++ b/newIDE/app/src/ExportAndShare/ShareDialog/index.js @@ -19,6 +19,7 @@ import PreferencesContext from '../../MainFrame/Preferences/PreferencesContext'; import { type FileMetadata, type StorageProvider } from '../../ProjectsStorage'; import { useOnlineStatus } from '../../Utils/OnlineStatus'; import ErrorBoundary from '../../UI/ErrorBoundary'; +import { extractGDevelopApiErrorStatusAndCode } from '../../Utils/GDevelopServices/Errors'; export type ShareTab = 'invite' | 'publish'; export type ExporterSection = 'browser' | 'desktop' | 'mobile'; @@ -184,13 +185,16 @@ const ShareDialog = ({ project.getProjectUuid() ); setGame(game); - } catch (err) { - console.error('Unable to load the game', err); - if (err && err.status === 404) { + } catch (error) { + console.error('Unable to load the game', error); + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); + if (extractedStatusAndCode && extractedStatusAndCode.status === 404) { setGameError('not-found'); return; } - if (err && err.status === 403) { + if (extractedStatusAndCode && extractedStatusAndCode.status === 403) { setGameError('not-owned'); return; } diff --git a/newIDE/app/src/GameDashboard/GameRegistration.js b/newIDE/app/src/GameDashboard/GameRegistration.js index e8a5d93f433f..6da3a768c6cf 100644 --- a/newIDE/app/src/GameDashboard/GameRegistration.js +++ b/newIDE/app/src/GameDashboard/GameRegistration.js @@ -13,6 +13,7 @@ import { getGame, registerGame, } from '../Utils/GDevelopServices/Game'; +import { extractGDevelopApiErrorStatusAndCode } from '../Utils/GDevelopServices/Errors'; export type GameRegistrationProps = {| project: ?gdProject, @@ -65,19 +66,25 @@ export const GameRegistration = ({ ); setUnavailableReason(null); setGame(game); - } catch (err) { - console.error(err); - if (err.response) { - if (err.response.status === 403) { + } catch (error) { + console.error( + `Unable to get the game ${project.getProjectUuid()}`, + error + ); + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); + if (extractedStatusAndCode) { + if (extractedStatusAndCode.status === 403) { setUnavailableReason('unauthorized'); return; - } else if (err.response.status === 404) { + } else if (extractedStatusAndCode.status === 404) { setUnavailableReason('not-existing'); return; } } - setError(err); + setError(error); } }, [project, getAuthorizationHeader, profile] diff --git a/newIDE/app/src/GameDashboard/LeaderboardAdmin/index.js b/newIDE/app/src/GameDashboard/LeaderboardAdmin/index.js index feb65ca57aaa..c3987e9376b7 100644 --- a/newIDE/app/src/GameDashboard/LeaderboardAdmin/index.js +++ b/newIDE/app/src/GameDashboard/LeaderboardAdmin/index.js @@ -72,6 +72,7 @@ import MaxLeaderboardCountAlertMessage from './MaxLeaderboardCountAlertMessage'; import useAlertDialog from '../../UI/Alert/useAlertDialog'; import Paper from '../../UI/Paper'; import SwitchHorizontal from '../../UI/CustomSvgIcons/SwitchHorizontal'; +import { extractGDevelopApiErrorStatusAndCode } from '../../Utils/GDevelopServices/Errors'; type Props = {| onLoading: boolean => void, @@ -312,12 +313,15 @@ export const LeaderboardAdmin = ({ setApiError(null); try { await listLeaderboards(); - } catch (err) { - if (err.response && err.response.status === 404) { + } catch (error) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); + if (extractedStatusAndCode && extractedStatusAndCode.status === 404) { setDisplayGameRegistration(true); return; } - console.error('An error occurred when fetching leaderboards', err); + console.error('An error occurred when fetching leaderboards', error); setApiError({ action: 'leaderboardsFetching', message: ( @@ -406,12 +410,15 @@ export const LeaderboardAdmin = ({ setApiError(null); try { await resetLeaderboard(); - } catch (err) { - console.error('An error occurred when resetting leaderboard', err); + } catch (error) { + console.error('An error occurred when resetting leaderboard', error); + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); setApiError({ action: 'leaderboardReset', message: - err.status && err.status === 409 ? ( + extractedStatusAndCode && extractedStatusAndCode.status === 409 ? ( This leaderboard is already resetting, please wait a bit, close the dialog, come back and try again. diff --git a/newIDE/app/src/MainFrame/EditorContainers/HomePage/BuildSection/ProjectFileListItem.js b/newIDE/app/src/MainFrame/EditorContainers/HomePage/BuildSection/ProjectFileListItem.js index 793a075e3ee9..8afb9ab00c21 100644 --- a/newIDE/app/src/MainFrame/EditorContainers/HomePage/BuildSection/ProjectFileListItem.js +++ b/newIDE/app/src/MainFrame/EditorContainers/HomePage/BuildSection/ProjectFileListItem.js @@ -36,6 +36,7 @@ import { type LastModifiedInfo } from './utils'; import DotBadge from '../../../../UI/DotBadge'; import { type FileMetadata } from '../../../../ProjectsStorage'; import StatusIndicator from './StatusIndicator'; +import { extractGDevelopApiErrorStatusAndCode } from '../../../../Utils/GDevelopServices/Errors'; const electron = optionalRequire('electron'); const path = optionalRequire('path'); @@ -260,8 +261,11 @@ export const ProjectFileListItem = ({ await deleteCloudProject(authenticatedUser, fileMetadata.fileIdentifier); authenticatedUser.onCloudProjectsChanged(); } catch (error) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); const message = - error.response && error.response.status === 403 + extractedStatusAndCode && extractedStatusAndCode.status === 403 ? t`You don't have permissions to delete this project.` : t`An error occurred when saving the project. Please try again later.`; showAlert({ diff --git a/newIDE/app/src/MainFrame/EditorContainers/HomePage/TeamSection/TeamSection.spec.js b/newIDE/app/src/MainFrame/EditorContainers/HomePage/TeamSection/TeamSection.spec.js index 440db9f1ef60..801234a3f2ee 100644 --- a/newIDE/app/src/MainFrame/EditorContainers/HomePage/TeamSection/TeamSection.spec.js +++ b/newIDE/app/src/MainFrame/EditorContainers/HomePage/TeamSection/TeamSection.spec.js @@ -9,6 +9,7 @@ const getDefaultUser = ({ id }: { id: string }) => ({ description: null, username: null, donateLink: null, + discordUsername: null, email: `${id}@email.net`, getGameStatsEmail: false, getNewsletterEmail: false, diff --git a/newIDE/app/src/MainFrame/index.js b/newIDE/app/src/MainFrame/index.js index 41d662e0be23..22de38246ca8 100644 --- a/newIDE/app/src/MainFrame/index.js +++ b/newIDE/app/src/MainFrame/index.js @@ -184,6 +184,7 @@ import useEditorTabsStateSaving from './EditorTabs/UseEditorTabsStateSaving'; import { type PrivateGameTemplateListingData } from '../Utils/GDevelopServices/Shop'; import PixiResourcesLoader from '../ObjectsRendering/PixiResourcesLoader'; import useResourcesWatcher from './ResourcesWatcher'; +import { extractGDevelopApiErrorStatusAndCode } from '../Utils/GDevelopServices/Errors'; const GD_STARTUP_TIMES = global.GD_STARTUP_TIMES || []; const gd: libGDevelop = global.gd; @@ -2489,8 +2490,11 @@ const MainFrame = (props: Props) => { _replaceSnackMessage(i18n._(t`Project properly saved`)); } } catch (error) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); const message = - error.response && error.response.status === 403 + extractedStatusAndCode && extractedStatusAndCode.status === 403 ? t`You don't have permissions to save this project. Please choose another location.` : t`An error occurred when saving the project. Please try again later.`; showAlert({ diff --git a/newIDE/app/src/Profile/AuthenticatedUserProfileDetails.js b/newIDE/app/src/Profile/AuthenticatedUserProfileDetails.js index 63543c435187..b74511b62506 100644 --- a/newIDE/app/src/Profile/AuthenticatedUserProfileDetails.js +++ b/newIDE/app/src/Profile/AuthenticatedUserProfileDetails.js @@ -58,6 +58,7 @@ const AuthenticatedUserProfileDetails = ({ ? { ...authenticatedUser.profile, email: firebaseUser.email } : null } + subscription={authenticatedUser.subscription} isAuthenticatedUserProfile onOpenChangeEmailDialog={onOpenChangeEmailDialog} onOpenEditProfileDialog={onOpenEditProfileDialog} diff --git a/newIDE/app/src/Profile/AuthenticatedUserProvider.js b/newIDE/app/src/Profile/AuthenticatedUserProvider.js index f168d3289397..8f8f1d263c77 100644 --- a/newIDE/app/src/Profile/AuthenticatedUserProvider.js +++ b/newIDE/app/src/Profile/AuthenticatedUserProvider.js @@ -52,6 +52,7 @@ import { Trans } from '@lingui/macro'; import Snackbar from '@material-ui/core/Snackbar'; import RequestDeduplicator from '../Utils/RequestDeduplicator'; import { burstCloudProjectAutoSaveCache } from '../ProjectsStorage/CloudStorageProvider/CloudProjectOpener'; +import { extractGDevelopApiErrorStatusAndCode } from '../Utils/GDevelopServices/Errors'; type Props = {| authentication: Authentication, @@ -419,7 +420,10 @@ export default class AuthenticatedUserProvider extends React.Component< }, })), error => { - if (error.response.status === 404) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); + if (extractedStatusAndCode && extractedStatusAndCode.status === 404) { console.warn( 'List recommendations endpoint returned 404, user might not have completed survey.' ); @@ -773,6 +777,7 @@ export default class AuthenticatedUserProvider extends React.Component< getNewsletterEmail: payload.getNewsletterEmail, appLanguage: preferences.language, donateLink: payload.donateLink, + discordUsername: payload.discordUsername, communityLinks: payload.communityLinks, survey: payload.survey, } diff --git a/newIDE/app/src/Profile/EditProfileDialog.js b/newIDE/app/src/Profile/EditProfileDialog.js index 10371fcacaa4..96a8e9c739f4 100644 --- a/newIDE/app/src/Profile/EditProfileDialog.js +++ b/newIDE/app/src/Profile/EditProfileDialog.js @@ -14,6 +14,7 @@ import { import { communityLinksConfig, donateLinkConfig, + discordUsernameConfig, type UsernameAvailability, type CommunityLinkType, } from '../Utils/GDevelopServices/User'; @@ -111,6 +112,9 @@ const EditProfileDialog = ({ profile.description || '' ); const [donateLink, setDonateLink] = React.useState(profile.donateLink || ''); + const [discordUsername, setDiscordUsername] = React.useState( + profile.discordUsername || '' + ); const [personalWebsiteLink, setPersonalWebsiteLink] = React.useState( communityLinks.personalWebsiteLink || '' ); @@ -192,6 +196,7 @@ const EditProfileDialog = ({ getGameStatsEmail, getNewsletterEmail, donateLink, + discordUsername, communityLinks: { personalWebsiteLink, personalWebsite2Link, @@ -296,6 +301,20 @@ const EditProfileDialog = ({ isValidatingUsername={isValidatingUsername} disabled={actionInProgress} /> + Discord username} + fullWidth + translatableHintText={t`Your Discord username`} + onChange={(e, value) => { + setDiscordUsername(value); + }} + disabled={actionInProgress} + maxLength={discordUsernameConfig.maxLength} + helperMarkdownText={i18n._( + t`Add your Discord username to get access to a dedicated channel if you have a subscription! Join the [GDevelop Discord](https://discord.gg/gdevelop).` + )} + /> Bio} diff --git a/newIDE/app/src/Profile/ProfileDetails.js b/newIDE/app/src/Profile/ProfileDetails.js index 47f02c5406e4..adf8b74899f5 100644 --- a/newIDE/app/src/Profile/ProfileDetails.js +++ b/newIDE/app/src/Profile/ProfileDetails.js @@ -31,9 +31,17 @@ import Link from '../UI/Link'; import { communityLinksConfig, type CommunityLinks, + syncDiscordUsername, } from '../Utils/GDevelopServices/User'; import { PrivateAssetPackTile } from '../AssetStore/ShopTiles'; import AuthenticatedUserContext from './AuthenticatedUserContext'; +import IconButton from '../UI/IconButton'; +import Refresh from '../UI/CustomSvgIcons/Refresh'; +import Check from '../UI/CustomSvgIcons/Check'; +import { MarkdownText } from '../UI/MarkdownText'; +import useAlertDialog from '../UI/Alert/useAlertDialog'; +import { type Subscription } from '../Utils/GDevelopServices/Usage'; +import { extractGDevelopApiErrorStatusAndCode } from '../Utils/GDevelopServices/Errors'; const getAssetPackColumnsFromWidth = (width: WidthType) => { switch (width) { @@ -90,12 +98,14 @@ type DisplayedProfile = { username: ?string, description: ?string, donateLink: ?string, + discordUsername: ?string, +isEmailAutogenerated?: boolean, // the "+" allows handling both public and private profile +communityLinks?: CommunityLinks, // the "+" allows handling both public and private profile }; type Props = {| profile: ?DisplayedProfile, + subscription?: ?Subscription, isAuthenticatedUserProfile?: boolean, error?: ?Error, onRetry?: () => void, @@ -107,6 +117,7 @@ type Props = {| const ProfileDetails = ({ profile, + subscription, isAuthenticatedUserProfile, error, onRetry, @@ -115,7 +126,9 @@ const ProfileDetails = ({ assetPacksListingDatas, onAssetPackOpen, }: Props) => { + const email = profile ? profile.email : null; const donateLink = profile ? profile.donateLink : null; + const discordUsername = profile ? profile.discordUsername : null; const communityLinks = (profile && profile.communityLinks) || {}; const personalWebsiteLink = communityLinks ? communityLinks.personalWebsiteLink @@ -133,6 +146,49 @@ const ProfileDetails = ({ const discordServerLink = profile ? communityLinks.discordServerLink : null; const windowWidth = useResponsiveWindowWidth(); const { receivedAssetPacks } = React.useContext(AuthenticatedUserContext); + const { getAuthorizationHeader } = React.useContext(AuthenticatedUserContext); + const { showAlert } = useAlertDialog(); + + const [ + discordUsernameSyncStatus, + setDiscordUsernameSyncStatus, + ] = React.useState(null); + + const onSyncDiscordUsername = React.useCallback( + async () => { + if (!profile) return; + setDiscordUsernameSyncStatus('syncing'); + try { + await syncDiscordUsername(getAuthorizationHeader, profile.id); + setDiscordUsernameSyncStatus('success'); + } catch (error) { + console.error('Error while syncing discord username:', error); + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode( + error + ); + if ( + extractedStatusAndCode && + extractedStatusAndCode.status === 400 && + extractedStatusAndCode.code === + 'discord-role-update/discord-user-not-found' + ) { + showAlert({ + title: t`Discord user not found`, + message: t`Ensure you don't have any typo in your username and that you have joined the GDevelop Discord server.`, + }); + return; + } + showAlert({ + title: t`Discord username sync failed`, + message: t`Something went wrong while syncing your Discord username. Please try again later.`, + }); + } finally { + // Wait a bit to avoid spam and allow showing the success icon. + setTimeout(() => setDiscordUsernameSyncStatus(null), 3000); + } + }, + [getAuthorizationHeader, profile, showAlert] + ); const assetPackTiles = React.useMemo( () => { @@ -219,7 +275,7 @@ const ProfileDetails = ({ {({ i18n }) => ( - + )} - {isAuthenticatedUserProfile && profile.email && ( + {isAuthenticatedUserProfile && email && ( Email - {profile.email} + {email} + + )} + {(isAuthenticatedUserProfile || !!discordUsername) && ( // Always show on private profile. + + + + Discord username + + {isAuthenticatedUserProfile && + !!subscription && + !!subscription.planId && + !!discordUsername && ( + + {discordUsernameSyncStatus === 'success' ? ( + + ) : ( + + )} + + )} + + + {!isAuthenticatedUserProfile ? ( + discordUsername + ) : !discordUsername ? ( + !subscription || !subscription.planId ? ( + + ) : ( + + ) + ) : ( + <> + {discordUsername} + {(!subscription || !subscription.planId) && ( + <> + {' - '} + + + )} + + )} + )} @@ -332,9 +441,7 @@ const ProfileDetails = ({ Donate link - - {profile.donateLink || No link defined.} - + {donateLink || No link defined.} )} {isAuthenticatedUserProfile && ( diff --git a/newIDE/app/src/Profile/Subscription/SubscriptionPendingDialog.js b/newIDE/app/src/Profile/Subscription/SubscriptionPendingDialog.js index aea2dba569a5..04b5ce557af5 100644 --- a/newIDE/app/src/Profile/Subscription/SubscriptionPendingDialog.js +++ b/newIDE/app/src/Profile/Subscription/SubscriptionPendingDialog.js @@ -1,5 +1,6 @@ // @flow -import { Trans } from '@lingui/macro'; +import { Trans, t } from '@lingui/macro'; +import { I18n } from '@lingui/react'; import React from 'react'; import FlatButton from '../../UI/FlatButton'; @@ -11,6 +12,10 @@ import UserVerified from '../../UI/CustomSvgIcons/UserVerified'; import Text from '../../UI/Text'; import { useInterval } from '../../Utils/UseInterval'; import CircularProgress from '../../UI/CircularProgress'; +import TextField from '../../UI/TextField'; +import { discordUsernameConfig } from '../../Utils/GDevelopServices/User'; +import PreferencesContext from '../../MainFrame/Preferences/PreferencesContext'; +import LeftLoader from '../../UI/LeftLoader'; type Props = {| onClose: Function, @@ -33,99 +38,165 @@ export default function SubscriptionPendingDialog({ }, hasPlan ? null : 3900 ); + const currentDiscordUsername = + !!authenticatedUser && !!authenticatedUser.profile + ? authenticatedUser.profile.discordUsername + : null; + + const [discordUsername, setDiscordUsername] = React.useState(''); + const [isLoading, setIsLoading] = React.useState(false); + const { values: preferences } = React.useContext(PreferencesContext); + + const onEdit = React.useCallback( + async () => { + if (!authenticatedUser || !authenticatedUser.profile) return; + if (!discordUsername) return; + setIsLoading(true); + try { + await authenticatedUser.onEditProfile( + { + discordUsername, + }, + preferences, + { throwError: false } + ); + } catch (error) { + console.error('Error while editing profile:', error); + // Ignore errors, we will let the user retry in their profile. + } finally { + setIsLoading(false); + } + }, + [authenticatedUser, discordUsername, preferences] + ); + + const onFinish = React.useCallback( + async () => { + // If the user has edited their Discord username, send it to the server before closing. + if (!!discordUsername) { + await onEdit(); + } + onClose(); + }, + [onClose, onEdit, discordUsername] + ); return ( - Confirming your subscription} - actions={[ - hasPlan ? ( - Done!} - key="close" - primary - onClick={onClose} - /> - ) : ( - Cancel and close} - key="close" - primary={false} - onClick={onClose} - /> - ), - ]} - onRequestClose={onClose} - maxWidth="sm" - open - > - {!hasPlan ? ( - - - - - Thanks for getting a subscription and supporting GDevelop! - {' '} - {'💜'} - - - - - - - Your browser will now open to enter your payment details. - - - - - - - - - Waiting for the subscription confirmation... - - - - - - - Once you're done, come back to GDevelop and your account will be - upgraded automatically, unlocking the extra exports and online - services. - - - - - ) : ( - - - - - Thanks for getting a subscription and supporting GDevelop! - {' '} - {'💜'} - - - - - - - - Your new plan is now activated. - - - - - - - - Your account is upgraded, with the extra exports and online - services. If you wish to change later, come back to your profile - and choose another plan. - - - - + + {({ i18n }) => ( + Confirming your subscription} + actions={[ + hasPlan ? ( + + Done!} + primary + onClick={onFinish} + disabled={isLoading} + /> + + ) : ( + Cancel and close} + key="close" + primary={false} + onClick={onClose} + disabled={isLoading} + /> + ), + ]} + onRequestClose={onClose} + maxWidth="sm" + open + > + {!hasPlan ? ( + + + + + Thanks for getting a subscription and supporting GDevelop! + {' '} + {'💜'} + + + + + + + Your browser will now open to enter your payment details. + + + + + + + + + Waiting for the subscription confirmation... + + + + + + + Once you're done, come back to GDevelop and your account + will be upgraded automatically, unlocking the extra exports + and online services. + + + + + ) : ( + + + + + Thanks for getting a subscription and supporting GDevelop! + {' '} + {'💜'} + + + + + + + + Your new plan is now activated. + + + + + + + + Your account is upgraded, with the extra exports and online + services. If you wish to change later, come back to your + profile and choose another plan. + + + + {!currentDiscordUsername && ( + + { + setDiscordUsername(value); + }} + disabled={isLoading} + maxLength={discordUsernameConfig.maxLength} + helperMarkdownText={i18n._( + t`Add your Discord username to get access to a dedicated channel! Join the [GDevelop Discord](https://discord.gg/gdevelop).` + )} + /> + + )} + + )} + )} - + ); } diff --git a/newIDE/app/src/Utils/GDevelopServices/Authentication.js b/newIDE/app/src/Utils/GDevelopServices/Authentication.js index 357b1d2fb9cb..262bd67d3d8f 100644 --- a/newIDE/app/src/Utils/GDevelopServices/Authentication.js +++ b/newIDE/app/src/Utils/GDevelopServices/Authentication.js @@ -33,6 +33,7 @@ export type Profile = {| isStudent: boolean, isEmailAutogenerated: boolean, donateLink: ?string, + discordUsername: ?string, communityLinks?: CommunityLinks, survey?: UserSurvey, @@ -66,6 +67,7 @@ export type EditForm = {| +getGameStatsEmail: boolean, +getNewsletterEmail: boolean, +donateLink: string, + +discordUsername: string, +communityLinks: CommunityLinks, |}; @@ -77,6 +79,7 @@ export type PatchUserPayload = { +appLanguage?: string, +isCreator?: boolean, +donateLink?: string, + +discordUsername?: string, +communityLinks?: CommunityLinks, +survey?: UserSurvey, }; @@ -322,6 +325,7 @@ export default class Authentication { appLanguage, isCreator, donateLink, + discordUsername, communityLinks, survey, }: PatchUserPayload @@ -342,6 +346,7 @@ export default class Authentication { appLanguage, isCreator, donateLink, + discordUsername, communityLinks, survey, }, diff --git a/newIDE/app/src/Utils/GDevelopServices/Badge.js b/newIDE/app/src/Utils/GDevelopServices/Badge.js index d1c751ebdc50..13616f280806 100644 --- a/newIDE/app/src/Utils/GDevelopServices/Badge.js +++ b/newIDE/app/src/Utils/GDevelopServices/Badge.js @@ -3,6 +3,7 @@ import axios from 'axios'; import { GDevelopUserApi } from './ApiConfigs'; import { type AuthenticatedUser } from '../../Profile/AuthenticatedUserContext'; +import { extractGDevelopApiErrorStatusAndCode } from './Errors'; export const TRIVIAL_FIRST_EVENT = 'trivial_first-event'; export const TRIVIAL_FIRST_BEHAVIOR = 'trivial_first-behavior'; @@ -73,11 +74,12 @@ const createOrEnsureBadgeForUser = async ( ); onBadgesChanged(); return response.data; - } catch (err) { - if (err.response && err.response.status === 409) { + } catch (error) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode(error); + if (extractedStatusAndCode && extractedStatusAndCode.status === 409) { console.warn('Badge already exists'); } else { - throw err; + throw error; } } }; diff --git a/newIDE/app/src/Utils/GDevelopServices/Project.js b/newIDE/app/src/Utils/GDevelopServices/Project.js index 58f8b4a72729..e15ba49fd9ea 100644 --- a/newIDE/app/src/Utils/GDevelopServices/Project.js +++ b/newIDE/app/src/Utils/GDevelopServices/Project.js @@ -9,6 +9,7 @@ import PromisePool from '@supercharge/promise-pool'; import { getFileSha512TruncatedTo256 } from '../FileHasher'; import { isNativeMobileApp } from '../Platform'; import { unzipFirstEntryOfBlob } from '../Zip.js/Utils'; +import { extractGDevelopApiErrorStatusAndCode } from './Errors'; export const CLOUD_PROJECT_NAME_MAX_LENGTH = 50; export const PROJECT_RESOURCE_MAX_SIZE_IN_BYTES = 15 * 1000 * 1000; @@ -153,7 +154,8 @@ const refetchCredentialsForProjectAndRetryIfUnauthorized = async ( const response = await apiCall(); return response; } catch (error) { - if (error.response && error.response.status === 403) { + const extractedStatusAndCode = extractGDevelopApiErrorStatusAndCode(error); + if (extractedStatusAndCode && extractedStatusAndCode.status === 403) { await getCredentialsForCloudProject(authenticatedUser, cloudProjectId); const response = await apiCall(); return response; diff --git a/newIDE/app/src/Utils/GDevelopServices/User.js b/newIDE/app/src/Utils/GDevelopServices/User.js index 24d648354aa3..fd0c8c7ddd6a 100644 --- a/newIDE/app/src/Utils/GDevelopServices/User.js +++ b/newIDE/app/src/Utils/GDevelopServices/User.js @@ -116,6 +116,7 @@ export type UserPublicProfile = {| username: ?string, description: ?string, donateLink: ?string, + discordUsername: ?string, communityLinks: CommunityLinks, iconUrl: string, |}; @@ -337,6 +338,21 @@ export const getUsernameAvailability = async ( return response.data; }; +export const syncDiscordUsername = async ( + getAuthorizationHeader: () => Promise, + userId: string +): Promise => { + const authorizationHeader = await getAuthorizationHeader(); + await axios.post( + `${GDevelopUserApi.baseUrl}/user/${userId}/action/update-discord-role`, + {}, + { + headers: { Authorization: authorizationHeader }, + params: { userId }, + } + ); +}; + const simpleUrlRegex = /^https:\/\/[^ ]+$/; const profileLinkFormattingErrorMessage = ( Please enter a valid URL, starting with https:// @@ -358,6 +374,10 @@ export const donateLinkConfig = { maxLength: 150, }; +export const discordUsernameConfig = { + maxLength: 32, +}; + export const communityLinksConfig = { personalWebsiteLink: { icon: , diff --git a/newIDE/app/src/fixtures/GDevelopServicesTestData/FakeBehaviorsRegistry.js b/newIDE/app/src/fixtures/GDevelopServicesTestData/FakeBehaviorsRegistry.js index 074e36c97d12..fe32c518990f 100644 --- a/newIDE/app/src/fixtures/GDevelopServicesTestData/FakeBehaviorsRegistry.js +++ b/newIDE/app/src/fixtures/GDevelopServicesTestData/FakeBehaviorsRegistry.js @@ -69,6 +69,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: '4ian', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -102,6 +103,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: '4ian', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -110,6 +112,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -161,6 +164,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'Bouh', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -191,6 +195,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'Bouh', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -225,6 +230,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: '4ian', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -233,6 +239,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -263,6 +270,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -294,6 +302,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -330,6 +339,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -338,6 +348,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -373,6 +384,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -381,6 +393,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -415,6 +428,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: '4ian', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -423,6 +437,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -479,6 +494,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -487,6 +503,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -521,6 +538,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -529,6 +547,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -563,6 +582,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -571,6 +591,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -605,6 +626,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -613,6 +635,7 @@ export const fakeBehaviorsRegistry: BehaviorsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, diff --git a/newIDE/app/src/fixtures/GDevelopServicesTestData/FakeExtensionsRegistry.js b/newIDE/app/src/fixtures/GDevelopServicesTestData/FakeExtensionsRegistry.js index 3247e9f467ab..c767fe8d3b18 100644 --- a/newIDE/app/src/fixtures/GDevelopServicesTestData/FakeExtensionsRegistry.js +++ b/newIDE/app/src/fixtures/GDevelopServicesTestData/FakeExtensionsRegistry.js @@ -38,6 +38,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake author', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -46,6 +47,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -75,6 +77,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake author', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -83,6 +86,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -113,6 +117,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'arthuro555', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -142,6 +147,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #EQ2', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -172,6 +178,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: '4ian', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -202,6 +209,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'arthuro555', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -231,6 +239,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Bouh', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -259,6 +268,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'arthuro555', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -288,6 +298,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #oj2', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -317,6 +328,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #pn1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -348,6 +360,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #2w2', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -356,6 +369,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -385,6 +399,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake author', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -393,6 +408,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -422,6 +438,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: '4ian', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -454,6 +471,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -462,6 +480,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #Gx1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -494,6 +513,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -502,6 +522,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #YV2', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -532,6 +553,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -561,6 +583,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -592,6 +615,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #YV2', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -600,6 +624,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Bouh', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -629,6 +654,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #Tw1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -660,6 +686,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Bouh', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -668,6 +695,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'arthuro555', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -701,6 +729,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -709,6 +738,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #pn1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -717,6 +747,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -745,6 +776,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'arthuro555', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -775,6 +807,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -805,6 +838,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -835,6 +869,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #Tw1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -864,6 +899,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -896,6 +932,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'arthuro555', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -904,6 +941,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Bouh', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -933,6 +971,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -962,6 +1001,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'arthuro555', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -991,6 +1031,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #xJ3', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -1023,6 +1064,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #oq2', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -1031,6 +1073,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -1060,6 +1103,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -1093,6 +1137,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'D8H', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -1101,6 +1146,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake user #Cg1', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, @@ -1130,6 +1176,7 @@ export const fakeExtensionsRegistry: ExtensionsRegistry & { username: 'Fake author', description: '', donateLink: null, + discordUsername: null, communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }, diff --git a/newIDE/app/src/fixtures/GDevelopServicesTestData/index.js b/newIDE/app/src/fixtures/GDevelopServicesTestData/index.js index 68a03759a1ee..f5874c072607 100644 --- a/newIDE/app/src/fixtures/GDevelopServicesTestData/index.js +++ b/newIDE/app/src/fixtures/GDevelopServicesTestData/index.js @@ -150,6 +150,7 @@ export const indieUserProfile: Profile = { isStudent: false, isEmailAutogenerated: false, donateLink: 'https://www.paypal.me/indie-user', + discordUsername: 'indie-user#1234', communityLinks: { personalWebsiteLink: 'https://indie-user.com', personalWebsite2Link: 'https://indie-user2.com', diff --git a/newIDE/app/src/stories/componentStories/AssetStore/AssetStore/AssetDetails.stories.js b/newIDE/app/src/stories/componentStories/AssetStore/AssetStore/AssetDetails.stories.js index 6bebfa60e62a..ad2cb1ca9b96 100644 --- a/newIDE/app/src/stories/componentStories/AssetStore/AssetStore/AssetDetails.stories.js +++ b/newIDE/app/src/stories/componentStories/AssetStore/AssetStore/AssetDetails.stories.js @@ -79,6 +79,7 @@ PrivateAsset.parameters = { username: 'Clem', description: "I'm Clement\n\ntada", donateLink: 'https://ko-fi/clem', + discordUsername: 'indie-user#1234', personalWebsiteLink: 'https://indie-user.com', personalWebsite2Link: 'https://indie-user2.com', twitterUsername: 'indie-user', @@ -131,6 +132,7 @@ PrivateAsset.parameters = { username: 'Clem', description: "I'm Clement\n\ntada", donateLink: 'https://ko-fi/clem', + discordUsername: 'indie-user#1234', personalWebsiteLink: 'https://indie-user.com', personalWebsite2Link: 'https://indie-user2.com', twitterUsername: 'indie-user', @@ -147,6 +149,7 @@ PrivateAsset.parameters = { username: 'Clem2', description: "I'm Clement 2\n\ntada", donateLink: 'https://ko-fi/clem2', + discordUsername: 'indie-user#12345', personalWebsiteLink: 'https://indie-user.com', personalWebsite2Link: 'https://indie-user2.com', twitterUsername: 'indie-user', diff --git a/newIDE/app/src/stories/componentStories/Profile/EditProfileDialog.stories.js b/newIDE/app/src/stories/componentStories/Profile/EditProfileDialog.stories.js index 6cc28d602a38..62f792d314e4 100644 --- a/newIDE/app/src/stories/componentStories/Profile/EditProfileDialog.stories.js +++ b/newIDE/app/src/stories/componentStories/Profile/EditProfileDialog.stories.js @@ -33,6 +33,7 @@ const defaultProps = { updatedAt: 12345, appLanguage: 'en', donateLink: 'https://www.gdevelop-app.com', + discordUsername: 'indie-user#1234', communityLinks: { personalWebsiteLink: 'https://indie-user.com', personalWebsite2Link: 'https://indie-user2.com', diff --git a/newIDE/app/src/stories/componentStories/Profile/Subscription/SubscriptionPendingDialog.stories.js b/newIDE/app/src/stories/componentStories/Profile/Subscription/SubscriptionPendingDialog.stories.js index a97e55923e3b..de4cda8f1e1c 100644 --- a/newIDE/app/src/stories/componentStories/Profile/Subscription/SubscriptionPendingDialog.stories.js +++ b/newIDE/app/src/stories/componentStories/Profile/Subscription/SubscriptionPendingDialog.stories.js @@ -5,6 +5,7 @@ import { action } from '@storybook/addon-actions'; import muiDecorator from '../../../ThemeDecorator'; import paperDecorator from '../../../PaperDecorator'; import { + indieUserProfile, fakeSilverAuthenticatedUser, fakeAuthenticatedUserWithNoSubscription, } from '../../../../fixtures/GDevelopServicesTestData'; @@ -22,9 +23,25 @@ export const DefaultNoSubscription = () => ( onClose={action('on close')} /> ); -export const AuthenticatedUserWithSubscription = () => ( + +export const AuthenticatedUserWithSubscriptionAndDiscordUsernameAlreadyFilled = () => ( ); + +const fakeProfileWithoutDiscordUsername = { + ...indieUserProfile, + discordUsername: '', +}; + +export const AuthenticatedUserWithSubscriptionButWithoutDiscordUsername = () => ( + +); diff --git a/newIDE/app/src/stories/componentStories/ProfileDetails.stories.js b/newIDE/app/src/stories/componentStories/ProfileDetails.stories.js index ed9ffe7290ee..e95adedd1233 100644 --- a/newIDE/app/src/stories/componentStories/ProfileDetails.stories.js +++ b/newIDE/app/src/stories/componentStories/ProfileDetails.stories.js @@ -6,7 +6,10 @@ import muiDecorator from '../ThemeDecorator'; import paperDecorator from '../PaperDecorator'; import ProfileDetails from '../../Profile/ProfileDetails'; -import { indieUserProfile } from '../../fixtures/GDevelopServicesTestData'; +import { + indieUserProfile, + subscriptionForBusinessUser, +} from '../../fixtures/GDevelopServicesTestData'; import { type Profile } from '../../Utils/GDevelopServices/Authentication'; import { type PrivateAssetPackListingData } from '../../Utils/GDevelopServices/Shop'; @@ -51,17 +54,43 @@ const getAssetPacksListingData = ( }, ]; -export const MyProfile = () => ( +export const MyCompleteProfileWithSubscription = () => ( + +); + +export const MyCompleteProfileWithoutSubscription = () => ( ); +export const MyProfileWithoutDiscordUsernameNorSubscription = () => ( + +); + +export const MyProfileWithoutDiscordUsernameButWithSubscription = () => ( + +); + export const OtherUserProfile = () => ( ); -export const IncompleteUserProfile = () => ( +export const OtherUserIncompleteProfile = () => ( ); diff --git a/newIDE/app/src/stories/componentStories/PublicProfileDialog.stories.js b/newIDE/app/src/stories/componentStories/PublicProfileDialog.stories.js index 1ec5407af6df..5f96c644830d 100644 --- a/newIDE/app/src/stories/componentStories/PublicProfileDialog.stories.js +++ b/newIDE/app/src/stories/componentStories/PublicProfileDialog.stories.js @@ -17,6 +17,7 @@ const indieUserWithoutUsernameNorDescriptionProfile: Profile = { username: null, description: null, donateLink: null, + discordUsername: null, communityLinks: {}, }; diff --git a/newIDE/app/src/stories/componentStories/UserChip/UserPublicProfileChip.stories.js b/newIDE/app/src/stories/componentStories/UserChip/UserPublicProfileChip.stories.js index dc69a20adc7c..175f15e52ae0 100644 --- a/newIDE/app/src/stories/componentStories/UserChip/UserPublicProfileChip.stories.js +++ b/newIDE/app/src/stories/componentStories/UserChip/UserPublicProfileChip.stories.js @@ -19,6 +19,7 @@ export const UserPublicProfileChip = () => ( username: 'username', description: 'something', donateLink: 'https://myurl.com', + discordUsername: 'username#1234', communityLinks: {}, iconUrl: 'https://resources.gdevelop-app.com/avatars/4ian.png', }}