diff --git a/newIDE/app/src/EventsFunctionsExtensionsLoader/EventsFunctionsExtensionsProvider.js b/newIDE/app/src/EventsFunctionsExtensionsLoader/EventsFunctionsExtensionsProvider.js index 08387416fe..40b33eee78 100644 --- a/newIDE/app/src/EventsFunctionsExtensionsLoader/EventsFunctionsExtensionsProvider.js +++ b/newIDE/app/src/EventsFunctionsExtensionsLoader/EventsFunctionsExtensionsProvider.js @@ -116,12 +116,13 @@ export default class EventsFunctionsExtensionsProvider extends React.Component< this.setState({ eventsFunctionsExtensionsError, }); - showErrorBox( - i18n._( + showErrorBox({ + message: i18n._( t`An error has occured during functions generation. If GDevelop is installed, verify that nothing is preventing GDevelop from writing on disk. If you're running GDevelop online, verify your internet connection and refresh functions from the Project Manager.` ), - eventsFunctionsExtensionsError - ); + rawError: eventsFunctionsExtensionsError, + errorId: 'events-functions-extensions-load-error', + }); }) .then(() => { this._lastLoadPromise = null; diff --git a/newIDE/app/src/Export/BrowserExporters/BrowserS3PreviewLauncher/BrowserPreviewLinkDialog.js b/newIDE/app/src/Export/BrowserExporters/BrowserS3PreviewLauncher/BrowserPreviewLinkDialog.js index a49c3578a8..695ac33032 100644 --- a/newIDE/app/src/Export/BrowserExporters/BrowserS3PreviewLauncher/BrowserPreviewLinkDialog.js +++ b/newIDE/app/src/Export/BrowserExporters/BrowserS3PreviewLauncher/BrowserPreviewLinkDialog.js @@ -20,12 +20,13 @@ export default class BrowserPreviewLinkDialog extends Component { _makeOnOpen = (i18n: I18nType) => () => { const windowObjectReference = window.open(this.props.url, '_blank'); if (!windowObjectReference) { - showErrorBox( - i18n._( + showErrorBox({ + message: i18n._( t`Unable to open the preview! Be sure that popup are allowed for this website.` ), - undefined - ); + rawError: undefined, + errorId: 'preview-popup-disallowed', + }); } this.props.onClose(); }; diff --git a/newIDE/app/src/Export/ExportLauncher.js b/newIDE/app/src/Export/ExportLauncher.js index f61f59ae90..af8adb0b05 100644 --- a/newIDE/app/src/Export/ExportLauncher.js +++ b/newIDE/app/src/Export/ExportLauncher.js @@ -97,14 +97,16 @@ export default class ExportLauncher extends Component { this.setState({ errored: true, }); - showErrorBox( - message + + showErrorBox({ + message: + message + (err.message ? `\n\nDetails of the error: ${err.message}` : ''), - { + rawError: { exportStep: this.state.exportStep, rawError: err, - } - ); + }, + errorId: 'export-error', + }); } throw err; diff --git a/newIDE/app/src/ExtensionsSearch/ExtensionsSearchDialog.js b/newIDE/app/src/ExtensionsSearch/ExtensionsSearchDialog.js index 62df69b9f9..60965e8543 100644 --- a/newIDE/app/src/ExtensionsSearch/ExtensionsSearchDialog.js +++ b/newIDE/app/src/ExtensionsSearch/ExtensionsSearchDialog.js @@ -12,7 +12,7 @@ import EventsFunctionsExtensionsContext, { type EventsFunctionsExtensionsState, } from '../EventsFunctionsExtensionsLoader/EventsFunctionsExtensionsContext'; import HelpButton from '../UI/HelpButton'; -import { showWarningBox } from '../UI/Messages/MessageBox'; +import { showErrorBox } from '../UI/Messages/MessageBox'; import Window from '../Utils/Window'; type Props = {| @@ -54,13 +54,14 @@ const importExtension = ( ); }); }) - .catch(error => { - showWarningBox( - i18n._( - t`An error happened while loading this extension. Please check that it is a proper extension file and compatible with this version of GDevelop`, - error - ) - ); + .catch(rawError => { + showErrorBox({ + message: i18n._( + t`An error happened while loading this extension. Please check that it is a proper extension file and compatible with this version of GDevelop` + ), + rawError, + errorId: 'extension-loading-error', + }); }); }; diff --git a/newIDE/app/src/ExtensionsSearch/index.js b/newIDE/app/src/ExtensionsSearch/index.js index 783ea622d2..9d0c46fbfe 100644 --- a/newIDE/app/src/ExtensionsSearch/index.js +++ b/newIDE/app/src/ExtensionsSearch/index.js @@ -170,13 +170,14 @@ export default class ExtensionsSearch extends Component { ); }); }, - err => { - showErrorBox( - i18n._( + rawError => { + showErrorBox({ + message: i18n._( t`Unable to download and install the extension. Verify that your internet connection is working or try again later.` ), - err - ); + rawError, + errorId: 'download-extension-error', + }); } ) .then(() => { diff --git a/newIDE/app/src/MainFrame/index.js b/newIDE/app/src/MainFrame/index.js index 4cc8b3b3c2..e8d3db462d 100644 --- a/newIDE/app/src/MainFrame/index.js +++ b/newIDE/app/src/MainFrame/index.js @@ -650,13 +650,14 @@ const MainFrame = (props: Props) => { const errorMessage = getOpenErrorMessage ? getOpenErrorMessage(error) : t`Check that the path/URL is correct, that you selected a file that is a game file created with GDevelop and that is was not removed.`; - showErrorBox( - [ + showErrorBox({ + message: [ i18n._(t`Unable to open the project.`), i18n._(errorMessage), ].join('\n'), - error - ); + errorId: 'project-open-error', + rawError: error, + }); setIsLoadingProject(false); return Promise.reject(error); }); @@ -1447,13 +1448,14 @@ const MainFrame = (props: Props) => { const errorMessage = storageProviderOperations.getOpenErrorMessage ? storageProviderOperations.getOpenErrorMessage(error) : t`Verify that you have the authorizations for reading the file you're trying to access.`; - showErrorBox( - [ + showErrorBox({ + message: [ i18n._(t`Unable to open the project.`), i18n._(errorMessage), ].join('\n'), - error - ); + errorId: 'project-open-with-picker-error', + rawError: error, + }); }); }); }, @@ -1572,13 +1574,14 @@ const MainFrame = (props: Props) => { } } }, - err => { - showErrorBox( - i18n._( + rawError => { + showErrorBox({ + message: i18n._( t`Unable to save as the project! Please try again by choosing another location.` ), - err - ); + rawError, + errorId: 'project-save-as-error', + }); } ) .catch(() => {}) @@ -1657,13 +1660,14 @@ const MainFrame = (props: Props) => { _showSnackMessage(i18n._(t`Project properly saved`)); } }, - err => { - showErrorBox( - i18n._( - t`Unable to save the project! Please try again by choosing another location.` + rawError => { + showErrorBox({ + message: i18n._( + t`Unable to save as the project! Please try again by choosing another location.` ), - err - ); + rawError, + errorId: 'project-save-error', + }); } ) .catch(() => {}) diff --git a/newIDE/app/src/PlatformSpecificAssetsEditor/PlatformSpecificAssetsDialog.js b/newIDE/app/src/PlatformSpecificAssetsEditor/PlatformSpecificAssetsDialog.js index ccf53e00e2..5030696f5c 100644 --- a/newIDE/app/src/PlatformSpecificAssetsEditor/PlatformSpecificAssetsDialog.js +++ b/newIDE/app/src/PlatformSpecificAssetsEditor/PlatformSpecificAssetsDialog.js @@ -142,7 +142,12 @@ export default class PlatformSpecificAssetsDialog extends React.Component< ), ]).then(results => { if (results.indexOf(false) !== -1) { - showErrorBox('Some icons could not be generated!'); + showErrorBox({ + message: 'Some icons could not be generated!', + rawError: undefined, + errorId: 'icon-generation-error', + doNotReport: true, + }); return; } diff --git a/newIDE/app/src/Profile/SubscriptionDialog.js b/newIDE/app/src/Profile/SubscriptionDialog.js index f16f1f6974..7fb0be6747 100644 --- a/newIDE/app/src/Profile/SubscriptionDialog.js +++ b/newIDE/app/src/Profile/SubscriptionDialog.js @@ -137,14 +137,15 @@ export default class SubscriptionDialog extends React.Component { } }; - handleUpdatedSubscriptionFailure = (i18n: I18nType, err: Error) => { + handleUpdatedSubscriptionFailure = (i18n: I18nType, rawError: Error) => { this.setState({ isLoading: false }); - showErrorBox( - i18n._( + showErrorBox({ + message: i18n._( t`Your subscription could not be updated. Please try again later!` ), - err - ); + rawError, + errorId: 'subscription-update-error', + }); }; _renderPrice(plan: PlanDetails): React.Node { diff --git a/newIDE/app/src/ProjectCreation/LocalExamples.js b/newIDE/app/src/ProjectCreation/LocalExamples.js index 8aee21fc6c..cb530ed915 100644 --- a/newIDE/app/src/ProjectCreation/LocalExamples.js +++ b/newIDE/app/src/ProjectCreation/LocalExamples.js @@ -11,7 +11,7 @@ import Text from '../UI/Text'; import { findExamples } from './LocalExamplesFinder'; import optionalRequire from '../Utils/OptionalRequire.js'; import ExamplesList from './ExamplesList'; -import { showWarningBox } from '../UI/Messages/MessageBox'; +import { showErrorBox } from '../UI/Messages/MessageBox'; import { type StorageProvider, type FileMetadata } from '../ProjectsStorage'; import LocalFileStorageProvider from '../ProjectsStorage/LocalFileStorageProvider'; const path = optionalRequire('path'); @@ -40,14 +40,15 @@ type State = {| export const showGameFileCreationError = ( i18n: I18nType, outputPath: string, - error: Error + rawError: Error ) => { - showWarningBox( - i18n._( + showErrorBox({ + message: i18n._( t`Unable to create the game in the specified folder. Check that you have permissions to write in this folder: ${outputPath} or choose another folder.` ), - error - ); + rawError, + errorId: 'local-example-creation-error', + }); }; export default class LocalExamples extends Component { diff --git a/newIDE/app/src/ProjectManager/ProjectErrorsChecker.js b/newIDE/app/src/ProjectManager/ProjectErrorsChecker.js index d7f37d2533..3e5603aed6 100644 --- a/newIDE/app/src/ProjectManager/ProjectErrorsChecker.js +++ b/newIDE/app/src/ProjectManager/ProjectErrorsChecker.js @@ -90,17 +90,21 @@ export const displayProjectErrorsBox = ( ): boolean => { if (!Object.keys(errors).length) return true; - showErrorBox( - t( - 'Your game has some invalid elements, please fix these before continuing:' - ) + + showErrorBox({ + message: + t( + 'Your game has some invalid elements, please fix these before continuing:' + ) + '\n\n' + values(errors) .map(errors => errors.map((error: ProjectError) => `- ${error.message}`).join('\n') ) - .join('\n') - ); + .join('\n'), + rawError: undefined, + errorId: 'project-invalid-settings-error', + doNotReport: true, + }); return false; }; diff --git a/newIDE/app/src/ProjectsStorage/DownloadFileStorageProvider/DownloadSaveAsDialog.js b/newIDE/app/src/ProjectsStorage/DownloadFileStorageProvider/DownloadSaveAsDialog.js index 206851438b..50de26a88e 100644 --- a/newIDE/app/src/ProjectsStorage/DownloadFileStorageProvider/DownloadSaveAsDialog.js +++ b/newIDE/app/src/ProjectsStorage/DownloadFileStorageProvider/DownloadSaveAsDialog.js @@ -20,8 +20,12 @@ export default class DownloadSaveAsDialog extends React.Component { let content = ''; try { content = JSON.stringify(serializeToJSObject(this.props.project)); - } catch (err) { - showErrorBox('Unable to save your project', err); + } catch (rawError) { + showErrorBox({ + message: 'Unable to save your project', + rawError, + errorId: 'download-as-json-error', + }); return; } var uri = encodeURI('data:application/json;charset=utf-8,' + content); diff --git a/newIDE/app/src/ProjectsStorage/ProjectContentChecker.js b/newIDE/app/src/ProjectsStorage/ProjectContentChecker.js index ff6402c304..9ca9c2db54 100644 --- a/newIDE/app/src/ProjectsStorage/ProjectContentChecker.js +++ b/newIDE/app/src/ProjectsStorage/ProjectContentChecker.js @@ -8,26 +8,32 @@ export default function verifyProjectContent( content: Object ): boolean { if (!content.gdVersion && content.eventsFunctions) { - showErrorBox( - [ + showErrorBox({ + message: [ i18n._(t`Unable to open this file.`), i18n._( t`This file is an extension file for GDevelop 5. You should instead import it, using the window to add a new extension to your project.` ), - ].join('\n') - ); + ].join('\n'), + rawError: undefined, + errorId: 'extension-opened-as-project-error', + doNotReport: true, + }); return false; } if (!content.gdVersion && !content.eventsFunctions) { - showErrorBox( - [ + showErrorBox({ + message: [ i18n._(t`Unable to open this file.`), i18n._( t`This file is not recognized as a GDevelop 5 project. Be sure to open a file that was saved using GDevelop.` ), - ].join('\n') - ); + ].join('\n'), + rawError: undefined, + errorId: 'malformed-project-error', + doNotReport: true, + }); return false; } return true; diff --git a/newIDE/app/src/UI/ErrorBoundary.js b/newIDE/app/src/UI/ErrorBoundary.js index cae9f48975..f656e548b2 100644 --- a/newIDE/app/src/UI/ErrorBoundary.js +++ b/newIDE/app/src/UI/ErrorBoundary.js @@ -21,10 +21,15 @@ import { const errorHandler = (error: Error, componentStack: string) => { console.error('Error catched by Boundary:', error, componentStack); - sendErrorMessage('Error catched by error boundary', 'error-boundary', { - error, - componentStack, - }); + sendErrorMessage( + 'Error catched by error boundary', + 'error-boundary', + { + error, + componentStack, + }, + 'error-boundary-error' + ); }; export const ErrorFallbackComponent = ({ diff --git a/newIDE/app/src/UI/Messages/MessageBox.js b/newIDE/app/src/UI/Messages/MessageBox.js index f7f386fa3d..8f3c3b72c2 100644 --- a/newIDE/app/src/UI/Messages/MessageBox.js +++ b/newIDE/app/src/UI/Messages/MessageBox.js @@ -7,13 +7,26 @@ export const showMessageBox = (message: string) => { Window.showMessageBox(message, 'info'); }; -export const showErrorBox = (message: string, rawError: any) => { - Window.showMessageBox(message, 'error'); - sendErrorMessage(message, 'error', rawError); - console.error(message, 'raw error:', rawError); +export const showWarningBox = (message: string) => { + Window.showMessageBox(message, 'warning'); }; -export const showWarningBox = (message: string, rawError: any) => { - Window.showMessageBox(message, 'warning'); - console.warn(message, 'raw error:', rawError); +type ErrorArgs = {| + message: string, + rawError: ?Error | Object, + errorId: string, + doNotReport?: boolean, +|}; + +export const showErrorBox = ({ + message, + rawError, + errorId, + doNotReport, +}: ErrorArgs) => { + Window.showMessageBox(message, 'error'); + if (!doNotReport) { + sendErrorMessage(message, 'error', rawError, errorId); + } + console.error(`${errorId}: "${message}". Raw error:`, rawError); }; diff --git a/newIDE/app/src/Utils/Analytics/EventSender.js b/newIDE/app/src/Utils/Analytics/EventSender.js index b4ceb01057..63bacfc35a 100644 --- a/newIDE/app/src/Utils/Analytics/EventSender.js +++ b/newIDE/app/src/Utils/Analytics/EventSender.js @@ -164,16 +164,18 @@ export const sendHelpSearch = (searchText: string) => { }; export const sendErrorMessage = ( - errorMessage: string, - type: string, - rawError: any + message: string, + type: 'error' | 'error-boundary', + rawError: any, + errorId: string ) => { if (isDev || !client) return; client.recordEvent('error_message', { - message: errorMessage, + message, type, rawError, + errorId, }); }; diff --git a/newIDE/app/src/index.js b/newIDE/app/src/index.js index e9085e77ac..fa7011d9d2 100644 --- a/newIDE/app/src/index.js +++ b/newIDE/app/src/index.js @@ -54,7 +54,9 @@ class Bootstrapper extends Component<{}, State> { GD_STARTUP_TIMES.push(['bootstrapperComponentDidMount', performance.now()]); // Load GDevelop.js, ensuring a new version is fetched when the version changes. - loadScript(`./libGD.js?cache-buster=${VersionMetadata.versionWithHash}`).then(() => { + loadScript( + `./libGD.js?cache-buster=${VersionMetadata.versionWithHash}` + ).then(() => { GD_STARTUP_TIMES.push(['libGDLoadedTime', performance.now()]); const initializeGDevelopJs = global.initializeGDevelopJs; if (!initializeGDevelopJs) { @@ -94,7 +96,7 @@ class Bootstrapper extends Component<{}, State> { }, this.handleEditorLoadError); } - handleEditorLoadError = (err) => { + handleEditorLoadError = rawError => { const message = !electron ? 'Please check your internet connectivity, close the tab and reopen it.' : 'Please restart the application or reinstall the latest version if the problem persists.'; @@ -102,8 +104,12 @@ class Bootstrapper extends Component<{}, State> { this.setState({ loadingMessage: `Unable to load GDevelop. ${message}`, }); - showErrorBox(`Unable to load GDevelop. ${message}`, err); - } + showErrorBox({ + message: `Unable to load GDevelop. ${message}`, + rawError, + errorId: 'editor-load-error', + }); + }; render() { const { App, loadingMessage } = this.state;