diff --git a/rtl-spec/components/commands-publish-button.spec.tsx b/rtl-spec/components/commands-publish-button.spec.tsx index 0871a16de8..4f6f561696 100644 --- a/rtl-spec/components/commands-publish-button.spec.tsx +++ b/rtl-spec/components/commands-publish-button.spec.tsx @@ -40,9 +40,21 @@ type GistCreateOpts = { public: boolean; }; +class RequestError extends Error { + public message: string; + public status: number; + + public constructor(message: string, status: number) { + super(message); + + this.message = message; + this.status = status; + } +} + describe('Action button component', () => { const description = 'Electron Fiddle Gist'; - const errorMessage = '💀'; + const error = new RequestError("Don't be blue 💙", 401); let app: App; let mocktokit: OctokitMock; let state: AppState; @@ -228,8 +240,9 @@ describe('Action button component', () => { }); it('handles an error in Gist publishing', async () => { + state.showGenericDialog = vi.fn().mockReturnValue({ confirm: true }); mocktokit.gists.create.mockImplementation(() => { - throw new Error(errorMessage); + throw new RequestError(error.message, error.status); }); state.editorMosaic.isEdited = true; @@ -240,6 +253,11 @@ describe('Action button component', () => { // On failure the editor should still be considered edited expect(state.editorMosaic.isEdited).toBe(true); + expect(state.showGenericDialog).toHaveBeenCalledWith( + expect.objectContaining({ + label: expect.stringContaining('Publishing Fiddle to GitHub failed.'), + }), + ); }); it('can publish private gists', async () => { @@ -292,16 +310,16 @@ describe('Action button component', () => { }); it('notifies the user if updating fails', async () => { + state.showGenericDialog = vi.fn().mockReturnValue({ confirm: true }); mocktokit.gists.update.mockImplementation(() => { - throw new Error(errorMessage); + throw new RequestError(error.message, error.status); }); await instance.performGistAction(); - expect(window.ElectronFiddle.showWarningDialog).toHaveBeenCalledWith( + expect(state.showGenericDialog).toHaveBeenCalledWith( expect.objectContaining({ - detail: expect.stringContaining(errorMessage), - message: expect.stringContaining('Updating Fiddle Gist failed.'), + label: expect.stringContaining('Updating Fiddle Gist failed.'), }), ); }); @@ -325,16 +343,16 @@ describe('Action button component', () => { }); it('notifies the user if deleting fails', async () => { + state.showGenericDialog = vi.fn().mockReturnValue({ confirm: true }); mocktokit.gists.delete.mockImplementation(() => { - throw new Error(errorMessage); + throw new RequestError(error.message, error.status); }); await instance.performGistAction(); - expect(window.ElectronFiddle.showWarningDialog).toHaveBeenCalledWith( + expect(state.showGenericDialog).toHaveBeenCalledWith( expect.objectContaining({ - detail: expect.stringContaining(errorMessage), - message: expect.stringContaining('Deleting Fiddle Gist failed.'), + label: expect.stringContaining('Deleting Fiddle Gist failed.'), }), ); }); diff --git a/src/renderer/components/commands-action-button.tsx b/src/renderer/components/commands-action-button.tsx index efb8aaddc0..128e411080 100644 --- a/src/renderer/components/commands-action-button.tsx +++ b/src/renderer/components/commands-action-button.tsx @@ -16,10 +16,12 @@ import { observer } from 'mobx-react'; import { EditorId, EditorValues, + GenericDialogType, GistActionState, GistActionType, PACKAGE_NAME, } from '../../interfaces'; +import { FIDDLE_GIST_DESCRIPTION_PLACEHOLDER } from '../constants'; import { AppState } from '../state'; import { ensureRequiredFiles } from '../utils/editor-utils'; import { getOctokit } from '../utils/octokit'; @@ -98,7 +100,7 @@ export const GistActionButton = observer( } private getFiddleDescriptionFromUser(): Promise { - const placeholder = 'Electron Fiddle Gist' as const; + const placeholder = FIDDLE_GIST_DESCRIPTION_PLACEHOLDER; return this.props.appState.showInputDialog({ defaultInput: placeholder, label: 'Please provide a brief description for your Fiddle Gist', @@ -156,11 +158,23 @@ export const GistActionButton = observer( } catch (error: any) { console.warn(`Could not publish gist`, { error }); - window.ElectronFiddle.showWarningDialog({ - message: - 'Publishing Fiddle to GitHub failed. Are you connected to the Internet?', - detail: `GitHub encountered the following error: ${error.message}`, - }); + if (error.status === 401 || error.status === 403) { + const { confirm } = await this.props.appState.showGenericDialog({ + label: `Publishing Fiddle to GitHub failed. GitHub encountered the following error: ${error.message}`, + ok: 'Update token', + cancel: 'Close', + type: GenericDialogType.warning, + wantsInput: false, + }); + + if (confirm) { + this.showGitHubTokenSettings(); + } + } else { + await this.props.appState.showErrorDialog( + `Publishing Fiddle to GitHub failed. Are you connected to the Internet? GitHub encountered the following error: ${error.message}`, + ); + } return false; } @@ -230,12 +244,23 @@ export const GistActionButton = observer( } catch (error: any) { console.warn(`Could not update gist`, { error }); - window.ElectronFiddle.showWarningDialog({ - message: - 'Updating Fiddle Gist failed. Are you connected to the Internet and is this your Gist?', - detail: `GitHub encountered the following error: ${error.message}`, - buttons: ['Ok'], - }); + if (error.status === 401 || error.status === 403) { + const { confirm } = await this.props.appState.showGenericDialog({ + label: `Updating Fiddle Gist failed. GitHub encountered the following error: ${error.message}`, + ok: 'Update token', + cancel: 'Close', + type: GenericDialogType.warning, + wantsInput: false, + }); + + if (confirm) { + this.showGitHubTokenSettings(); + } + } else { + await this.props.appState.showErrorDialog( + `Updating Fiddle Gist failed. Are you connected to the Internet? GitHub encountered the following error: ${error.message}`, + ); + } } appState.activeGistAction = GistActionState.none; @@ -262,11 +287,23 @@ export const GistActionButton = observer( } catch (error: any) { console.warn(`Could not delete gist`, { error }); - window.ElectronFiddle.showWarningDialog({ - message: - 'Deleting Fiddle Gist failed. Are you connected to the Internet, is this your Gist, and have you loaded it?', - detail: `GitHub encountered the following error: ${error.message}`, - }); + if (error.status === 401 || error.status === 403) { + const { confirm } = await this.props.appState.showGenericDialog({ + label: `Deleting Fiddle Gist failed. GitHub encountered the following error: ${error.message}`, + ok: 'Update token', + cancel: 'Close', + type: GenericDialogType.warning, + wantsInput: false, + }); + + if (confirm) { + this.showGitHubTokenSettings(); + } + } else { + await this.props.appState.showErrorDialog( + `Deleting Fiddle Gist failed. Are you connected to the Internet? GitHub encountered the following error: ${error.message}`, + ); + } } appState.gistId = undefined; @@ -455,5 +492,10 @@ export const GistActionButton = observer( .map(([id, content]) => [id, { filename: id, content }]), ); }; + + private showGitHubTokenSettings() { + this.props.appState.toggleSettings(); + this.props.appState.toggleAuthDialog(); + } }, );