mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 01:23:52 +00:00
begin rework of notifications
The notification reducer should decide when to display notifications rather than having notifications scattered throughout the code. Also start using translations so that user-visible strings are not scattered in code.
This commit is contained in:
committed by
David Lechner
parent
ec05ad38aa
commit
c373b3abc5
Vendored
+5
-1
@@ -3,7 +3,11 @@
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"],
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"mrmlnc.vscode-json5"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"dependencies": {
|
||||
"@pybricks/firmware": "^1.0.0",
|
||||
"@pybricks/mpy-cross-v4": "^1.0.0",
|
||||
"@shopify/react-i18n": "^3.0.2",
|
||||
"@testing-library/jest-dom": "^5.8.0",
|
||||
"@testing-library/react": "^10.0.4",
|
||||
"@testing-library/user-event": "^10.3.4",
|
||||
|
||||
@@ -18,9 +18,9 @@ export enum BootloaderConnectionActionType {
|
||||
*/
|
||||
DidConnect = 'bootloader.action.connection.did.connect',
|
||||
/**
|
||||
* The connection was cancelled.
|
||||
* The connection was not successful.
|
||||
*/
|
||||
DidCancel = 'bootloader.action.connection.did.cancel',
|
||||
DidFailToConnect = 'bootloader.action.connection.did.connect.fail',
|
||||
/**
|
||||
* There was a connection error.
|
||||
*/
|
||||
@@ -62,12 +62,31 @@ export function didConnect(
|
||||
return { type: BootloaderConnectionActionType.DidConnect, canWriteWithoutResponse };
|
||||
}
|
||||
|
||||
export type BootloaderConnectionDidCancelAction = Action<
|
||||
BootloaderConnectionActionType.DidCancel
|
||||
>;
|
||||
/**
|
||||
* Possible reasons a device could fail to connect.
|
||||
*/
|
||||
export enum BootloaderConnectionFailureReason {
|
||||
/** The reason is not known */
|
||||
Unknown = 'unknown',
|
||||
/** The connection was canceled */
|
||||
Canceled = 'canceled',
|
||||
/** Web Bluetooth is not available */
|
||||
NoWebBluetooth = 'no-web-bluetooth',
|
||||
/** Connected but failed to find the bootloader GATT service */
|
||||
GattServiceNotFound = 'gatt-service-not-found',
|
||||
}
|
||||
|
||||
export function didCancel(): BootloaderConnectionDidCancelAction {
|
||||
return { type: BootloaderConnectionActionType.DidCancel };
|
||||
export interface BootloaderConnectionDidFailToConnectAction
|
||||
extends Action<BootloaderConnectionActionType.DidFailToConnect> {
|
||||
reason: BootloaderConnectionFailureReason;
|
||||
err?: Error;
|
||||
}
|
||||
|
||||
export function didFailToConnect(
|
||||
reason: BootloaderConnectionFailureReason,
|
||||
err?: Error,
|
||||
): BootloaderConnectionDidFailToConnectAction {
|
||||
return { type: BootloaderConnectionActionType.DidFailToConnect, reason, err };
|
||||
}
|
||||
|
||||
export interface BootloaderConnectionDidErrorAction
|
||||
@@ -124,7 +143,7 @@ export function didDisconnect(): BootloaderConnectionDidDisconnectAction {
|
||||
export type BootloaderConnectionAction =
|
||||
| BootloaderConnectionConnectAction
|
||||
| BootloaderConnectionDidConnectAction
|
||||
| BootloaderConnectionDidCancelAction
|
||||
| BootloaderConnectionDidFailToConnectAction
|
||||
| BootloaderConnectionDidErrorAction
|
||||
| BootloaderConnectionSendAction
|
||||
| BootloaderConnectionDidSendAction
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from './bootloader';
|
||||
import { EditorAction } from './editor';
|
||||
import { NotificationAction } from './notification';
|
||||
import { ServiceWorkerAction } from './service-worker';
|
||||
import { TerminalDataAction } from './terminal';
|
||||
|
||||
/**
|
||||
@@ -21,6 +22,7 @@ export type Action =
|
||||
| BootloaderAction
|
||||
| EditorAction
|
||||
| NotificationAction
|
||||
| ServiceWorkerAction
|
||||
| TerminalDataAction;
|
||||
|
||||
/**
|
||||
|
||||
@@ -59,7 +59,7 @@ export function add(
|
||||
message: string,
|
||||
helpUrl?: string,
|
||||
): NotificationAddAction {
|
||||
return { type: NotificationActionType.Add, id: nextId(), level, message, helpUrl };
|
||||
return { type: NotificationActionType.Add, id: -nextId(), level, message, helpUrl };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { Action } from 'redux';
|
||||
|
||||
export enum ServiceWorkerActionType {
|
||||
Update = 'serviceWorker.update',
|
||||
Success = 'serviceWorker.success',
|
||||
}
|
||||
|
||||
export interface ServiceWorkerAction<
|
||||
T extends ServiceWorkerActionType = ServiceWorkerActionType
|
||||
> extends Action<T> {
|
||||
registration: ServiceWorkerRegistration;
|
||||
}
|
||||
|
||||
export function update(
|
||||
registration: ServiceWorkerRegistration,
|
||||
): ServiceWorkerAction<ServiceWorkerActionType.Update> {
|
||||
return { type: ServiceWorkerActionType.Update, registration };
|
||||
}
|
||||
|
||||
export function success(
|
||||
registration: ServiceWorkerRegistration,
|
||||
): ServiceWorkerAction<ServiceWorkerActionType.Success> {
|
||||
return { type: ServiceWorkerActionType.Success, registration };
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { WithI18nProps, withI18n } from '@shopify/react-i18n';
|
||||
import React from 'react';
|
||||
import Toast from 'react-bootstrap/Toast';
|
||||
import { connect } from 'react-redux';
|
||||
import { Dispatch } from '../actions';
|
||||
import * as notification from '../actions/notification';
|
||||
import { remove } from '../actions/notification';
|
||||
import en from './notification.en.json';
|
||||
|
||||
interface DispatchProps {
|
||||
onClose: () => void;
|
||||
@@ -14,11 +16,12 @@ interface DispatchProps {
|
||||
interface OwnProps {
|
||||
id: number;
|
||||
style: string;
|
||||
message: string;
|
||||
message?: string;
|
||||
messageId?: string;
|
||||
helpUrl?: string;
|
||||
}
|
||||
|
||||
type NotificationProps = DispatchProps & OwnProps;
|
||||
type NotificationProps = DispatchProps & OwnProps & WithI18nProps;
|
||||
|
||||
function mapTitle(style: string): string {
|
||||
switch (style) {
|
||||
@@ -47,7 +50,11 @@ class Notification extends React.Component<NotificationProps> {
|
||||
</strong>
|
||||
</Toast.Header>
|
||||
<Toast.Body>
|
||||
<p>{this.props.message}</p>
|
||||
<p>
|
||||
{this.props.messageId
|
||||
? this.props.i18n.translate(this.props.messageId)
|
||||
: this.props.message || 'missing message!'}
|
||||
</p>
|
||||
<p>
|
||||
{this.props.helpUrl && (
|
||||
<a
|
||||
@@ -67,8 +74,17 @@ class Notification extends React.Component<NotificationProps> {
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch, ownProps: OwnProps): DispatchProps => ({
|
||||
onClose: (): void => {
|
||||
dispatch(notification.remove(ownProps.id));
|
||||
dispatch(remove(ownProps.id));
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps)(Notification);
|
||||
export default connect(
|
||||
null,
|
||||
mapDispatchToProps,
|
||||
)(
|
||||
withI18n({
|
||||
id: 'notification',
|
||||
fallback: en,
|
||||
translations: () => ({ en }),
|
||||
})(Notification),
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Collapse } from 'react-bootstrap';
|
||||
import { connect } from 'react-redux';
|
||||
import { TransitionGroup } from 'react-transition-group';
|
||||
import { RootState } from '../reducers';
|
||||
import { NotificationList } from '../reducers/notification';
|
||||
import { Level, NotificationList } from '../reducers/notification';
|
||||
import Notification from './Notification';
|
||||
|
||||
interface StateProps {
|
||||
@@ -15,6 +15,17 @@ interface StateProps {
|
||||
|
||||
type NotificationStackProps = StateProps;
|
||||
|
||||
function mapLevelToStyle(level: Level): string {
|
||||
switch (level) {
|
||||
case Level.Error:
|
||||
return 'danger';
|
||||
case Level.Warning:
|
||||
return 'warning';
|
||||
case Level.Info:
|
||||
return 'info';
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationStack extends React.Component<NotificationStackProps> {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
@@ -33,8 +44,9 @@ class NotificationStack extends React.Component<NotificationStackProps> {
|
||||
<Collapse key={n.id} in={true}>
|
||||
<Notification
|
||||
id={n.id}
|
||||
style={n.style}
|
||||
style={mapLevelToStyle(n.level)}
|
||||
message={n.message}
|
||||
messageId={n.messageId}
|
||||
helpUrl={n.helpUrl}
|
||||
/>
|
||||
</Collapse>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"bootloader": {
|
||||
"connection": {
|
||||
"didConnect": {
|
||||
"cannotWriteWithoutResponse": "This web browser does not support Web Bluetooth Write Characteristic Without Response. Flashing firmware will take a long time."
|
||||
},
|
||||
"didFailToConnect": {
|
||||
"gattServiceNotFound": "Connected to hub but failed to get LEGO bootloader service. Try removing the \"LEGO Bootloader\" device in your OS Bluetooth settings, then try again.",
|
||||
"noWebBluetooth": "This web browser does not support Web Bluetooth or it is not enabled.",
|
||||
"unknown": "Unexpected error while trying to connect. Check console log and report the error."
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceWorker": {
|
||||
"success": "Content is cached for offline use.",
|
||||
"update": "New content is available and will be used when all tabs for this page are closed.'"
|
||||
}
|
||||
}
|
||||
+13
-15
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { I18nContext, I18nManager } from '@shopify/react-i18n';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
@@ -10,7 +11,7 @@ import { createEpicMiddleware } from 'redux-observable';
|
||||
import createSagaMiddleware from 'redux-saga';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
import './index.scss';
|
||||
import * as notification from './actions/notification';
|
||||
import { success, update } from './actions/service-worker';
|
||||
import App from './components/App';
|
||||
import NotificationStack from './components/NotificationStack';
|
||||
import rootEpic from './epics';
|
||||
@@ -24,6 +25,11 @@ const epicMiddleware = createEpicMiddleware();
|
||||
// TODO: add runtime option or filter - logger affects firmware flash performance
|
||||
const loggerMiddleware = createLogger({ predicate: () => false });
|
||||
|
||||
const i18n = new I18nManager({
|
||||
locale: 'en',
|
||||
onError: (err): void => console.error(err),
|
||||
});
|
||||
|
||||
const store = createStore(
|
||||
rootReducer,
|
||||
applyMiddleware(
|
||||
@@ -41,8 +47,10 @@ epicMiddleware.run(rootEpic);
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<Provider store={store}>
|
||||
<NotificationStack />
|
||||
<App />
|
||||
<I18nContext.Provider value={i18n}>
|
||||
<NotificationStack />
|
||||
<App />
|
||||
</I18nContext.Provider>
|
||||
</Provider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root'),
|
||||
@@ -52,16 +60,6 @@ ReactDOM.render(
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.register({
|
||||
onUpdate: () => {
|
||||
store.dispatch(
|
||||
notification.add(
|
||||
'info',
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.',
|
||||
),
|
||||
);
|
||||
},
|
||||
onSuccess: () => {
|
||||
store.dispatch(notification.add('info', 'Content is cached for offline use.'));
|
||||
},
|
||||
onUpdate: (r) => store.dispatch(update(r)),
|
||||
onSuccess: (r) => store.dispatch(success(r)),
|
||||
});
|
||||
|
||||
Vendored
+5
@@ -1,5 +1,10 @@
|
||||
/// <reference types="react-scripts" />
|
||||
|
||||
declare module '*.json' {
|
||||
const src: object;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.zip' {
|
||||
const src: string;
|
||||
export default src;
|
||||
|
||||
@@ -44,15 +44,8 @@ const connection: Reducer<BootloaderConnectionState, Action> = (
|
||||
case BootloaderRequestActionType.Disconnect:
|
||||
return BootloaderConnectionState.Disconnecting;
|
||||
case BootloaderConnectionActionType.DidDisconnect:
|
||||
case BootloaderConnectionActionType.DidCancel:
|
||||
case BootloaderConnectionActionType.DidFailToConnect:
|
||||
return BootloaderConnectionState.Disconnected;
|
||||
case BootloaderConnectionActionType.DidError:
|
||||
// Error while connecting means we didn't connect.
|
||||
if (state === BootloaderConnectionState.Connecting) {
|
||||
return BootloaderConnectionState.Disconnected;
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -3,35 +3,110 @@
|
||||
|
||||
import { Reducer } from 'react';
|
||||
import { combineReducers } from 'redux';
|
||||
import { NotificationAction, NotificationActionType } from '../actions/notification';
|
||||
import { Action } from '../actions';
|
||||
import {
|
||||
BootloaderConnectionActionType,
|
||||
BootloaderConnectionFailureReason,
|
||||
} from '../actions/bootloader';
|
||||
import { NotificationActionType } from '../actions/notification';
|
||||
import { ServiceWorkerActionType } from '../actions/service-worker';
|
||||
import { createCountFunc } from '../utils/iter';
|
||||
|
||||
export type NotificationList = Array<{
|
||||
/**
|
||||
* Severity level of notification.
|
||||
*/
|
||||
export enum Level {
|
||||
/**
|
||||
* This is an error (requires user action to resolve)
|
||||
*/
|
||||
Error = 'error',
|
||||
/**
|
||||
* This is a warning (user could take action or ignore)
|
||||
*/
|
||||
Warning = 'warning',
|
||||
/**
|
||||
* This is just FYI (no user action required)
|
||||
*/
|
||||
Info = 'info',
|
||||
}
|
||||
|
||||
export interface Notification {
|
||||
readonly id: number;
|
||||
readonly style: string;
|
||||
readonly message: string;
|
||||
readonly level: Level;
|
||||
readonly message?: string;
|
||||
readonly messageId?: string;
|
||||
readonly helpUrl?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
const levelMap = {
|
||||
error: 'danger',
|
||||
warning: 'warning',
|
||||
info: 'info',
|
||||
};
|
||||
export type NotificationList = Array<Notification>;
|
||||
|
||||
const list: Reducer<NotificationList, NotificationAction> = (state = [], action) => {
|
||||
const nextId = createCountFunc();
|
||||
|
||||
function append(
|
||||
state: NotificationList,
|
||||
level: Level,
|
||||
messageId: string,
|
||||
helpUrl?: string,
|
||||
): NotificationList {
|
||||
return [...state, { id: nextId(), level, messageId, helpUrl }];
|
||||
}
|
||||
|
||||
const list: Reducer<NotificationList, Action> = (state = [], action) => {
|
||||
switch (action.type) {
|
||||
case BootloaderConnectionActionType.DidConnect:
|
||||
if (!action.canWriteWithoutResponse) {
|
||||
return append(
|
||||
state,
|
||||
Level.Warning,
|
||||
'bootloader.connection.didConnect.cannotWriteWithoutResponse',
|
||||
'https://github.com/WebBluetoothCG/web-bluetooth/blob/master/implementation-status.md',
|
||||
);
|
||||
}
|
||||
return state;
|
||||
case BootloaderConnectionActionType.DidFailToConnect:
|
||||
switch (action.reason) {
|
||||
case BootloaderConnectionFailureReason.GattServiceNotFound:
|
||||
return append(
|
||||
state,
|
||||
Level.Error,
|
||||
'bootloader.connection.didFailToConnect.gattServiceNotFound',
|
||||
);
|
||||
case BootloaderConnectionFailureReason.NoWebBluetooth:
|
||||
return append(
|
||||
state,
|
||||
Level.Error,
|
||||
'bootloader.connection.didFailToConnect.noWebBluetooth',
|
||||
'https://github.com/WebBluetoothCG/web-bluetooth/blob/master/implementation-status.md',
|
||||
);
|
||||
case BootloaderConnectionFailureReason.Unknown:
|
||||
return append(
|
||||
state,
|
||||
Level.Error,
|
||||
'bootloader.connection.didFailToConnect.unknown',
|
||||
);
|
||||
}
|
||||
return state;
|
||||
case NotificationActionType.Add:
|
||||
return [
|
||||
...state,
|
||||
{
|
||||
id: action.id,
|
||||
style: levelMap[action.level],
|
||||
level: action.level as Level,
|
||||
message: action.message,
|
||||
helpUrl: action.helpUrl,
|
||||
},
|
||||
];
|
||||
case NotificationActionType.Remove:
|
||||
return state.filter((e) => e.id !== action.id);
|
||||
case ServiceWorkerActionType.Update:
|
||||
return append(
|
||||
state,
|
||||
Level.Info,
|
||||
'serviceWorker.update',
|
||||
'https://bit.ly/CRA-PWA',
|
||||
);
|
||||
case ServiceWorkerActionType.Success:
|
||||
return append(state, Level.Info, 'serviceWorker.success');
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
+7
-15
@@ -22,9 +22,8 @@ import {
|
||||
BootloaderActionType,
|
||||
BootloaderChecksumResponseAction,
|
||||
BootloaderConnectionActionType,
|
||||
BootloaderConnectionDidCancelAction,
|
||||
BootloaderConnectionDidConnectAction,
|
||||
BootloaderConnectionDidErrorAction,
|
||||
BootloaderConnectionDidFailToConnectAction,
|
||||
BootloaderConnectionDidReceiveAction,
|
||||
BootloaderConnectionDidSendAction,
|
||||
BootloaderDidRequestAction,
|
||||
@@ -312,24 +311,17 @@ function* flashFirmware(action: BootloaderFlashFirmwareAction): Generator {
|
||||
}
|
||||
|
||||
yield put(connect());
|
||||
const didConnect = (yield take([
|
||||
const connectResult = (yield take([
|
||||
BootloaderConnectionActionType.DidConnect,
|
||||
BootloaderConnectionActionType.DidCancel,
|
||||
BootloaderConnectionActionType.DidError,
|
||||
BootloaderConnectionActionType.DidFailToConnect,
|
||||
])) as
|
||||
| BootloaderConnectionDidConnectAction
|
||||
| BootloaderConnectionDidCancelAction
|
||||
| BootloaderConnectionDidErrorAction;
|
||||
| BootloaderConnectionDidFailToConnectAction;
|
||||
|
||||
if (didConnect.type === BootloaderConnectionActionType.DidCancel) {
|
||||
if (connectResult.type === BootloaderConnectionActionType.DidFailToConnect) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (didConnect.type === BootloaderConnectionActionType.DidError) {
|
||||
// TODO: proper error handling
|
||||
throw didConnect.err;
|
||||
}
|
||||
|
||||
yield put(infoRequest());
|
||||
const info = (yield wait(BootloaderResponseActionType.Info)) as WaitResponse<
|
||||
BootloaderInfoResponseAction
|
||||
@@ -373,7 +365,7 @@ function* flashFirmware(action: BootloaderFlashFirmwareAction): Generator {
|
||||
}
|
||||
|
||||
// City hub bootloader is buggy. See note in encodeRequest().
|
||||
if (info[0].hubType === HubType.CityHub && !didConnect.canWriteWithoutResponse) {
|
||||
if (info[0].hubType === HubType.CityHub && !connectResult.canWriteWithoutResponse) {
|
||||
yield put(
|
||||
notification.add(
|
||||
'error',
|
||||
@@ -420,7 +412,7 @@ function* flashFirmware(action: BootloaderFlashFirmwareAction): Generator {
|
||||
|
||||
yield put(progress(offset, firmware.length));
|
||||
|
||||
if (didConnect.canWriteWithoutResponse) {
|
||||
if (connectResult.canWriteWithoutResponse) {
|
||||
// request checksum every 8K to prevent buffer overrun on the hub
|
||||
// because of sending too much data at once
|
||||
if (++count % 585 === 0) {
|
||||
|
||||
+18
-40
@@ -5,14 +5,13 @@ import { Action, Dispatch } from '../actions';
|
||||
|
||||
import {
|
||||
BootloaderConnectionActionType,
|
||||
didCancel,
|
||||
BootloaderConnectionFailureReason as Reason,
|
||||
didConnect,
|
||||
didDisconnect,
|
||||
didError,
|
||||
didFailToConnect,
|
||||
didReceive,
|
||||
didSend,
|
||||
} from '../actions/bootloader';
|
||||
import * as notification from '../actions/notification';
|
||||
import { CharacteristicUUID, ServiceUUID } from '../protocols/bootloader';
|
||||
import {
|
||||
PolyfillBluetoothRemoteGATTCharacteristic,
|
||||
@@ -33,13 +32,7 @@ async function connect(action: Action, dispatch: Dispatch): Promise<void> {
|
||||
throw Error('already connected');
|
||||
}
|
||||
if (navigator.bluetooth === undefined) {
|
||||
dispatch(
|
||||
notification.add(
|
||||
'error',
|
||||
'This web browser does not support Web Bluetooth or it is not enabled.',
|
||||
'https://github.com/WebBluetoothCG/web-bluetooth/blob/master/implementation-status.md',
|
||||
),
|
||||
);
|
||||
dispatch(didFailToConnect(Reason.NoWebBluetooth));
|
||||
return;
|
||||
}
|
||||
// TODO: check navigator.bluetooth.getAvailability()
|
||||
@@ -55,7 +48,7 @@ async function connect(action: Action, dispatch: Dispatch): Promise<void> {
|
||||
) {
|
||||
// this error is received if the user clicks the cancel button in
|
||||
// the bluetooth scan dialog
|
||||
dispatch(didCancel());
|
||||
dispatch(didFailToConnect(Reason.Canceled));
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
@@ -81,44 +74,29 @@ async function connect(action: Action, dispatch: Dispatch): Promise<void> {
|
||||
dispatch(didReceive(char.value));
|
||||
});
|
||||
await char.startNotifications();
|
||||
|
||||
// char.writeValueWithoutResponse() was introduced in Chrome 85
|
||||
// Older versions of Chrome for Android will write without response
|
||||
// by default, so don't warn on Android.
|
||||
if (
|
||||
!char.writeValueWithoutResponse &&
|
||||
!/Android/i.test(navigator.userAgent)
|
||||
) {
|
||||
// TODO: this needs to be an error if connected to city hub
|
||||
// however it is not currently possible to get mfg-specific
|
||||
// advertising data, so we don't know what type of hub it is
|
||||
// until after we connect
|
||||
dispatch(
|
||||
notification.add(
|
||||
'warning',
|
||||
'This web browser does not support Web Bluetooth Write Characteristic Without Response. Flashing firmware will take a long time.',
|
||||
'https://github.com/WebBluetoothCG/web-bluetooth/blob/master/implementation-status.md',
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
device.gatt.disconnect();
|
||||
if (
|
||||
err instanceof DOMException &&
|
||||
err.code === DOMException.NOT_FOUND_ERR
|
||||
) {
|
||||
dispatch(
|
||||
notification.add(
|
||||
'error',
|
||||
'Connected to hub but failed to get LEGO bootloader service. Try removing the "LEGO Bootloader" device in your OS Bluetooth settings, then try again.',
|
||||
),
|
||||
);
|
||||
// Possibly/probably caused by Chrome BlueZ back-end bug
|
||||
// https://chromium-review.googlesource.com/c/chromium/src/+/2214098
|
||||
dispatch(didFailToConnect(Reason.GattServiceNotFound));
|
||||
return;
|
||||
}
|
||||
device.gatt.disconnect();
|
||||
throw err;
|
||||
}
|
||||
dispatch(didConnect(char.writeValueWithoutResponse !== undefined));
|
||||
|
||||
// char.writeValueWithoutResponse() was introduced in Chrome 85.
|
||||
// Older versions of Chrome for Android will write without response
|
||||
// by default when using the deprecated writeValue().
|
||||
const canWriteWithoutResponse =
|
||||
char.writeValueWithoutResponse !== undefined ||
|
||||
/Android/i.test(navigator.userAgent);
|
||||
dispatch(didConnect(canWriteWithoutResponse));
|
||||
} catch (err) {
|
||||
dispatch(didError(err));
|
||||
dispatch(didFailToConnect(Reason.Unknown, err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-10
@@ -9,11 +9,7 @@ import { combineServices } from '.';
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
async function open(
|
||||
action: Action,
|
||||
_dispatch: Dispatch,
|
||||
state: RootState,
|
||||
): Promise<void> {
|
||||
function open(action: Action, _dispatch: Dispatch, state: RootState): void {
|
||||
if (action.type !== EditorActionType.Open) {
|
||||
return;
|
||||
}
|
||||
@@ -26,11 +22,7 @@ async function open(
|
||||
state.editor.current.getDocument().setValue(text);
|
||||
}
|
||||
|
||||
async function save(
|
||||
action: Action,
|
||||
_dispatch: Dispatch,
|
||||
state: RootState,
|
||||
): Promise<void> {
|
||||
function save(action: Action, _dispatch: Dispatch, state: RootState): void {
|
||||
if (action.type !== EditorActionType.Save) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,25 @@
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { Action } from '../actions';
|
||||
import { BootloaderConnectionActionType } from '../actions/bootloader';
|
||||
import {
|
||||
BootloaderConnectionActionType,
|
||||
BootloaderConnectionFailureReason,
|
||||
} from '../actions/bootloader';
|
||||
import { combineServices } from '.';
|
||||
|
||||
async function consoleLog(action: Action): Promise<void> {
|
||||
/**
|
||||
* Logs unexpected errors to console.error and expected errors to console.debug.
|
||||
* @param action An action
|
||||
*/
|
||||
function consoleLog(action: Action): void {
|
||||
switch (action.type) {
|
||||
case BootloaderConnectionActionType.DidFailToConnect:
|
||||
if (action.reason === BootloaderConnectionFailureReason.Unknown) {
|
||||
console.error(action.err);
|
||||
} else {
|
||||
console.debug(action.err);
|
||||
}
|
||||
break;
|
||||
case BootloaderConnectionActionType.DidError:
|
||||
console.error(action.err);
|
||||
break;
|
||||
|
||||
+15
-6
@@ -8,7 +8,11 @@ import bootloader from './bootloader';
|
||||
import editor from './editor';
|
||||
import errorLog from './error-log';
|
||||
|
||||
type Service = (action: Action, dispatch: Dispatch, state: RootState) => Promise<void>;
|
||||
type Service = (
|
||||
action: Action,
|
||||
dispatch: Dispatch,
|
||||
state: RootState,
|
||||
) => void | Promise<void>;
|
||||
|
||||
function runService(
|
||||
service: Service,
|
||||
@@ -16,15 +20,20 @@ function runService(
|
||||
dispatch: Dispatch,
|
||||
state: RootState,
|
||||
): void {
|
||||
service(action, dispatch, state).catch((err) =>
|
||||
console.log(`Unhandled exception in service: ${err}`),
|
||||
);
|
||||
// Services are deferred so that the current action completes before
|
||||
// dispatching another action by calling dispatch() in the service.
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await service(action, dispatch, state);
|
||||
} catch (err) {
|
||||
console.log(`Unhandled exception in service: ${err}`);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
export function combineServices(...services: Service[]): Service {
|
||||
return (a, d, s): Promise<void> => {
|
||||
return (a, d, s): void => {
|
||||
services.forEach((x) => runService(x, a, d, s));
|
||||
return Promise.resolve();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,16 @@
|
||||
semver "^5.4.1"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/generator@^7.10.0":
|
||||
version "7.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.0.tgz#a238837896edf35ee5fbfb074548d3256b4bc55d"
|
||||
integrity sha512-ThoWCJHlgukbtCP79nAK4oLqZt5fVo70AHUni/y8Jotyg5rtJiG2FVl+iJjRNKIyl4hppqztLyAoEWcCvqyOFQ==
|
||||
dependencies:
|
||||
"@babel/types" "^7.10.0"
|
||||
jsesc "^2.5.1"
|
||||
lodash "^4.17.13"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/generator@^7.4.0", "@babel/generator@^7.9.0", "@babel/generator@^7.9.6":
|
||||
version "7.9.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43"
|
||||
@@ -296,6 +306,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7"
|
||||
integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==
|
||||
|
||||
"@babel/parser@^7.10.0":
|
||||
version "7.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.0.tgz#8eca3e71a73dd562c5222376b08253436bb4995b"
|
||||
integrity sha512-fnDUl1Uy2gThM4IFVW4ISNHqr3cJrCsRkSCasFgx0XDO9JcttDS5ytyBc4Cu4X1+fjoo3IVvFbRD6TeFlHJlEQ==
|
||||
|
||||
"@babel/plugin-proposal-async-generator-functions@^7.8.3":
|
||||
version "7.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f"
|
||||
@@ -995,6 +1010,15 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/template@^7.0.0":
|
||||
version "7.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.0.tgz#f15d852ce16cd5fb3e219097a75f662710b249b1"
|
||||
integrity sha512-aMLEQn5tcG49LEWrsEwxiRTdaJmvLem3+JMCMSeCy2TILau0IDVyWdm/18ACx7XOCady64FLt6KkHy28tkDQHQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.8.3"
|
||||
"@babel/parser" "^7.10.0"
|
||||
"@babel/types" "^7.10.0"
|
||||
|
||||
"@babel/template@^7.4.0", "@babel/template@^7.8.3", "@babel/template@^7.8.6":
|
||||
version "7.8.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
|
||||
@@ -1004,6 +1028,21 @@
|
||||
"@babel/parser" "^7.8.6"
|
||||
"@babel/types" "^7.8.6"
|
||||
|
||||
"@babel/traverse@^7.0.0":
|
||||
version "7.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.0.tgz#290935529881baf619398d94fd453838bef36740"
|
||||
integrity sha512-NZsFleMaLF1zX3NxbtXI/JCs2RPOdpGru6UBdGsfhdsDsP+kFF+h2QQJnMJglxk0kc69YmMFs4A44OJY0tKo5g==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.8.3"
|
||||
"@babel/generator" "^7.10.0"
|
||||
"@babel/helper-function-name" "^7.9.5"
|
||||
"@babel/helper-split-export-declaration" "^7.8.3"
|
||||
"@babel/parser" "^7.10.0"
|
||||
"@babel/types" "^7.10.0"
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
lodash "^4.17.13"
|
||||
|
||||
"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.0", "@babel/traverse@^7.8.3", "@babel/traverse@^7.9.0", "@babel/traverse@^7.9.6":
|
||||
version "7.9.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.6.tgz#5540d7577697bf619cc57b92aa0f1c231a94f442"
|
||||
@@ -1028,6 +1067,15 @@
|
||||
lodash "^4.17.13"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@babel/types@^7.10.0":
|
||||
version "7.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.0.tgz#d47d92249e42393a5723aad5319035ae411e3e38"
|
||||
integrity sha512-t41W8yWFyQFPOAAvPvjyRhejcLGnJTA3iRpFcDbEKwVJ3UnHQePFzLk8GagTsucJlImyNwrGikGsYURrWbQG8w==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.9.5"
|
||||
lodash "^4.17.13"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@cnakazawa/watch@^1.0.3":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a"
|
||||
@@ -1321,6 +1369,77 @@
|
||||
lodash "^4.17.15"
|
||||
lodash-es "^4.17.15"
|
||||
|
||||
"@shopify/dates@^0.3.6":
|
||||
version "0.3.6"
|
||||
resolved "https://registry.yarnpkg.com/@shopify/dates/-/dates-0.3.6.tgz#5118ae0b16cbe384dcb7ae19c21f17c6396e8302"
|
||||
integrity sha512-dr71oU2x7ThQ7r7jzWWkk61+0nwHFSiIpZXIvlUDCJ/31rWZGWv9S1XDV7/rLJQgNfVIwA2x9eOz0CbYCAuL6Q==
|
||||
dependencies:
|
||||
"@shopify/decorators" "^1.1.11"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@shopify/decorators@^1.1.11":
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/@shopify/decorators/-/decorators-1.1.11.tgz#ae111160185d9f33aebbab4df401b4bea858f302"
|
||||
integrity sha512-qE5y08Oh6ukSQuF7CIPaCs9oLy99nRv1Rmq9rhb7Q02DB9+8z6uNGVmhcftbrIs/7VEMlqk0LrfL6e0ijrxy3g==
|
||||
dependencies:
|
||||
"@shopify/function-enhancers" "^1.0.9"
|
||||
|
||||
"@shopify/function-enhancers@^1.0.9":
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@shopify/function-enhancers/-/function-enhancers-1.0.9.tgz#aa95e49ca2a3be1c76259ebce078b607faf26007"
|
||||
integrity sha512-iGoGIuJIFgWyEnEttjvtSH+5nQS6Lv4LAAooSNaviL10Ah+gLBfFcuSIj19fVLaRl9WxYSIu4sWqwnwn/W+n1g==
|
||||
|
||||
"@shopify/i18n@^0.1.10":
|
||||
version "0.1.10"
|
||||
resolved "https://registry.yarnpkg.com/@shopify/i18n/-/i18n-0.1.10.tgz#8f07f538cba43581ea79dc1104d351b30da6d03d"
|
||||
integrity sha512-RCSMyiDIMrjdiG8BDCtlu2L+0CCM7XDiWx5VbAvSb0GteBRcH8BZHh+22LJt3rcbCNnbiyKlLg/7ikbSyEgzHg==
|
||||
dependencies:
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@shopify/react-effect@^3.2.12":
|
||||
version "3.2.12"
|
||||
resolved "https://registry.yarnpkg.com/@shopify/react-effect/-/react-effect-3.2.12.tgz#81b641cee3f5f451cabc5dd11318f57756786e76"
|
||||
integrity sha512-q4R2x+K3D/M1F5e32qTFLVrN+9BonQACQxu+GnKjQGdZaRqndKgaLUKQQZJbIhCa1/rcj8H/d1JEADeU1XvSrQ==
|
||||
dependencies:
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@shopify/react-hooks@^1.10.0":
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@shopify/react-hooks/-/react-hooks-1.10.0.tgz#39671f889872a345f7e60a635a3d9fcb2a487825"
|
||||
integrity sha512-kgw/lUOhvd5hqOWVGT3Z41Yl9k9knr0s8n+m8YdQTIklww7QzDNV341XKslaRy0qHAf2GUw7JH5g8BQFm1ZQSA==
|
||||
|
||||
"@shopify/react-i18n@^3.0.2":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@shopify/react-i18n/-/react-i18n-3.0.2.tgz#dd4a07185952707bae71b01bbec6ef3ce2087279"
|
||||
integrity sha512-xfOXZgE/DDQQRFxAYGBelQjygcddRPHzUo/ejHP5aU1W2Bs7RqiIQFv/CpLghwO8rKZvKoaHPickEk7ULwbQ3w==
|
||||
dependencies:
|
||||
"@shopify/dates" "^0.3.6"
|
||||
"@shopify/decorators" "^1.1.11"
|
||||
"@shopify/function-enhancers" "^1.0.9"
|
||||
"@shopify/i18n" "^0.1.10"
|
||||
"@shopify/react-effect" "^3.2.12"
|
||||
"@shopify/react-hooks" "^1.10.0"
|
||||
"@shopify/useful-types" "^2.1.5"
|
||||
"@types/hoist-non-react-statics" "^3.0.1"
|
||||
change-case "^3.1.0"
|
||||
glob "^7.1.4"
|
||||
hoist-non-react-statics "^3.0.1"
|
||||
lodash.clonedeep "^4.0.0"
|
||||
lodash.merge "^4.0.0"
|
||||
string-hash "^1.1.3"
|
||||
tslib "^1.9.3"
|
||||
optionalDependencies:
|
||||
"@babel/template" "^7.0.0"
|
||||
"@babel/traverse" "^7.0.0"
|
||||
fs-extra "^8.1.0"
|
||||
|
||||
"@shopify/useful-types@^2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@shopify/useful-types/-/useful-types-2.1.5.tgz#9fd09dcdfe272d8eb2cdd8e94058456eefdf57fa"
|
||||
integrity sha512-V+6gW+fzr+XY//4RyI6XVvwu9cvLVWt9rae1t8Lx2rPjGt/kIM6TdCzk/Lfhd5E3RUR7vVgvWTfTw1FtrxmYUw==
|
||||
dependencies:
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@svgr/babel-plugin-add-jsx-attribute@^4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz#dadcb6218503532d6884b210e7f3c502caaa44b1"
|
||||
@@ -1527,7 +1646,7 @@
|
||||
"@types/minimatch" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/hoist-non-react-statics@^3.3.0":
|
||||
"@types/hoist-non-react-statics@^3.0.1", "@types/hoist-non-react-statics@^3.3.0":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
||||
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
|
||||
@@ -2841,6 +2960,14 @@ callsites@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||
|
||||
camel-case@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
|
||||
integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
upper-case "^1.1.1"
|
||||
|
||||
camel-case@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.1.tgz#1fc41c854f00e2f7d0139dfeba1542d6896fe547"
|
||||
@@ -2932,6 +3059,30 @@ chalk@^3.0.0:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
change-case@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.1.0.tgz#0e611b7edc9952df2e8513b27b42de72647dd17e"
|
||||
integrity sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw==
|
||||
dependencies:
|
||||
camel-case "^3.0.0"
|
||||
constant-case "^2.0.0"
|
||||
dot-case "^2.1.0"
|
||||
header-case "^1.0.0"
|
||||
is-lower-case "^1.1.0"
|
||||
is-upper-case "^1.1.0"
|
||||
lower-case "^1.1.1"
|
||||
lower-case-first "^1.0.0"
|
||||
no-case "^2.3.2"
|
||||
param-case "^2.1.0"
|
||||
pascal-case "^2.0.0"
|
||||
path-case "^2.1.0"
|
||||
sentence-case "^2.1.0"
|
||||
snake-case "^2.1.0"
|
||||
swap-case "^1.1.0"
|
||||
title-case "^2.1.0"
|
||||
upper-case "^1.1.1"
|
||||
upper-case-first "^1.1.0"
|
||||
|
||||
chardet@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||
@@ -3234,6 +3385,14 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
||||
integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
|
||||
|
||||
constant-case@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46"
|
||||
integrity sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY=
|
||||
dependencies:
|
||||
snake-case "^2.1.0"
|
||||
upper-case "^1.1.1"
|
||||
|
||||
constants-browserify@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
|
||||
@@ -3983,6 +4142,13 @@ domutils@^1.5.1, domutils@^1.7.0:
|
||||
dom-serializer "0"
|
||||
domelementtype "1"
|
||||
|
||||
dot-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee"
|
||||
integrity sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
|
||||
dot-case@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.3.tgz#21d3b52efaaba2ea5fda875bb1aa8124521cf4aa"
|
||||
@@ -5310,6 +5476,14 @@ he@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
header-case@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/header-case/-/header-case-1.0.1.tgz#9535973197c144b09613cd65d317ef19963bd02d"
|
||||
integrity sha1-lTWXMZfBRLCWE81l0xfvGZY70C0=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
upper-case "^1.1.3"
|
||||
|
||||
hex-color-regex@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
|
||||
@@ -5324,7 +5498,7 @@ hmac-drbg@^1.0.0:
|
||||
minimalistic-assert "^1.0.0"
|
||||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
hoist-non-react-statics@^3.3.0:
|
||||
hoist-non-react-statics@^3.0.1, hoist-non-react-statics@^3.3.0:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||
@@ -5908,6 +6082,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
|
||||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-lower-case@^1.1.0:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393"
|
||||
integrity sha1-fhR75HaNxGbbO/shzGCzHmrWk5M=
|
||||
dependencies:
|
||||
lower-case "^1.1.0"
|
||||
|
||||
is-number@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
|
||||
@@ -6012,6 +6193,13 @@ is-typedarray@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
||||
|
||||
is-upper-case@^1.1.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f"
|
||||
integrity sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8=
|
||||
dependencies:
|
||||
upper-case "^1.1.0"
|
||||
|
||||
is-utf8@^0.2.0:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
|
||||
@@ -6907,6 +7095,11 @@ lodash._reinterpolate@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
|
||||
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
|
||||
|
||||
lodash.clonedeep@^4.0.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
||||
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
|
||||
|
||||
lodash.get@^4.4.2:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
||||
@@ -6922,6 +7115,11 @@ lodash.memoize@^4.1.2:
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
||||
|
||||
lodash.merge@^4.0.0:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash.sortby@^4.7.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||
@@ -6972,6 +7170,18 @@ loud-rejection@^1.0.0:
|
||||
currently-unhandled "^0.4.1"
|
||||
signal-exit "^3.0.0"
|
||||
|
||||
lower-case-first@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1"
|
||||
integrity sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E=
|
||||
dependencies:
|
||||
lower-case "^1.1.2"
|
||||
|
||||
lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
|
||||
integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
|
||||
|
||||
lower-case@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7"
|
||||
@@ -7394,6 +7604,13 @@ nice-try@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
no-case@^2.2.0, no-case@^2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
|
||||
integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
|
||||
dependencies:
|
||||
lower-case "^1.1.1"
|
||||
|
||||
no-case@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.3.tgz#c21b434c1ffe48b39087e86cfb4d2582e9df18f8"
|
||||
@@ -7910,6 +8127,13 @@ parallel-transform@^1.1.0:
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^2.1.5"
|
||||
|
||||
param-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
|
||||
integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
|
||||
param-case@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.3.tgz#4be41f8399eff621c56eebb829a5e451d9801238"
|
||||
@@ -7977,6 +8201,14 @@ parseurl@~1.3.2, parseurl@~1.3.3:
|
||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
||||
|
||||
pascal-case@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-2.0.1.tgz#2d578d3455f660da65eca18ef95b4e0de912761e"
|
||||
integrity sha1-LVeNNFX2YNpl7KGO+VtODekSdh4=
|
||||
dependencies:
|
||||
camel-case "^3.0.0"
|
||||
upper-case-first "^1.1.0"
|
||||
|
||||
pascal-case@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.1.tgz#5ac1975133ed619281e88920973d2cd1f279de5f"
|
||||
@@ -7995,6 +8227,13 @@ path-browserify@0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
|
||||
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
|
||||
|
||||
path-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5"
|
||||
integrity sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
|
||||
path-dirname@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
|
||||
@@ -9990,6 +10229,14 @@ send@0.17.1:
|
||||
range-parser "~1.2.1"
|
||||
statuses "~1.5.0"
|
||||
|
||||
sentence-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4"
|
||||
integrity sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
upper-case-first "^1.1.2"
|
||||
|
||||
serialize-javascript@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"
|
||||
@@ -10161,6 +10408,13 @@ slice-ansi@^2.1.0:
|
||||
astral-regex "^1.0.0"
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
|
||||
snake-case@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f"
|
||||
integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
|
||||
snapdragon-node@^2.0.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
|
||||
@@ -10427,6 +10681,11 @@ strict-uri-encode@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||
integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
|
||||
|
||||
string-hash@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b"
|
||||
integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=
|
||||
|
||||
string-length@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
|
||||
@@ -10686,6 +10945,14 @@ svgo@^1.0.0, svgo@^1.2.2:
|
||||
unquote "~1.1.1"
|
||||
util.promisify "~1.0.0"
|
||||
|
||||
swap-case@^1.1.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3"
|
||||
integrity sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM=
|
||||
dependencies:
|
||||
lower-case "^1.1.1"
|
||||
upper-case "^1.1.1"
|
||||
|
||||
symbol-observable@^1.0.3, symbol-observable@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||
@@ -10809,6 +11076,14 @@ timsort@^0.3.0:
|
||||
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
|
||||
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
|
||||
|
||||
title-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa"
|
||||
integrity sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
upper-case "^1.0.3"
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
@@ -10910,6 +11185,11 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
|
||||
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
|
||||
|
||||
tslib@^1.9.3:
|
||||
version "1.13.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
|
||||
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
|
||||
|
||||
tsutils@^3.17.1:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
|
||||
@@ -11093,6 +11373,18 @@ upath@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
|
||||
integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
|
||||
|
||||
upper-case-first@^1.1.0, upper-case-first@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115"
|
||||
integrity sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=
|
||||
dependencies:
|
||||
upper-case "^1.1.1"
|
||||
|
||||
upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
|
||||
integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||
|
||||
Reference in New Issue
Block a user