From 2ede557a744c669b909afb50ac2ace83a062d10a Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 6 Jan 2020 15:46:49 +1100 Subject: [PATCH] Modal additions incl. QR fixup and Seed View --- _locales/en/messages.json | 22 +- js/modules/signal.js | 8 +- js/views/qr_dialog_view.js | 16 +- js/views/seed_dialog_view.js | 94 ++------- js/views/session_dropdown_view.js | 32 --- package.json | 1 - stylesheets/_session.scss | 32 ++- ts/components/DevicePairingDialog.tsx | 108 +++++----- .../conversation/ConversationHeader.tsx | 15 +- ts/components/session/SessionModal.tsx | 5 +- ts/components/session/SessionQRModal.tsx | 41 ++-- ts/components/session/SessionSeedModal.tsx | 198 ++++++++++++++++++ ts/global.d.ts | 1 + yarn.lock | 67 +----- 14 files changed, 353 insertions(+), 287 deletions(-) create mode 100644 ts/components/session/SessionSeedModal.tsx diff --git a/_locales/en/messages.json b/_locales/en/messages.json index e16cef15d..07d5362f0 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -2102,16 +2102,26 @@ }, "showSeed": { - "message": "Show seed", + "message": "Show Seed", "description": "Button action that the user can click to view their unique seed" }, + "showSeedPasswordRequest": { + "message": "Please enter your password", + "description": "Request for user to enter password to show seed." + }, + "seedSavePrompt": { + "message": + "Please save the seed below in a safe location. They can be used to restore your account if you lose access or migrate to a new device.", + "description": "Prompt on seed modal requesting user to save their seed" + }, "QRCodeTitle": { "message": "Your Public Key QRCode", "description": "Title given to QRCode modal" }, "QRCodeDescription": { - "message": "Your friends can scan this QR code to start a conversation with you.", + "message": + "Your friends can scan this QR code to start a conversation with you.", "description": "Description given to QRCode modal" }, "showQRCode": { @@ -2168,7 +2178,10 @@ "description": "A button action that the user can click to reset the database" }, - + "password": { + "message": "Password", + "description": "Placeholder for password input" + }, "setPassword": { "message": "Set Password", "description": "Button action that the user can click to set a password" @@ -2190,6 +2203,9 @@ "invalidPassword": { "message": "Invalid password" }, + "noGivenPassword": { + "message": "Please enter your password" + }, "passwordsDoNotMatch": { "message": "Passwords do not match" }, diff --git a/js/modules/signal.js b/js/modules/signal.js index e8b9006ce..c34d37c8c 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -61,7 +61,12 @@ const { const { SessionToast } = require('../../ts/components/session/SessionToast'); const { SessionToggle } = require('../../ts/components/session/SessionToggle'); const { SessionModal } = require('../../ts/components/session/SessionModal'); -const { SessionQRModal } = require('../../ts/components/session/SessionQRModal'); +const { + SessionQRModal, +} = require('../../ts/components/session/SessionQRModal'); +const { + SessionSeedModal, +} = require('../../ts/components/session/SessionSeedModal'); const { SessionConfirm, } = require('../../ts/components/session/SessionConfirm'); @@ -275,6 +280,7 @@ exports.setup = (options = {}) => { SessionConfirm, SessionModal, SessionQRModal, + SessionSeedModal, SessionDropdown, MediaGallery, Message, diff --git a/js/views/qr_dialog_view.js b/js/views/qr_dialog_view.js index 440a6694c..6019f87c9 100644 --- a/js/views/qr_dialog_view.js +++ b/js/views/qr_dialog_view.js @@ -11,16 +11,17 @@ initialize(options) { this.value = options.value || ''; this.close = this.close.bind(this); - this.onKeyup = this.onKeyup.bind(this); + this.render(); }, render() { this.dialogView = new Whisper.ReactWrapperView({ - className: 'qr-dialog', + className: 'qr-dialog-wrapper', Component: window.Signal.Components.SessionQRModal, props: { value: this.value, + onClose: this.close, }, }); @@ -31,16 +32,5 @@ close() { this.remove(); }, - onKeyup(event) { - switch (event.key) { - case 'Enter': - case 'Escape': - case 'Esc': - this.close(); - break; - default: - break; - } - }, }); })(); diff --git a/js/views/seed_dialog_view.js b/js/views/seed_dialog_view.js index 88b282008..0ddeea06c 100644 --- a/js/views/seed_dialog_view.js +++ b/js/views/seed_dialog_view.js @@ -1,4 +1,4 @@ -/* global Whisper, i18n, Signal, passwordUtil */ +/* global Whisper */ // eslint-disable-next-line func-names (function() { @@ -8,90 +8,26 @@ Whisper.SeedDialogView = Whisper.View.extend({ className: 'loki-dialog seed-dialog modal', - templateName: 'seed-dialog', - initialize(options = {}) { - this.okText = options.okText || i18n('ok'); - this.cancelText = options.cancelText || i18n('cancel'); - this.confirmText = options.confirmText || i18n('confirm'); - this.copyText = options.copyText || i18n('copySeed'); - this.seed = options.seed || '-'; - + initialize() { + this.close = this.close.bind(this); this.render(); - this.showSeedView(false); - this.initPasswordHash(); - - this.$('#password').bind('keyup', event => this.onKeyup(event)); - }, - events: { - 'click .ok': 'close', - 'click .confirm': 'confirmPassword', - 'click .copy-seed': 'copySeed', - }, - render_attributes() { - return { - passwordViewTitle: i18n('passwordViewTitle'), - seedViewTitle: i18n('seedViewTitle'), - ok: this.okText, - copyText: this.copyText, - confirm: this.confirmText, - cancel: this.cancelText, - }; - }, - async initPasswordHash() { - const hash = await Signal.Data.getPasswordHash(); - this.passwordHash = hash; - this.showSeedView(!hash); - }, - showSeedView(show) { - const seedView = this.$('.seedView'); - const passwordView = this.$('.passwordView'); - if (show) { - this.$('.seed').html(this.seed); - seedView.show(); - passwordView.hide(); - } else { - this.$('.seed').html(''); - passwordView.show(); - this.$('#password').focus(); - seedView.hide(); - } }, - confirmPassword() { - this.$('.error').html(); - const password = this.$('#password').val(); - if ( - this.passwordHash && - !passwordUtil.matchesHash(password, this.passwordHash) - ) { - this.$('.error').html(`Error: ${i18n('invalidPassword')}`); - return; - } - this.showSeedView(true); + render() { + this.dialogView = new Whisper.ReactWrapperView({ + className: 'seed-dialog-wrapper', + Component: window.Signal.Components.SessionSeedModal, + props: { + onClose: this.close, + }, + }); + + this.$el.append(this.dialogView.el); + return this; }, + close() { this.remove(); }, - copySeed() { - window.clipboard.writeText(this.seed); - window.pushToast({ - title: i18n('copiedMnemonic'), - type: 'success', - id: 'copySeedToast', - }); - }, - onKeyup(event) { - switch (event.key) { - case 'Enter': - this.confirmPassword(); - break; - case 'Escape': - case 'Esc': - this.close(); - break; - default: - break; - } - }, }); })(); diff --git a/js/views/session_dropdown_view.js b/js/views/session_dropdown_view.js index a655bad34..e69de29bb 100644 --- a/js/views/session_dropdown_view.js +++ b/js/views/session_dropdown_view.js @@ -1,32 +0,0 @@ -/* global Whisper */ - -// eslint-disable-next-line func-names -(function() { - 'use strict'; - - window.Whisper = window.Whisper || {}; - - Whisper.SessionDropdownView = Whisper.View.extend({ - initialize(options) { - this.props = { - items: options.items, - }; - - this.render(); - }, - - render() { - this.dropdownView = new Whisper.ReactWrapperView({ - className: 'session-dropdown-wrapper', - Component: window.Signal.Components.SessionDropdown, - props: this.props, - }); - - this.$el.append(this.dropdownView.el); - }, - - openDropdown() {}, - - closeDropdown() {}, - }); -})(); diff --git a/package.json b/package.json index 2ee4e6a2a..dc3135079 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,6 @@ "pify": "3.0.0", "protobufjs": "6.8.6", "proxy-agent": "3.0.3", - "qrcode": "^1.4.4", "react": "16.8.3", "react-contextmenu": "2.11.0", "react-dom": "16.8.3", diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index d1efe5f37..b01fbadeb 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -105,6 +105,13 @@ div.spacer-lg { text-align: center; } +@mixin text-highlight($color) { + background-color: rgba($color, 0.8); + padding: $session-margin-xs; + border-radius: 3px; + display: inline-block; +} + .fullwidth { width: 100%; } @@ -115,7 +122,9 @@ $session-icon-size-sm: 15px; $session-icon-size-md: 20px; $session-icon-size-lg: 30px; -$session-modal-size: 220px; +$session-modal-size-sm: 220px; +$session-modal-size-md: 400px; +$session-modal-size-lg: 650px; $session-conversation-header-height: 60px; @@ -470,7 +479,7 @@ label { right: $session-margin-lg; bottom: $session-margin-lg; - z-index: 100; + z-index: 10000; } .session-toast { @@ -552,6 +561,7 @@ label { left: 50%; top: 50%; transform: translate(-50%, -50%); + min-width: 300px; box-sizing: border-box; max-height: 70vh; @@ -622,6 +632,10 @@ label { margin: 0 $session-margin-xs; } } + + &__text-highlight { + @include text-highlight($session-shade-15); + } } .session-toggle { @@ -803,16 +817,22 @@ label { } #qr svg { - width: $session-modal-size; - height: $session-modal-size; + width: $session-modal-size-sm; + height: $session-modal-size-sm; border: 6px solid white; border-radius: 3px; } .qr-dialog { &__description { - max-width: $session-modal-size; + max-width: $session-modal-size-sm; text-align: center; margin: 0 auto; } -} \ No newline at end of file +} + +.seed-dialog { + &__description { + max-width: $session-modal-size-lg; + } +} diff --git a/ts/components/DevicePairingDialog.tsx b/ts/components/DevicePairingDialog.tsx index 6c5f3b44b..216ee0b30 100644 --- a/ts/components/DevicePairingDialog.tsx +++ b/ts/components/DevicePairingDialog.tsx @@ -49,7 +49,7 @@ export class DevicePairingDialog extends React.Component { }; } - componentDidMount() { + public componentDidMount() { this.getSecondaryDevices(); } @@ -98,7 +98,7 @@ export class DevicePairingDialog extends React.Component { value={window.textsecure.storage.user.getNumber()} bgColor="#FFFFFF" fgColor="#000000" - level="L" + level="L" /> @@ -207,58 +207,58 @@ export class DevicePairingDialog extends React.Component { this.showView(); } - private requestReceived(secondaryDevicePubKey: string | EventHandlerNonNull) { - // FIFO: push at the front of the array with unshift() - this.state.pubKeyRequests.unshift(secondaryDevicePubKey); - if (!this.state.currentPubKey) { - this.nextPubKey(); - - this.showView('requestReceived'); - } - } - - private allowDevice() { - this.setState({ - accepted: true, - }); - window.Whisper.trigger( - 'devicePairingRequestAccepted', - this.state.currentPubKey, - (errors: any) => { - this.transmisssionCB(errors); - - return true; - } - ); - this.showView(); - } - - private transmisssionCB(errors: any) { - if (!errors) { - this.setState({ - success: true, - }); - } else { - return; - } - } - - private skipDevice() { - window.Whisper.trigger( - 'devicePairingRequestRejected', - this.state.currentPubKey - ); - this.nextPubKey(); - this.showView(); - } - - private nextPubKey() { - // FIFO: pop at the back of the array using pop() - const pubKeyRequests = this.state.pubKeyRequests; - this.setState({ - currentPubKey: pubKeyRequests.pop(), - }); - } + // private requestReceived(secondaryDevicePubKey: string | EventHandlerNonNull) { + // // FIFO: push at the front of the array with unshift() + // this.state.pubKeyRequests.unshift(secondaryDevicePubKey); + // if (!this.state.currentPubKey) { + // this.nextPubKey(); + + // this.showView('requestReceived'); + // } + // } + + // private allowDevice() { + // this.setState({ + // accepted: true, + // }); + // window.Whisper.trigger( + // 'devicePairingRequestAccepted', + // this.state.currentPubKey, + // (errors: any) => { + // this.transmisssionCB(errors); + + // return true; + // } + // ); + // this.showView(); + // } + + // private transmisssionCB(errors: any) { + // if (!errors) { + // this.setState({ + // success: true, + // }); + // } else { + // return; + // } + // } + + // private skipDevice() { + // window.Whisper.trigger( + // 'devicePairingRequestRejected', + // this.state.currentPubKey + // ); + // this.nextPubKey(); + // this.showView(); + // } + + // private nextPubKey() { + // // FIFO: pop at the back of the array using pop() + // const pubKeyRequests = this.state.pubKeyRequests; + // this.setState({ + // currentPubKey: pubKeyRequests.pop(), + // }); + // } private onKeyUp(event: any) { switch (event.key) { diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx index fe065e6bc..06e3433c0 100644 --- a/ts/components/conversation/ConversationHeader.tsx +++ b/ts/components/conversation/ConversationHeader.tsx @@ -3,12 +3,7 @@ import React from 'react'; import { ContactName } from './ContactName'; import { Avatar } from '../Avatar'; import { Colors, LocalizerType } from '../../types/Util'; -import { - ContextMenu, - ContextMenuTrigger, - MenuItem, - SubMenu, -} from 'react-contextmenu'; +import { ContextMenu, MenuItem, SubMenu } from 'react-contextmenu'; import { SessionIconButton, @@ -22,8 +17,6 @@ import { SessionButtonType, } from '../session/SessionButton'; -import { SessionDropdownTrigger } from '../session/SessionDropdownTrigger'; - interface TimerOption { name: string; value: number; @@ -239,7 +232,7 @@ export class ConversationHeader extends React.Component { ); } - public renderOptions(triggerId: string) { + public renderOptions() { const { showBackButton } = this.props; if (showBackButton) { @@ -247,12 +240,12 @@ export class ConversationHeader extends React.Component { } return ( - + <> - + ); } diff --git a/ts/components/session/SessionModal.tsx b/ts/components/session/SessionModal.tsx index b77d0aba3..3a14421e6 100644 --- a/ts/components/session/SessionModal.tsx +++ b/ts/components/session/SessionModal.tsx @@ -84,7 +84,10 @@ export class SessionModal extends React.PureComponent { }); window.removeEventListener('keyup', this.onKeyUp); - this.props.onClose(); + + if (this.props.onClose) { + this.props.onClose(); + } } public onKeyUp(event: any) { diff --git a/ts/components/session/SessionQRModal.tsx b/ts/components/session/SessionQRModal.tsx index 0c9ed3411..0f4e2ba70 100644 --- a/ts/components/session/SessionQRModal.tsx +++ b/ts/components/session/SessionQRModal.tsx @@ -2,9 +2,11 @@ import React from 'react'; import { QRCode } from 'react-qr-svg'; import { SessionModal } from './SessionModal'; +import { SessionButton } from './SessionButton'; interface Props { value: string; + onClose: any; } export class SessionQRModal extends React.Component { @@ -13,37 +15,30 @@ export class SessionQRModal extends React.Component { } public render() { - const { value } = this.props; - - console.log('skbsvbsgb'); - console.log('skbsvbsgb'); - console.log('skbsvbsgb'); + const { value, onClose } = this.props; return ( null} - onClose={() => null} + onClose={onClose} > -
- -
-

- {window.i18n('QRCodeDescription')} -

+
+ +
+

{window.i18n('QRCodeDescription')}

-
+
- + +
+ +
+
+
- - ); - } - } \ No newline at end of file + ); + } +} diff --git a/ts/components/session/SessionSeedModal.tsx b/ts/components/session/SessionSeedModal.tsx new file mode 100644 index 000000000..621434254 --- /dev/null +++ b/ts/components/session/SessionSeedModal.tsx @@ -0,0 +1,198 @@ +import React from 'react'; + +import { SessionModal } from './SessionModal'; +import { SessionButton } from './SessionButton'; + +interface Props { + onClose: any; +} + +interface State { + error: string; + loadingPassword: boolean; + loadingSeed: boolean; + seed: string; + hasPassword: boolean | null; + passwordHash: string; + passwordValid: boolean; +} + +export class SessionSeedModal extends React.Component { + constructor(props: any) { + super(props); + + this.state = { + error: '', + loadingPassword: true, + loadingSeed: true, + seed: '', + hasPassword: null, + passwordHash: '', + passwordValid: false, + }; + + this.copySeed = this.copySeed.bind(this); + this.getSeed = this.getSeed.bind(this); + this.confirmPassword = this.confirmPassword.bind(this); + this.checkHasPassword = this.checkHasPassword.bind(this); + } + + public render() { + const i18n = window.i18n; + const { onClose } = this.props; + + const maxPasswordLen = 64; + + this.checkHasPassword(); + this.getSeed(); + + const error = this.state.error; + const hasPassword = this.state.hasPassword; + const passwordValid = this.state.passwordValid; + + const loading = this.state.loadingPassword || this.state.loadingSeed; + + return ( + null} + onClose={onClose} + > + {!loading && ( + <> +
+ + {hasPassword && !passwordValid ? ( +
+

{i18n('showSeedPasswordRequest')}

+ + + {error && ( + <> +
+
{error}
+ + )} + +
+ +
+ + + +
+
+ ) : ( + <> +
+

+ {i18n('seedSavePrompt')} +

+
+ + + {this.state.seed} + +
+
+ +
+ + + { + this.copySeed(this.state.seed); + }} + /> +
+ + )} + + )} + + ); + } + + private confirmPassword() { + const passwordHash = this.state.passwordHash; + const passwordValue = $('#seed-input-password').val(); + const isPasswordValid = window.passwordUtil.matchesHash( + passwordValue, + passwordHash + ); + + if (!passwordValue) { + this.setState({ + error: window.i18n('noGivenPassword'), + }); + + return false; + } + + if (passwordHash && !isPasswordValid) { + this.setState({ + error: window.i18n('invalidPassword'), + }); + + return false; + } + + this.setState({ + passwordValid: true, + error: '', + }); + + return true; + } + + private checkHasPassword() { + if (!this.state.loadingPassword) { + return; + } + + const hashPromise = window.Signal.Data.getPasswordHash(); + + hashPromise.then((hash: any) => { + this.setState({ + hasPassword: !!hash, + passwordHash: hash, + loadingPassword: false, + }); + }); + } + + private async getSeed() { + if (this.state.seed) { + return this.state.seed; + } + + const manager = await window.getAccountManager(); + const seed = manager.getCurrentMnemonic(); + + this.setState({ + seed, + loadingSeed: false, + }); + + return seed; + } + + private copySeed(seed: string) { + window.clipboard.writeText(seed); + + window.pushToast({ + title: window.i18n('copiedMnemonic'), + type: 'success', + id: 'copySeedToast', + }); + } +} diff --git a/ts/global.d.ts b/ts/global.d.ts index 9e1d3dbc4..f5a1845cc 100644 --- a/ts/global.d.ts +++ b/ts/global.d.ts @@ -1,6 +1,7 @@ interface Window { getAccountManager: any; mnemonic: any; + clipboard: any; passwordUtil: any; dcodeIO: any; libsignal: any; diff --git a/yarn.lock b/yarn.lock index 20e87d833..66dc3a06e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -139,9 +139,9 @@ integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ== "@types/dompurify@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-2.0.1.tgz#0bf3a9f8ee21d81adb20b8c374ab034d6a74dbf7" - integrity sha512-OQ16dECrRv/I//woKkVUxyVGYR94W3qp3Wy//B63awHVe3h/1/URFqP5a/V2m4k01DEvWs1+z7FWW3xfM1lH3Q== + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-2.0.0.tgz#9616caa5bf2569aea2e4889d4f929d968c081b40" + integrity sha512-g/ilp+Bo6Ljy60i5LnjkGw00X7EIoFjoPGlxqZhV8TJ9fWEzXheioU1O+U/UzCzUA7pUDy/JNMytTQDJctpUHg== dependencies: "@types/trusted-types" "*" @@ -1243,19 +1243,6 @@ buble@^0.19.3: os-homedir "^1.0.1" vlq "^1.0.0" -buffer-alloc-unsafe@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" - integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== - -buffer-alloc@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" - integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== - dependencies: - buffer-alloc-unsafe "^1.1.0" - buffer-fill "^1.0.0" - buffer-crc32@0.2.13, buffer-crc32@^0.2.1: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -1265,20 +1252,10 @@ buffer-equal@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" -buffer-fill@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" - integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= - buffer-from@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531" -buffer-from@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - buffer-indexof@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" @@ -1295,14 +1272,6 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -buffer@^5.4.3: - version "5.4.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.4.3.tgz#3fbc9c69eb713d323e3fc1a895eee0710c072115" - integrity sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - buffers@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" @@ -2553,11 +2522,6 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" -dijkstrajs@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.1.tgz#d3cd81221e3ea40742cfcde556d4e99e98ddc71b" - integrity sha1-082BIh4+pAdCz83lVtTpnpjdxxs= - dir-glob@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" @@ -5018,11 +4982,6 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" -isarray@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - isbinaryfile@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.2.tgz#bfc45642da645681c610cca831022e30af426488" @@ -7022,11 +6981,6 @@ pngjs@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.0.1.tgz#b15086ac1ac47298c8fd3f9cdf364fa9879c4db6" -pngjs@^3.3.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" - integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== - portfinder@^1.0.9: version "1.0.13" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9" @@ -7525,19 +7479,6 @@ qr.js@0.0.0: resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f" integrity sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8= -qrcode@^1.4.4: - version "1.4.4" - resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.4.4.tgz#f0c43568a7e7510a55efc3b88d9602f71963ea83" - integrity sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q== - dependencies: - buffer "^5.4.3" - buffer-alloc "^1.2.0" - buffer-from "^1.1.1" - dijkstrajs "^1.0.1" - isarray "^2.0.1" - pngjs "^3.3.0" - yargs "^13.2.4" - qs@5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/qs/-/qs-5.2.0.tgz#a9f31142af468cb72b25b30136ba2456834916be" @@ -10446,7 +10387,7 @@ yargs@^10.0.3: y18n "^3.2.1" yargs-parser "^8.0.0" -yargs@^13.2.4, yargs@^13.3.0: +yargs@^13.3.0: version "13.3.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==