(core) Adding colors to toast notification

Summary:
Styling toast notification. Adding colors and icons.
In Grist, changed the default style for errors (will be shown in red), and a style for
Linked copied to clipboard (will be shown in Green).
All other colors are not used currently, left for another diff.

Test Plan: manual

Reviewers: georgegevoian

Reviewed By: georgegevoian

Differential Revision: https://phab.getgrist.com/D3053
This commit is contained in:
Jarosław Sadziński
2021-10-01 21:38:58 +02:00
parent 43a62e7254
commit 40ddb57dfc
12 changed files with 203 additions and 22 deletions

View File

@@ -139,7 +139,7 @@ export class TopAppModelImpl extends Disposable implements TopAppModel {
}
if (org.billingAccount && org.billingAccount.product &&
org.billingAccount.product.name === 'suspended') {
this.notifier.createUserError(
this.notifier.createUserMessage(
'This team site is suspended. Documents can be read, but not modified.',
{actions: ['renew', 'personal']}
);

View File

@@ -17,6 +17,7 @@ interface INotifier {
// If you are looking to report errors, please do that via reportError rather
// than these methods so that we have a chance to send the error to our logs.
createUserError(message: string, options?: INotifyOptions): INotification;
createUserMessage(message: string, options?: INotifyOptions): INotification;
createAppError(error: Error): void;
createProgressIndicator(name: string, size: string, expireOnComplete: boolean): IProgress;
@@ -46,6 +47,7 @@ export interface INotifyOptions {
inDropdown?: boolean;
expireSec?: number;
badgeCounter?: boolean;
level: 'message' | 'info' | 'success' | 'warning' | 'error';
memos?: string[]; // A list of relevant notes.
@@ -93,6 +95,7 @@ export class Notification extends Expirable implements INotification {
actions: [],
memos: [],
key: null,
level: 'message'
};
constructor(_opts: INotifyOptions) {
@@ -196,6 +199,7 @@ export class Notifier extends Disposable implements INotifier {
title: msg.title,
canUserClose: true,
inToast: true,
level : 'message'
})) : null);
}
@@ -222,6 +226,21 @@ export class Notifier extends Disposable implements INotifier {
* that we have a chance to send the error to our logs.
*/
public createUserError(message: string, options: Partial<INotifyOptions> = {}): INotification {
return this.createUserMessage(message, {
level: 'error',
...options
});
}
/**
* Creates a basic toast notification. By default, expires in 10 seconds.
* Takes an options objects to configure `expireSec` and `canUserClose`.
* Set `expireSec` to 0 to prevent expiration.
*
* Additional option level, can be used to style the notification to like a success, warning,
* info or error message.
*/
public createUserMessage(message: string, options: Partial<INotifyOptions> = {}): INotification {
const timestamp = Date.now();
if (options.actions && options.actions.includes('ask-for-help')) {
// If user should be able to ask for help, add this error to the notifier dropdown too for a
@@ -232,6 +251,7 @@ export class Notifier extends Disposable implements INotifier {
inToast: false,
expireSec: 300,
canUserClose: true,
level: 'message',
inDropdown: true,
...options,
key: options.key && ("dropdown:" + options.key),
@@ -244,6 +264,7 @@ export class Notifier extends Disposable implements INotifier {
expireSec: 10,
canUserClose: true,
inDropdown: false,
level: 'message',
...options,
});
}
@@ -312,6 +333,7 @@ export class Notifier extends Disposable implements INotifier {
message: "Still working...",
canUserClose: false,
inToast: true,
level: 'message',
}));
}
await this._slowNotificationInactivityTimer.disableUntilFinish(promise);
@@ -366,6 +388,7 @@ export class Notifier extends Disposable implements INotifier {
expireSec: where === 'toast' ? 10 : 0,
inDropdown: where === 'dropdown',
actions: ['report-problem'],
level: 'error',
});
}
}

View File

@@ -41,6 +41,30 @@ export function getAppErrors(): string[] {
return _notifier.getFullAppErrors().map((e) => e.error.message);
}
/**
* Shows normal notification without any styling or icon.
*/
export function reportMessage(msg: string, options?: Partial<INotifyOptions>) {
if (_notifier && !_notifier.isDisposed()) {
_notifier.createUserMessage(msg, {
level : 'message',
...options
});
}
}
/**
* Shows notification with green border and a tick icon.
*/
export function reportSuccess(msg: string, options?: Partial<INotifyOptions>) {
if (_notifier && !_notifier.isDisposed()) {
_notifier.createUserMessage(msg, {
level : 'success',
...options
});
}
}
/**
* Report an error to the user using the global Notifier instance. If the argument is a UserError
* or an error with a status in the 400 range, it indicates a user error. Otherwise, it's an
@@ -75,7 +99,7 @@ export function reportError(err: Error|string): void {
options.title = "Add users as team members first";
options.actions = [];
}
_notifier.createUserError(message, options);
_notifier.createUserMessage(message, options);
} else if (err.name === 'UserError' || (typeof status === 'number' && status >= 400 && status < 500)) {
// This is explicitly a user error, or one in the "Client Error" range, so treat it as user
// error rather than a bug. Using message as the key causes same-message notifications to
@@ -86,9 +110,9 @@ export function reportError(err: Error|string): void {
}
_notifier.createUserError(message, options);
} else if (err.name === 'NeedUpgradeError') {
_notifier.createUserError(err.message, {actions: ['upgrade'], key: 'NEED_UPGRADE'});
_notifier.createUserMessage(err.message, {actions: ['upgrade'], key: 'NEED_UPGRADE'});
} else if (code === 'AUTH_NO_EDIT' || code === 'ACL_DENY') {
_notifier.createUserError(err.message, {key: code, memos: details?.memos});
_notifier.createUserMessage(err.message, {key: code, memos: details?.memos});
} else {
// If we don't recognize it, consider it an application error (bug) that the user should be
// able to report.