toasterTypes: change toaster to ref

Instead of rendering the toaster separate from the main index, add it
there so we can inherit all of the context providers. This requires
passing the ref object instead of the toaster instance itself, so
sagas have to be updated.
This commit is contained in:
David Lechner
2022-10-19 18:26:37 -05:00
committed by David Lechner
parent be9272ca9c
commit fa27649fe3
27 changed files with 121 additions and 141 deletions
+17 -6
View File
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2021-2022 The Pybricks Authors
import { ToasterInstance } from '@blueprintjs/core';
import type { IToastOptions, ToasterInstance } from '@blueprintjs/core';
import { FirmwareReaderError, FirmwareReaderErrorCode } from '@pybricks/firmware';
import { I18nManager } from '@shopify/react-i18n';
import { mock } from 'jest-mock-extended';
import { AnyAction } from 'redux';
import { AsyncSaga, uuid } from '../../test';
import { appDidCheckForUpdate } from '../app/actions';
@@ -23,7 +23,6 @@ import {
MetadataProblem,
didFailToFinish,
} from '../firmware/actions';
import * as i18nToaster from '../i18nToaster';
import {
BootloaderConnectionFailureReason,
didFailToConnect as bootloaderDidFailToConnect,
@@ -38,15 +37,27 @@ import { I18nId } from './i18n';
import notification from './sagas';
function createTestToasterSaga(): { toaster: ToasterInstance; saga: AsyncSaga } {
const i18n = new I18nManager({ locale: 'en' });
const toaster = i18nToaster.create(i18n);
const toasts = new Map<string, IToastOptions>();
const toaster = mock<ToasterInstance>({
show: (props, key) => {
return key ?? '';
},
dismiss: (key) => {
toasts.delete(key);
},
clear: () => {
toasts.clear();
},
getToasts: () => [...toasts.values()],
});
jest.spyOn(toaster, 'clear');
jest.spyOn(toaster, 'dismiss');
jest.spyOn(toaster, 'getToasts');
jest.spyOn(toaster, 'show');
const saga = new AsyncSaga(notification, { notification: { toaster } });
const saga = new AsyncSaga(notification, { toasterRef: { current: toaster } });
return { toaster, saga };
}
+16 -23
View File
@@ -3,13 +3,7 @@
// Saga for managing notifications (toasts)
import {
ActionProps,
IconName,
Intent,
LinkProps,
ToasterInstance,
} from '@blueprintjs/core';
import { ActionProps, IconName, Intent, LinkProps } from '@blueprintjs/core';
import { Replacements } from '@shopify/react-i18n';
import React from 'react';
import { channel } from 'redux-saga';
@@ -34,22 +28,13 @@ import {
} from '../lwp3-bootloader/actions';
import { didCompile, didFailToCompile } from '../mpy/actions';
import { serviceWorkerDidUpdate } from '../service-worker/actions';
import type { ToasterRef } from '../toasterTypes';
import { defined } from '../utils';
import NotificationAction from './NotificationAction';
import NotificationMessage from './NotificationMessage';
import { add as addNotification } from './actions';
import { I18nId } from './i18n';
type NotificationContext = {
toaster: ToasterInstance;
};
/**
* Partial saga context type for context used in the notification sagas.
*/
export type NotificationSagaContext = {
notification: NotificationContext;
};
/** Severity level of notification. */
enum Level {
/** This is an error (requires user action to resolve). */
@@ -126,7 +111,8 @@ function* showSingleton(
action?: ActionProps & LinkProps,
onDismiss?: (didTimeoutExpire: boolean) => void,
): Generator {
const { toaster } = yield* getContext<NotificationContext>('notification');
const toaster = (yield* getContext<ToasterRef>('toasterRef')).current;
defined(toaster);
// if the message is already showing, close it and wait some time so that
// users can see that something triggered the message again
@@ -158,7 +144,9 @@ function* showSingleton(
/** Shows a special notification for unexpected errors. */
function* showUnexpectedError(messageId: I18nId, error: Error): Generator {
const { toaster } = yield* getContext<NotificationContext>('notification');
const toaster = (yield* getContext<ToasterRef>('toasterRef')).current;
defined(toaster);
const key = `alerts.unexpectedError.${messageId}`;
toaster.show(
@@ -248,7 +236,9 @@ function* showFlashFirmwareError(
}
function* dismissCompilerError(): Generator {
const { toaster } = yield* getContext<NotificationContext>('notification');
const toaster = (yield* getContext<ToasterRef>('toasterRef')).current;
defined(toaster);
toaster.dismiss(I18nId.MpyError);
}
@@ -263,7 +253,8 @@ function* showCompilerError(action: ReturnType<typeof didFailToCompile>): Genera
}
function* handleAddNotification(action: ReturnType<typeof addNotification>): Generator {
const { toaster } = yield* getContext<NotificationContext>('notification');
const toaster = (yield* getContext<ToasterRef>('toasterRef')).current;
defined(toaster);
toaster.show({
intent: mapIntent(action.level as Level),
@@ -303,7 +294,9 @@ function* showNoUpdateInfo(action: ReturnType<typeof appDidCheckForUpdate>): Gen
return;
}
const { toaster } = yield* getContext<NotificationContext>('notification');
const toaster = (yield* getContext<ToasterRef>('toasterRef')).current;
defined(toaster);
toaster.show({
intent: mapIntent(Level.Info),
icon: mapIcon(Level.Info),