Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add robust error dialog for when digest cycle is broken #2977

Merged
merged 1 commit into from
Jan 24, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Inject } from '@angular/core';
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { browserLinks, getLinkHTML, issuesEmailTemplate, supportedBrowser } from 'xforge-common/utils';
import { environment } from '../../environments/environment';
Expand All @@ -14,19 +14,90 @@
templateUrl: './error-dialog.component.html',
styleUrls: ['./error-dialog.component.scss']
})
export class ErrorDialogComponent {
export class ErrorDialogComponent implements OnInit {
initComplete = false;
showDetails = false;
browserUnsupported = !supportedBrowser();
outsideAngularErrorDialog?: OutsideAngularErrorDialog;

issueEmailLink = getLinkHTML(environment.issueEmail, issuesEmailTemplate(this.data.eventId));

constructor(
public dialogRef: MatDialogRef<ErrorDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: ErrorAlertData,
readonly i18n: I18nService
) {}
) {
// If the dialog doesn't init in the expected time, open the outsideAngularErrorDialog
setTimeout(() => {
if (!this.initComplete) this.openOutsideAngularErrorDialog();
}, 100);
}

get browserLinks(): { chromeLink: string; firefoxLink: string; safariLink: string } {
return browserLinks();
}

ngOnInit(): void {
this.initComplete = true;
// In the unlikely event that this dialog didn't init in the expected time, but eventually did init, and the
// outsideAngularErrorDialog was wrongly created, close it.
if (this.outsideAngularErrorDialog != null) {
this.outsideAngularErrorDialog.close();

Check warning on line 45 in src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts

View check run for this annotation

Codecov / codecov/patch

src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts#L45

Added line #L45 was not covered by tests
}
}

openOutsideAngularErrorDialog(): void {
this.outsideAngularErrorDialog = new OutsideAngularErrorDialog(

Check warning on line 50 in src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts

View check run for this annotation

Codecov / codecov/patch

src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts#L50

Added line #L50 was not covered by tests
this.data.message,
issuesEmailTemplate(this.data.eventId)
);
}
}

/**
* This is a last-ditch error dialog for when an error prevents Angular from rendering the error dialog, such as when
* there's an error happening in every Angular digest cycle.
* It is not internationalized, and is not intended to look like the normal error dialog.
*/
class OutsideAngularErrorDialog {
private dialogElement: HTMLDialogElement;

constructor(message: string, linkUrl: string) {
// Elements
this.dialogElement = document.createElement('dialog');
const titleElement = document.createElement('h1');
const messageElement = document.createElement('p');
const issueLinkWrapper = document.createElement('p');
const issueLinkElement = document.createElement('a');
const closeElement = document.createElement('button');

Check warning on line 72 in src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts

View check run for this annotation

Codecov / codecov/patch

src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts#L67-L72

Added lines #L67 - L72 were not covered by tests

// Text content
titleElement.textContent = 'An error has occurred';
messageElement.textContent = `Error: ${message}`;
issueLinkElement.textContent = `Report error to ${environment.issueEmail}`;
closeElement.textContent = 'Close';

Check warning on line 78 in src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts

View check run for this annotation

Codecov / codecov/patch

src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts#L75-L78

Added lines #L75 - L78 were not covered by tests

// Report issue link
issueLinkElement.href = linkUrl;
issueLinkElement.target = '_blank';
issueLinkWrapper.appendChild(issueLinkElement);

Check warning on line 83 in src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts

View check run for this annotation

Codecov / codecov/patch

src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts#L81-L83

Added lines #L81 - L83 were not covered by tests

// Close button
closeElement.onclick = () => this.close();

Check warning on line 86 in src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts

View check run for this annotation

Codecov / codecov/patch

src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts#L86

Added line #L86 was not covered by tests

// Assemble element tree
this.dialogElement.appendChild(titleElement);
this.dialogElement.appendChild(messageElement);
this.dialogElement.appendChild(issueLinkWrapper);
this.dialogElement.appendChild(closeElement);

Check warning on line 92 in src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts

View check run for this annotation

Codecov / codecov/patch

src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts#L89-L92

Added lines #L89 - L92 were not covered by tests

// Create and open dialog
document.body.appendChild(this.dialogElement);
this.dialogElement.showModal();

Check warning on line 96 in src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts

View check run for this annotation

Codecov / codecov/patch

src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts#L95-L96

Added lines #L95 - L96 were not covered by tests
}

close(): void {
this.dialogElement.close();
this.dialogElement.remove();

Check warning on line 101 in src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts

View check run for this annotation

Codecov / codecov/patch

src/SIL.XForge.Scripture/ClientApp/src/xforge-common/error-dialog/error-dialog.component.ts#L100-L101

Added lines #L100 - L101 were not covered by tests
}
}
Loading