diff --git a/.github/workflows/pr.components.yml b/.github/workflows/pr.components.yml index 1eb7739f1..fe5f0ab59 100644 --- a/.github/workflows/pr.components.yml +++ b/.github/workflows/pr.components.yml @@ -23,9 +23,10 @@ jobs: - name: Lint components run: yarn lint working-directory: packages/components + - name: Build components - run: yarn build - working-directory: packages/components + run: yarn build:components + - name: Test components run: yarn test working-directory: packages/components diff --git a/.yarn/cache/@otplib-core-npm-12.0.1-4b9787d379-b3c34bc20b.zip b/.yarn/cache/@otplib-core-npm-12.0.1-4b9787d379-b3c34bc20b.zip new file mode 100644 index 000000000..331fb0ab6 Binary files /dev/null and b/.yarn/cache/@otplib-core-npm-12.0.1-4b9787d379-b3c34bc20b.zip differ diff --git a/.yarn/cache/@otplib-plugin-crypto-npm-12.0.1-d0dc5d1d98-6867c74ee8.zip b/.yarn/cache/@otplib-plugin-crypto-npm-12.0.1-d0dc5d1d98-6867c74ee8.zip new file mode 100644 index 000000000..e91c34c65 Binary files /dev/null and b/.yarn/cache/@otplib-plugin-crypto-npm-12.0.1-d0dc5d1d98-6867c74ee8.zip differ diff --git a/.yarn/cache/@otplib-plugin-thirty-two-npm-12.0.1-b85109b20e-920099e40d.zip b/.yarn/cache/@otplib-plugin-thirty-two-npm-12.0.1-b85109b20e-920099e40d.zip new file mode 100644 index 000000000..7c12077b2 Binary files /dev/null and b/.yarn/cache/@otplib-plugin-thirty-two-npm-12.0.1-b85109b20e-920099e40d.zip differ diff --git a/.yarn/cache/@otplib-preset-default-npm-12.0.1-77f04f54c4-8133231384.zip b/.yarn/cache/@otplib-preset-default-npm-12.0.1-77f04f54c4-8133231384.zip new file mode 100644 index 000000000..e3b0629b3 Binary files /dev/null and b/.yarn/cache/@otplib-preset-default-npm-12.0.1-77f04f54c4-8133231384.zip differ diff --git a/.yarn/cache/@otplib-preset-v11-npm-12.0.1-df44c202c1-367cb09397.zip b/.yarn/cache/@otplib-preset-v11-npm-12.0.1-df44c202c1-367cb09397.zip new file mode 100644 index 000000000..591384043 Binary files /dev/null and b/.yarn/cache/@otplib-preset-v11-npm-12.0.1-df44c202c1-367cb09397.zip differ diff --git a/.yarn/cache/@standardnotes-editor-kit-npm-2.2.1-36e60a90e1-d856714b8d.zip b/.yarn/cache/@standardnotes-editor-kit-npm-2.2.1-36e60a90e1-d856714b8d.zip deleted file mode 100644 index 316409117..000000000 Binary files a/.yarn/cache/@standardnotes-editor-kit-npm-2.2.1-36e60a90e1-d856714b8d.zip and /dev/null differ diff --git a/.yarn/cache/otplib-npm-11.0.1-616c4df344-42225f1ccc.zip b/.yarn/cache/otplib-npm-11.0.1-616c4df344-42225f1ccc.zip deleted file mode 100644 index d28d717d3..000000000 Binary files a/.yarn/cache/otplib-npm-11.0.1-616c4df344-42225f1ccc.zip and /dev/null differ diff --git a/.yarn/cache/otplib-npm-12.0.1-77263e8084-4a1b91cf1b.zip b/.yarn/cache/otplib-npm-12.0.1-77263e8084-4a1b91cf1b.zip new file mode 100644 index 000000000..96e6ac6b1 Binary files /dev/null and b/.yarn/cache/otplib-npm-12.0.1-77263e8084-4a1b91cf1b.zip differ diff --git a/.yarn/cache/sass-loader-npm-13.0.0-0cbb09f3b3-f7af03813d.zip b/.yarn/cache/sass-loader-npm-13.0.0-0cbb09f3b3-f7af03813d.zip new file mode 100644 index 000000000..bb06f1f1c Binary files /dev/null and b/.yarn/cache/sass-loader-npm-13.0.0-0cbb09f3b3-f7af03813d.zip differ diff --git a/.yarn/cache/style-loader-npm-0.13.2-eceeb6744f-68bdfbf4e7.zip b/.yarn/cache/style-loader-npm-0.13.2-eceeb6744f-68bdfbf4e7.zip deleted file mode 100644 index e0b3b7006..000000000 Binary files a/.yarn/cache/style-loader-npm-0.13.2-eceeb6744f-68bdfbf4e7.zip and /dev/null differ diff --git a/packages/components/package.json b/packages/components/package.json index 4dee3c7f9..64c00e4d1 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -17,11 +17,13 @@ "package": "node scripts/package.mjs", "version": "./scripts/VERSION.sh" }, + "dependencies": { + "@standardnotes/styles": "workspace:*" + }, "devDependencies": { "@standardnotes/deterministic-zip": "^1.2.0", "@standardnotes/eslint-config-extensions": "^1.0.4", "@standardnotes/features": "^1.45.1", - "@standardnotes/styles": "workspace:*", "copy-webpack-plugin": "^11.0.0", "mini-css-extract-plugin": "^2.6.0", "minimatch": "^5.1.0", diff --git a/packages/components/src/packages/org.standardnotes.token-vault/.babelrc b/packages/components/src/packages/org.standardnotes.token-vault/.babelrc index bd93f018b..c8da2949e 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/.babelrc +++ b/packages/components/src/packages/org.standardnotes.token-vault/.babelrc @@ -3,10 +3,16 @@ [ "@babel/preset-env", { - "modules": false + "modules": false, + "targets": "defaults" } ], - "@babel/preset-react" + [ + "@babel/preset-react", + { + "runtime": "automatic" + } + ] ], "plugins": [ "@babel/plugin-proposal-class-properties", diff --git a/packages/components/src/packages/org.standardnotes.token-vault/.eslintrc b/packages/components/src/packages/org.standardnotes.token-vault/.eslintrc index 9f4d821d8..ecf6cc379 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/.eslintrc +++ b/packages/components/src/packages/org.standardnotes.token-vault/.eslintrc @@ -22,5 +22,9 @@ "react": { "version": "detect" } + }, + "rules": { + "react/jsx-uses-react": "off", + "react/react-in-jsx-scope": "off" } } diff --git a/packages/components/src/packages/org.standardnotes.token-vault/README.md b/packages/components/src/packages/org.standardnotes.token-vault/README.md index 13617f751..f93c2d98b 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/README.md +++ b/packages/components/src/packages/org.standardnotes.token-vault/README.md @@ -1,7 +1,7 @@ ## LICENSE -As of version 2.0, TokenVault houses a public-source, but not open-source presence. This means you are free to browse the code and even use the code *privately*, but you may not redistribute, repackage, or otherwise use its public distributable/release assets on GitHub, for free or for profit. +As of version 2.0, TokenVault houses a public-source, but not open-source presence. This means you are free to browse the code and even use the code _privately_, but you may not redistribute, repackage, or otherwise use its public distributable/release assets on GitHub, for free or for profit. For more information, read [this blog post](https://blog.standardnotes.com/why-tokenvault-is-going-public-source/). -Previous versions of TokenVault retain the license they were released with. \ No newline at end of file +Previous versions of TokenVault retain the license they were released with. diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/App.js b/packages/components/src/packages/org.standardnotes.token-vault/app/App.js deleted file mode 100644 index dc3fb380d..000000000 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/App.js +++ /dev/null @@ -1,14 +0,0 @@ -import 'regenerator-runtime/runtime'; - -import React from 'react'; -import Home from '@Components/Home'; - -export default class App extends React.Component { - constructor(props) { - super(props); - } - - render() { - return ; - } -} diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/components/AuthEntry.jsx b/packages/components/src/packages/org.standardnotes.token-vault/app/components/AuthEntry.jsx index 17696b516..52778bfc2 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/components/AuthEntry.jsx +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/components/AuthEntry.jsx @@ -1,14 +1,14 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { totp } from '@Lib/otp'; -import CountdownPie from '@Components/CountdownPie'; -import AuthMenu from '@Components/AuthMenu'; -import DragIndicator from '../assets/svg/drag-indicator.svg'; -import { getEntryColor, getVarColorForContrast, hexColorToRGB } from '@Lib/utils'; +import AuthMenu from '@Components/AuthMenu' +import CountdownPie from '@Components/CountdownPie' +import { totp } from '@Lib/otp' +import { getEntryColor, getVarColorForContrast, hexColorToRGB } from '@Lib/utils' +import PropTypes from 'prop-types' +import React from 'react' +import DragIndicator from '../assets/svg/drag-indicator.svg' export default class AuthEntry extends React.Component { constructor(props) { - super(props); + super(props) this.state = { token: '', @@ -16,106 +16,109 @@ export default class AuthEntry extends React.Component { entryStyle: { color: '', backgroundColor: '', - } - }; + }, + } - this.updateToken(); + this.updateToken() } getTimeLeft() { - const seconds = new Date().getSeconds(); - return seconds > 29 ? 60 - seconds : 30 - seconds; + const seconds = new Date().getSeconds() + return seconds > 29 ? 60 - seconds : 30 - seconds } updateToken = async () => { - const { secret } = this.props.entry; - const token = await totp.gen(secret); + const { secret } = this.props.entry + if (!secret) { + return + } - const timeLeft = this.getTimeLeft(); + const token = await totp.gen(secret) + const timeLeft = this.getTimeLeft() this.setState({ token, - timeLeft - }); + timeLeft, + }) - this.timer = setTimeout(this.updateToken, timeLeft * 1000); + this.timer = setTimeout(this.updateToken, timeLeft * 1000) } componentDidMount() { - this.updateEntryStyle(); + this.updateEntryStyle() } componentDidUpdate(prevProps) { // If the secret changed make sure to recalculate token if (prevProps.entry.secret !== this.props.entry.secret) { - clearTimeout(this.timer); - this.timer = setTimeout(this.updateToken, 0); + clearTimeout(this.timer) + this.timer = setTimeout(this.updateToken, 0) } if (prevProps.lastUpdated !== this.props.lastUpdated) { - this.updateEntryStyle(true); + this.updateEntryStyle(true) } } componentWillUnmount() { - clearTimeout(this.timer); + clearTimeout(this.timer) } - handleInputChange = event => { - const target = event.target; - const name = target.name; + handleInputChange = (event) => { + const target = event.target + const name = target.name this.props.onEntryChange({ id: this.props.id, name, - value: target.value - }); + value: target.value, + }) } copyToClipboard = (value) => { - const textField = document.createElement('textarea'); - textField.innerText = value; - document.body.appendChild(textField); - textField.select(); - document.execCommand('copy'); - textField.remove(); - this.props.onCopyValue(); + const textField = document.createElement('textarea') + textField.innerText = value + document.body.appendChild(textField) + textField.select() + document.execCommand('copy') + textField.remove() + this.props.onCopyValue() } updateEntryStyle = (useDelay = false) => { /** - * A short amount of time to wait in order to prevent reading + * A short amount of time to wait in order to prevent reading * stale information from the DOM after a theme is activated. */ - const DELAY_BEFORE_READING_PROPERTIES = useDelay ? 0 : 50; + const DELAY_BEFORE_READING_PROPERTIES = useDelay ? 0 : 50 setTimeout(() => { - const { entryStyle } = this.state; - const entryColor = getEntryColor(document, this.props.entry); + const { entryStyle } = this.state + const entryColor = getEntryColor(document, this.props.entry) if (entryColor) { // The background color for the entry. - entryStyle.backgroundColor = entryColor; + entryStyle.backgroundColor = entryColor - const rgbColor = hexColorToRGB(entryColor); - const varColor = getVarColorForContrast(rgbColor); + const rgbColor = hexColorToRGB(entryColor) + const varColor = getVarColorForContrast(rgbColor) // The foreground color for the entry. - entryStyle.color = `var(${varColor})`; + entryStyle.color = `var(${varColor})` } this.setState({ - entryStyle - }); - }, DELAY_BEFORE_READING_PROPERTIES); + entryStyle, + }) + }, DELAY_BEFORE_READING_PROPERTIES) } render() { - const { service, account, notes, password } = this.props.entry; - const { id, onEdit, onRemove, canEdit, style, innerRef, ...divProps } = this.props; - const { token, timeLeft, entryStyle } = this.state; + const { service, account, notes, password, secret } = this.props.entry + const { id, onEdit, onRemove, canEdit, style, innerRef, ...divProps } = this.props + const { token, timeLeft, entryStyle } = this.state - delete divProps.onCopyValue; - delete divProps.lastUpdated; + delete divProps.onCopyValue + delete divProps.lastUpdated return (
{canEdit && (
- +
)}
@@ -146,27 +149,29 @@ export default class AuthEntry extends React.Component { {password && (
this.copyToClipboard(password)}> - •••••••••••• + {'•'.repeat(password.length)}
)}
-
-
this.copyToClipboard(token)}> -
{token.slice(0, 3)}
-
{token.slice(3, 6)}
+ {secret && ( +
+
this.copyToClipboard(token)}> +
{token.slice(0, 3)}
+
{token.slice(3, 6)}
+
+
+ +
-
- -
-
+ )}
{canEdit && (
@@ -179,7 +184,7 @@ export default class AuthEntry extends React.Component { )}
- ); + ) } } @@ -193,5 +198,5 @@ AuthEntry.propTypes = { canEdit: PropTypes.bool.isRequired, innerRef: PropTypes.func.isRequired, lastUpdated: PropTypes.number.isRequired, - style: PropTypes.object.isRequired -}; + style: PropTypes.object.isRequired, +} diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/components/AuthMenu.jsx b/packages/components/src/packages/org.standardnotes.token-vault/app/components/AuthMenu.jsx index 494bea9ce..66c155a6a 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/components/AuthMenu.jsx +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/components/AuthMenu.jsx @@ -1,37 +1,37 @@ -import React from 'react'; -import PropTypes from 'prop-types'; +import PropTypes from 'prop-types' +import React from 'react' export default class AuthMenu extends React.Component { constructor(props) { - super(props); + super(props) this.state = { - show: false - }; + show: false, + } } onToggle = () => { this.setState({ - show: !this.state.show - }); + show: !this.state.show, + }) } onEdit = () => { - this.onToggle(); - this.props.onEdit(); + this.onToggle() + this.props.onEdit() } onRemove = () => { - this.onToggle(); - this.props.onRemove(); + this.onToggle() + this.props.onRemove() } render() { - const { buttonColor } = this.props; + const { buttonColor } = this.props - const buttonStyle = {}; + const buttonStyle = {} if (buttonColor) { - buttonStyle.color = buttonColor; + buttonStyle.color = buttonColor } return ( @@ -39,24 +39,25 @@ export default class AuthMenu extends React.Component {
•••
- {this.state.show && ( -
, -
-
-
Edit
+ {this.state.show && + ((
), + ( +
+
+
Edit
+
+
+
Remove
+
-
-
Remove
-
-
- )} + ))}
- ); + ) } } AuthMenu.propTypes = { onEdit: PropTypes.func.isRequired, onRemove: PropTypes.func.isRequired, - buttonColor: PropTypes.string -}; + buttonColor: PropTypes.string, +} diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/components/ConfirmDialog.jsx b/packages/components/src/packages/org.standardnotes.token-vault/app/components/ConfirmDialog.jsx index e9a4c3093..6539e3848 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/components/ConfirmDialog.jsx +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/components/ConfirmDialog.jsx @@ -1,5 +1,4 @@ -import React from 'react'; -import PropTypes from 'prop-types'; +import PropTypes from 'prop-types' const ConfirmDialog = ({ title, message, onConfirm, onCancel }) => (
@@ -26,13 +25,13 @@ const ConfirmDialog = ({ title, message, onConfirm, onCancel }) => (
-); +) ConfirmDialog.propTypes = { title: PropTypes.string.isRequired, message: PropTypes.string.isRequired, onConfirm: PropTypes.func.isRequired, - onCancel: PropTypes.func.isRequired -}; + onCancel: PropTypes.func.isRequired, +} -export default ConfirmDialog; +export default ConfirmDialog diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/components/CopyNotification.jsx b/packages/components/src/packages/org.standardnotes.token-vault/app/components/CopyNotification.jsx index c085b08fa..9584bb9c8 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/components/CopyNotification.jsx +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/components/CopyNotification.jsx @@ -1,18 +1,15 @@ -import React from 'react'; -import PropTypes from 'prop-types'; +import PropTypes from 'prop-types' const CopyNotification = ({ isVisible }) => ( -
+
Copied value to clipboard.
-); +) -CopyNotification.propTypes = { +CopyNotification.propTypes = { isVisible: PropTypes.bool.isRequired, -}; +} -export default CopyNotification; +export default CopyNotification diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/components/CountdownPie.jsx b/packages/components/src/packages/org.standardnotes.token-vault/app/components/CountdownPie.jsx index 3f482c40e..cfe1e84f1 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/components/CountdownPie.jsx +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/components/CountdownPie.jsx @@ -1,11 +1,9 @@ -import React, { useEffect } from 'react'; -import PropTypes from 'prop-types'; +import PropTypes from 'prop-types' +import { useEffect } from 'react' -const animationName = (token) => `countdown${token}`; +const animationName = (token) => `countdown${token}` -const rotaAnimation = (token, offset) => `@keyframes rota_${animationName( - token -)} { +const rotaAnimation = (token, offset) => `@keyframes rota_${animationName(token)} { 0% { transform: rotate(${offset}deg); } @@ -13,11 +11,9 @@ const rotaAnimation = (token, offset) => `@keyframes rota_${animationName( 100% { transform: rotate(360deg); } -}`; +}` -const opaAnimation = (token, offset) => `@keyframes opa_${animationName( - token -)} { +const opaAnimation = (token, offset) => `@keyframes opa_${animationName(token)} { 0% { opacity: 1; } @@ -26,12 +22,9 @@ const opaAnimation = (token, offset) => `@keyframes opa_${animationName( 100% { opacity: 0; } -}`; +}` -const opaReverseAnimation = ( - token, - offset -) => `@keyframes opa_r_${animationName(token)} { +const opaReverseAnimation = (token, offset) => `@keyframes opa_r_${animationName(token)} { 0% { opacity: 0; } @@ -40,81 +33,81 @@ const opaReverseAnimation = ( 100% { opacity: 1; } -}`; +}` function calculateOpaOffset(timeLeft, total) { - const percentage = calculatePercentage(timeLeft, total) * 100; - const percTo50 = 50 - percentage; + const percentage = calculatePercentage(timeLeft, total) * 100 + const percTo50 = 50 - percentage // 8 is an offset because the animation is not in sync otherwise - return percTo50 < 0 ? 0 : Math.ceil(Math.min(percTo50 + 8, 50)); + return percTo50 < 0 ? 0 : Math.ceil(Math.min(percTo50 + 8, 50)) } function calculateRotaOffset(timeLeft, total) { - return calculatePercentage(timeLeft, total) * 360; + return calculatePercentage(timeLeft, total) * 360 } function calculatePercentage(timeLeft, total) { - return (total - timeLeft) / total; + return (total - timeLeft) / total } function useRotateAnimation(token, timeLeft, total) { useEffect( function createRotateAnimation() { - const style = document.createElement('style'); - document.head.appendChild(style); - const styleSheet = style.sheet; + const style = document.createElement('style') + document.head.appendChild(style) + const styleSheet = style.sheet - const rotaKeyframes = rotaAnimation( - token, - calculateRotaOffset(timeLeft, total) - ); - const opaKeyframes = opaAnimation(token, calculateOpaOffset(timeLeft, total)); - const opaReverseKeyframes = opaReverseAnimation( - token, - calculateOpaOffset(timeLeft, total) - ); + const rotaKeyframes = rotaAnimation(token, calculateRotaOffset(timeLeft, total)) + const opaKeyframes = opaAnimation(token, calculateOpaOffset(timeLeft, total)) + const opaReverseKeyframes = opaReverseAnimation(token, calculateOpaOffset(timeLeft, total)) - styleSheet.insertRule(rotaKeyframes, styleSheet.cssRules.length); - styleSheet.insertRule(opaKeyframes, styleSheet.cssRules.length); - styleSheet.insertRule(opaReverseKeyframes, styleSheet.cssRules.length); + styleSheet.insertRule(rotaKeyframes, styleSheet.cssRules.length) + styleSheet.insertRule(opaKeyframes, styleSheet.cssRules.length) + styleSheet.insertRule(opaReverseKeyframes, styleSheet.cssRules.length) function cleanup() { - style.remove(); + style.remove() } - const timer = setTimeout(cleanup, timeLeft * 1000); + const timer = setTimeout(cleanup, timeLeft * 1000) return () => { - clearTimeout(timer); - cleanup(); - }; + clearTimeout(timer) + cleanup() + } }, - [token, timeLeft, total] - ); + [token, timeLeft, total], + ) } const CountdownPie = ({ token, timeLeft, total, bgColor, fgColor }) => { - useRotateAnimation(token, timeLeft, total); + useRotateAnimation(token, timeLeft, total) return ( -
+
+
-
{ }} />
- ); -}; + ) +} CountdownPie.propTypes = { token: PropTypes.string.isRequired, timeLeft: PropTypes.number.isRequired, total: PropTypes.number.isRequired, bgColor: PropTypes.string, - fgColor: PropTypes.string -}; + fgColor: PropTypes.string, +} -export default CountdownPie; +export default CountdownPie diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/components/DataErrorAlert.jsx b/packages/components/src/packages/org.standardnotes.token-vault/app/components/DataErrorAlert.jsx index 9af14e333..f10369833 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/components/DataErrorAlert.jsx +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/components/DataErrorAlert.jsx @@ -1,5 +1,3 @@ -import React from 'react'; - const DataErrorAlert = () => (
@@ -10,15 +8,14 @@ const DataErrorAlert = () => (
- The note you selected already has existing data that is not valid - with this editor. Please clear the note, or select a new one, and - try again. + The note you selected already has existing data that is not valid with this editor. Please clear the note, + or select a new one, and try again.
-); +) -export default DataErrorAlert; +export default DataErrorAlert diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/components/EditEntry.jsx b/packages/components/src/packages/org.standardnotes.token-vault/app/components/EditEntry.jsx index e6cd79b6e..f67f84cbd 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/components/EditEntry.jsx +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/components/EditEntry.jsx @@ -1,10 +1,10 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import QRCodeReader from '@Components/QRCodeReader'; -import { secretPattern } from '@Lib/otp'; -import { TwitterPicker } from 'react-color'; -import { SKAlert } from 'sn-stylekit'; -import { contextualColors, defaultBgColor, getAllContextualColors, getEntryColor } from '@Lib/utils'; +import QRCodeReader from '@Components/QRCodeReader' +import { secretPattern } from '@Lib/otp' +import { contextualColors, defaultBgColor, getAllContextualColors, getEntryColor } from '@Lib/utils' +import { SKAlert } from '@standardnotes/styles' +import PropTypes from 'prop-types' +import React from 'react' +import { TwitterPicker } from 'react-color' export default class EditEntry extends React.Component { static defaultProps = { @@ -12,93 +12,102 @@ export default class EditEntry extends React.Component { service: '', account: '', secret: '', - notes: '' - } - }; + notes: '', + }, + } constructor(props) { - super(props); + super(props) + + const { id, entry } = props this.state = { - id: this.props.id, - entry: this.props.entry, + id: id, + entry, showColorPicker: false, - qrCodeError: false - }; + qrCodeError: false, + is2fa: id !== undefined ? !!entry.secret : true, + } } formatSecret(secret) { - return secret.replace(/\s/g, '').toUpperCase(); + return secret.replace(/\s/g, '').toUpperCase() } - handleInputChange = event => { - const target = event.target; - const name = target.name; + handleInputChange = (event) => { + const target = event.target + const name = target.name - const value = name === 'secret' ? - this.formatSecret(target.value) : target.value; + const value = name === 'secret' ? this.formatSecret(target.value) : target.value - this.setState(state => ({ + this.setState((state) => ({ entry: { ...state.entry, - [name]: value - } - })); - }; + [name]: value, + }, + })) + } handleSwatchClick = () => { this.setState({ - showColorPicker: !this.state.showColorPicker - }); - }; + showColorPicker: !this.state.showColorPicker, + }) + } handleColorPickerClose = () => { this.setState({ - showColorPicker: false - }); - }; + showColorPicker: false, + }) + } removeColor = () => { this.setState((state) => { - delete state.entry.color; + delete state.entry.color return { - entry: state.entry - }; - }); - }; + entry: state.entry, + } + }) + } onSave = () => { - const { id, entry } = this.state; - this.props.onSave({ id, entry }); - }; + const { id, entry, is2fa } = this.state + this.props.onSave({ + id, + entry: { + ...entry, + secret: is2fa ? entry.secret : '', + }, + }) + } - onQRCodeSuccess = otpData => { - const { issuer: labelIssuer, account } = otpData.label; - const { issuer: queryIssuer, secret } = otpData.query; + onQRCodeSuccess = (otpData) => { + const { issuer: labelIssuer, account } = otpData.label + const { issuer: queryIssuer, secret } = otpData.query this.setState({ entry: { service: labelIssuer || queryIssuer || '', account, - secret: this.formatSecret(secret) - } - }); - }; + secret: this.formatSecret(secret), + }, + is2fa: true, + }) + } - onQRCodeError = message => { + onQRCodeError = (message) => { this.setState({ - qrCodeError: message - }); - }; + qrCodeError: message, + }) + } dismissQRCodeError = () => { this.setState({ - qrCodeError: false - }); - }; + qrCodeError: false, + }) + } render() { - const { id, entry, showColorPicker, qrCodeError } = this.state; + const { id, entry, showColorPicker, qrCodeError, is2fa } = this.state const qrCodeAlert = new SKAlert({ title: 'Error', @@ -107,63 +116,69 @@ export default class EditEntry extends React.Component { { text: 'OK', style: 'info', - action: this.dismissQRCodeError - } - ] - }); + action: this.dismissQRCodeError, + }, + ], + }) if (qrCodeError) { - qrCodeAlert.present(); + qrCodeAlert.present() } - const entryColor = getEntryColor(document, entry); + const entryColor = getEntryColor(document, entry) const swatchStyle = { width: '36px', height: '14px', borderRadius: '2px', background: `${entryColor ?? defaultBgColor}`, - }; + } - const themeColors = getAllContextualColors(document); - const defaultColorOptions = [ - ...themeColors, - '#658bdb', - '#4CBBFC', - '#FF794D', - '#EF5276', - '#91B73D', - '#9B7ECF' - ]; + const themeColors = getAllContextualColors(document) + const defaultColorOptions = [...themeColors, '#658bdb', '#4CBBFC', '#FF794D', '#EF5276', '#91B73D', '#9B7ECF'] const handleColorChange = (color) => { - let selectedColor = color.hex.toUpperCase(); - const colorIndex = defaultColorOptions.indexOf(selectedColor); + let selectedColor = color.hex.toUpperCase() + const colorIndex = defaultColorOptions.indexOf(selectedColor) if (colorIndex > -1 && colorIndex <= themeColors.length - 1) { - selectedColor = contextualColors[colorIndex]; + selectedColor = contextualColors[colorIndex] } - this.setState(state => ({ + this.setState((state) => ({ entry: { ...state.entry, - color: selectedColor - } - })); - }; + color: selectedColor, + }, + })) + } + + const handleTypeChange = ({ target }) => { + this.setState({ + is2fa: target.value === '2fa', + }) + } return (
- {id != null ? 'Edit entry' : 'Add new entry'} +
+
+
{id != null ? 'Edit entry' : 'Add new entry'}
+
+ + +
+
+
- {id == null && ( - - )} + {id == null && } <> {entryColor && (
@@ -195,15 +210,26 @@ export default class EditEntry extends React.Component { onChange={this.handleInputChange} type="text" /> + {is2fa && ( + + )} -
{showColorPicker && (
@@ -231,12 +249,9 @@ export default class EditEntry extends React.Component { onChangeComplete={handleColorChange} triangle="top-right" onSwatchHover={(color, event) => { - const hoveredColor = color.hex.toUpperCase(); + const hoveredColor = color.hex.toUpperCase() if (themeColors.includes(hoveredColor)) { - event.target.setAttribute( - 'title', - 'This color will change depending on your active theme.' - ); + event.target.setAttribute('title', 'This color will change depending on your active theme.') } }} /> @@ -248,9 +263,7 @@ export default class EditEntry extends React.Component {
Cancel
@@ -258,7 +271,7 @@ export default class EditEntry extends React.Component {
- ); + ) } } @@ -266,5 +279,5 @@ EditEntry.propTypes = { id: PropTypes.number, entry: PropTypes.object.isRequired, onSave: PropTypes.func.isRequired, - onCancel: PropTypes.func.isRequired -}; + onCancel: PropTypes.func.isRequired, +} diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/components/Home.jsx b/packages/components/src/packages/org.standardnotes.token-vault/app/components/Home.jsx index 50b8e611e..b5c063c1f 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/components/Home.jsx +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/components/Home.jsx @@ -1,12 +1,12 @@ -import React from 'react'; -import update from 'immutability-helper'; -import EditEntry from '@Components/EditEntry'; -import ViewEntries from '@Components/ViewEntries'; -import ConfirmDialog from '@Components/ConfirmDialog'; -import DataErrorAlert from '@Components/DataErrorAlert'; -import EditorKit from '@standardnotes/editor-kit'; -import ReorderIcon from '../assets/svg/reorder-icon.svg'; -import CopyNotification from './CopyNotification'; +import ConfirmDialog from '@Components/ConfirmDialog' +import DataErrorAlert from '@Components/DataErrorAlert' +import EditEntry from '@Components/EditEntry' +import ViewEntries from '@Components/ViewEntries' +import EditorKit from '@standardnotes/editor-kit' +import update from 'immutability-helper' +import React from 'react' +import ReorderIcon from '../assets/svg/reorder-icon.svg' +import CopyNotification from './CopyNotification' const initialState = { text: '', @@ -19,31 +19,31 @@ const initialState = { displayCopy: false, canEdit: true, searchValue: '', - lastUpdated: 0 -}; + lastUpdated: 0, +} export default class Home extends React.Component { constructor(props) { - super(props); - this.configureEditorKit(); - this.state = initialState; + super(props) + this.configureEditorKit() + this.state = initialState } configureEditorKit() { const delegate = { - setEditorRawText: text => { - let parseError = false; - let entries = []; + setEditorRawText: (text) => { + let parseError = false + let entries = [] if (text) { try { - entries = this.parseNote(text); + entries = this.parseNote(text) } catch (e) { // Couldn't parse the content - parseError = true; + parseError = true this.setState({ - parseError: true - }); + parseError: true, + }) } } @@ -51,225 +51,225 @@ export default class Home extends React.Component { ...initialState, text, parseError, - entries - }); + entries, + }) }, - generateCustomPreview: text => { - let entries = []; + generateCustomPreview: (text) => { + let entries = [] try { - entries = this.parseNote(text); + entries = this.parseNote(text) } finally { // eslint-disable-next-line no-unsafe-finally return { html: `
${entries.length} TokenVault Entries
`, plain: `${entries.length} TokenVault Entries`, - }; + } } }, - clearUndoHistory: () => { }, + clearUndoHistory: () => {}, getElementsBySelector: () => [], onNoteLockToggle: (isLocked) => { this.setState({ - canEdit: !isLocked - }); + canEdit: !isLocked, + }) }, onThemesChange: () => { this.setState({ lastUpdated: Date.now(), - }); - } - }; + }) + }, + } this.editorKit = new EditorKit(delegate, { mode: 'json', - supportsFileSafe: false - }); + supportsFileSafe: false, + }) } parseNote(text) { - const entries = JSON.parse(text); + const entries = JSON.parse(text) if (entries instanceof Array) { if (entries.length === 0) { - return []; + return [] } for (const entry of entries) { if (!('service' in entry)) { - throw Error('Service key is missing for an entry.'); + throw Error('Service key is missing for an entry.') } - if (!('secret' in entry)) { - throw Error('Secret key is missing for an entry.'); + if (!('secret' in entry) && !('password' in entry)) { + throw Error('An entry does not have a secret key or a password.') } } - return entries; + return entries } - return []; + return [] } saveNote(entries) { - this.editorKit.onEditorValueChanged(JSON.stringify(entries, null, 2)); + this.editorKit.onEditorValueChanged(JSON.stringify(entries, null, 2)) } // Entry operations - addEntry = entry => { - this.setState(state => { - const entries = state.entries.concat([entry]); - this.saveNote(entries); + addEntry = (entry) => { + this.setState((state) => { + const entries = state.entries.concat([entry]) + this.saveNote(entries) return { editMode: false, editEntry: null, - entries - }; - }); - }; + entries, + } + }) + } editEntry = ({ id, entry }) => { - this.setState(state => { - const entries = update(state.entries, { [id]: { $set: entry } }); - this.saveNote(entries); + this.setState((state) => { + const entries = update(state.entries, { [id]: { $set: entry } }) + this.saveNote(entries) return { editMode: false, editEntry: null, - entries - }; - }); - }; + entries, + } + }) + } - removeEntry = id => { - this.setState(state => { - const entries = update(state.entries, { $splice: [[id, 1]] }); - this.saveNote(entries); + removeEntry = (id) => { + this.setState((state) => { + const entries = update(state.entries, { $splice: [[id, 1]] }) + this.saveNote(entries) return { confirmRemove: false, editEntry: null, - entries - }; - }); - }; + entries, + } + }) + } // Event Handlers onAddNew = () => { if (!this.state.canEdit) { - return; + return } this.setState({ editMode: true, - editEntry: null - }); - }; + editEntry: null, + }) + } - onEdit = id => { + onEdit = (id) => { if (!this.state.canEdit) { - return; + return } - this.setState(state => ({ + this.setState((state) => ({ editMode: true, editEntry: { id, - entry: state.entries[id] - } - })); - }; + entry: state.entries[id], + }, + })) + } onCancel = () => { this.setState({ confirmRemove: false, confirmReorder: false, editMode: false, - editEntry: null - }); - }; + editEntry: null, + }) + } - onRemove = id => { + onRemove = (id) => { if (!this.state.canEdit) { - return; + return } - this.setState(state => ({ + this.setState((state) => ({ confirmRemove: true, editEntry: { id, - entry: state.entries[id] - } - })); - }; + entry: state.entries[id], + }, + })) + } onSave = ({ id, entry }) => { // If there's no ID it's a new note if (id != null) { - this.editEntry({ id, entry }); + this.editEntry({ id, entry }) } else { - this.addEntry(entry); + this.addEntry(entry) } - }; + } onCopyValue = () => { this.setState({ - displayCopy: true - }); + displayCopy: true, + }) if (this.clearTooltipTimer) { - clearTimeout(this.clearTooltipTimer); + clearTimeout(this.clearTooltipTimer) } this.clearTooltipTimer = setTimeout(() => { this.setState({ - displayCopy: false - }); - }, 2000); - }; + displayCopy: false, + }) + }, 2000) + } updateEntries = (entries) => { - this.saveNote(entries); + this.saveNote(entries) this.setState({ - entries - }); - }; + entries, + }) + } onReorderEntries = () => { if (!this.state.canEdit) { - return; + return } this.setState({ - confirmReorder: true - }); - }; + confirmReorder: true, + }) + } - onSearchChange = event => { - const target = event.target; + onSearchChange = (event) => { + const target = event.target this.setState({ - searchValue: target.value.toLowerCase() - }); - }; + searchValue: target.value.toLowerCase(), + }) + } clearSearchValue = () => { this.setState({ - searchValue: '' - }); + searchValue: '', + }) } reorderEntries = () => { - const { entries } = this.state; + const { entries } = this.state const orderedEntries = entries.sort((a, b) => { - const serviceA = a.service.toLowerCase(); - const serviceB = b.service.toLowerCase(); - return (serviceA < serviceB) ? -1 : (serviceA > serviceB) ? 1 : 0; - }); - this.saveNote(orderedEntries); + const serviceA = a.service.toLowerCase() + const serviceB = b.service.toLowerCase() + return serviceA < serviceB ? -1 : serviceA > serviceB ? 1 : 0 + }) + this.saveNote(orderedEntries) this.setState({ entries: orderedEntries, - confirmReorder: false - }); - }; + confirmReorder: false, + }) + } render() { - const editEntry = this.state.editEntry || {}; + const editEntry = this.state.editEntry || {} const { canEdit, displayCopy, @@ -279,15 +279,15 @@ export default class Home extends React.Component { confirmRemove, confirmReorder, searchValue, - lastUpdated - } = this.state; + lastUpdated, + } = this.state if (parseError) { return (
- ); + ) } return ( @@ -327,12 +327,7 @@ export default class Home extends React.Component { )}
{editMode ? ( - + ) : (
- ); + ) } } diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/components/QRCodeReader.jsx b/packages/components/src/packages/org.standardnotes.token-vault/app/components/QRCodeReader.jsx index aae6e1b73..935d2f159 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/components/QRCodeReader.jsx +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/components/QRCodeReader.jsx @@ -1,91 +1,85 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import jsQR from 'jsqr'; -import { parseKeyUri } from '@Lib/otp'; +import { parseKeyUri } from '@Lib/otp' +import jsQR from 'jsqr' +import PropTypes from 'prop-types' +import React from 'react' const convertToGrayScale = (imageData) => { if (!imageData) { - return; + return } for (let i = 0; i < imageData.data.length; i += 4) { - const count = imageData.data[i] + imageData.data[i + 1] + imageData.data[i + 2]; - let color = 0; + const count = imageData.data[i] + imageData.data[i + 1] + imageData.data[i + 2] + let color = 0 if (count > 510) { - color = 255; + color = 255 } else if (count > 255) { - color = 127.5; + color = 127.5 } - imageData.data[i] = color; - imageData.data[i + 1] = color; - imageData.data[i + 2] = color; - imageData.data[i + 3] = 255; + imageData.data[i] = color + imageData.data[i + 1] = color + imageData.data[i + 2] = color + imageData.data[i + 3] = 255 } - return imageData; -}; + return imageData +} export default class QRCodeReader extends React.Component { - onImageSelected = evt => { - const file = evt.target.files[0]; - const url = URL.createObjectURL(file); - const img = new Image(); - const self = this; + onImageSelected = (evt) => { + const file = evt.target.files[0] + const url = URL.createObjectURL(file) + const img = new Image() + const self = this - img.onload = function() { - URL.revokeObjectURL(this.src); + img.onload = function () { + URL.revokeObjectURL(this.src) - const canvas = document.createElement('canvas'); - const context = canvas.getContext('2d'); - canvas.width = this.width; - canvas.height = this.height; - context.drawImage(this, 0, 0); + const canvas = document.createElement('canvas') + const context = canvas.getContext('2d') + canvas.width = this.width + canvas.height = this.height + context.drawImage(this, 0, 0) - let imageData = context.getImageData(0, 0, this.width, this.height); - imageData = convertToGrayScale(imageData); + let imageData = context.getImageData(0, 0, this.width, this.height) + imageData = convertToGrayScale(imageData) - const code = jsQR(imageData.data, imageData.width, imageData.height); + const code = jsQR(imageData.data, imageData.width, imageData.height) - const { onError, onSuccess } = self.props; + const { onError, onSuccess } = self.props if (code) { - const otpData = parseKeyUri(code.data); + const otpData = parseKeyUri(code.data) if (otpData.type !== 'totp') { - onError(`The '${otpData.type}' type is not supported.`); + onError(`The '${otpData.type}' type is not supported.`) } else { - onSuccess(otpData); + onSuccess(otpData) } } else { - onError('Error reading QR code from image. Please try again.'); + onError('Error reading QR code from image. Please try again.') } - }; + } - img.src = url; + img.src = url - return false; - }; + return false + } render() { return (
- +
Upload QR Code
+
- ); + ) } } QRCodeReader.propTypes = { onError: PropTypes.func.isRequired, - onSuccess: PropTypes.func.isRequired -}; + onSuccess: PropTypes.func.isRequired, +} diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/components/ViewEntries.jsx b/packages/components/src/packages/org.standardnotes.token-vault/app/components/ViewEntries.jsx index ca0802662..7929d8179 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/components/ViewEntries.jsx +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/components/ViewEntries.jsx @@ -1,48 +1,39 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import AuthEntry from '@Components/AuthEntry'; -import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'; +import AuthEntry from '@Components/AuthEntry' +import PropTypes from 'prop-types' +import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd' const reorderEntries = (list, startIndex, endIndex) => { - const result = Array.from(list); - const [removed] = result.splice(startIndex, 1); - result.splice(endIndex, 0, removed); + const result = Array.from(list) + const [removed] = result.splice(startIndex, 1) + result.splice(endIndex, 0, removed) - return result; -}; + return result +} const ViewEntries = ({ entries, onEdit, onRemove, onCopyValue, canEdit, updateEntries, searchValue, lastUpdated }) => { const onDragEnd = (result) => { - const droppedOutsideList = !result.destination; + const droppedOutsideList = !result.destination if (droppedOutsideList) { - return; + return } - const orderedEntries = reorderEntries( - entries, - result.source.index, - result.destination.index - ); + const orderedEntries = reorderEntries(entries, result.source.index, result.destination.index) - updateEntries(orderedEntries); - }; + updateEntries(orderedEntries) + } return ( {(provided) => ( -
+
{entries.map((entry, index) => { /** * Filtering entries by account, service and notes properties. */ - const combinedString = `${entry.account}${entry.service}${entry.notes}`.toLowerCase(); + const combinedString = `${entry.account}${entry.service}${entry.notes}`.toLowerCase() if (searchValue && !combinedString.includes(searchValue)) { - return; + return } return ( )} - ); + ) })} {provided.placeholder}
)} - ); -}; + ) +} -ViewEntries.propTypes = { +ViewEntries.propTypes = { entries: PropTypes.arrayOf(PropTypes.object), onEdit: PropTypes.func.isRequired, onRemove: PropTypes.func.isRequired, @@ -85,7 +76,7 @@ ViewEntries.propTypes = { canEdit: PropTypes.bool.isRequired, lastUpdated: PropTypes.number.isRequired, updateEntries: PropTypes.func.isRequired, - searchValue: PropTypes.string -}; + searchValue: PropTypes.string, +} -export default ViewEntries; +export default ViewEntries diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/index.js b/packages/components/src/packages/org.standardnotes.token-vault/app/index.js new file mode 100644 index 000000000..4d266b495 --- /dev/null +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/index.js @@ -0,0 +1,4 @@ +import Home from '@Components/Home' +import ReactDOM from 'react-dom' + +ReactDOM.render(, document.body.appendChild(document.createElement('div'))) diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/lib/otp.js b/packages/components/src/packages/org.standardnotes.token-vault/app/lib/otp.js index e757dd782..c6d25cc26 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/lib/otp.js +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/lib/otp.js @@ -1,12 +1,5 @@ -import { - base32ToHex, - leftpad, - decToHex, - bufToHex, - hextoBuf, - hexToBytes -} from '@Lib/utils'; -export { secretPattern, parseKeyUri } from '@Lib/utils'; +import { base32ToHex, bufToHex, decToHex, hextoBuf, hexToBytes, leftpad } from '@Lib/utils' +export { parseKeyUri, secretPattern } from '@Lib/utils' class Hotp { /** @@ -25,25 +18,25 @@ class Hotp { * */ async gen(secret, opt) { - var key = base32ToHex(secret) || ''; - opt = opt || {}; - var counter = opt.counter || 0; + var key = base32ToHex(secret) || '' + opt = opt || {} + var counter = opt.counter || 0 - var hexCounter = leftpad(decToHex(counter), 16, '0'); - var digest = await this.createHmac('SHA-1', key, hexCounter); - var h = hexToBytes(digest); + var hexCounter = leftpad(decToHex(counter), 16, '0') + var digest = await this.createHmac('SHA-1', key, hexCounter) + var h = hexToBytes(digest) // Truncate - var offset = h[h.length - 1] & 0xf; + var offset = h[h.length - 1] & 0xf var v = ((h[offset] & 0x7f) << 24) | ((h[offset + 1] & 0xff) << 16) | ((h[offset + 2] & 0xff) << 8) | - (h[offset + 3] & 0xff); + (h[offset + 3] & 0xff) - v = (v % 1000000) + ''; + v = (v % 1000000) + '' - return Array(7 - v.length).join('0') + v; + return Array(7 - v.length).join('0') + v } /** @@ -76,23 +69,23 @@ class Hotp { * */ async verify(token, key, opt) { - opt = opt || {}; - var window = opt.window || 50; - var counter = opt.counter || 0; + opt = opt || {} + var window = opt.window || 50 + var counter = opt.counter || 0 // Now loop through from C to C + W to determine if there is // a correct code for (var i = counter - window; i <= counter + window; ++i) { - opt.counter = i; + opt.counter = i if ((await this.gen(key, opt)) === token) { // We have found a matching code, trigger callback // and pass offset - return { delta: i - counter }; + return { delta: i - counter } } } // If we get to here then no codes have matched, return null - return null; + return null } async createHmac(alg, key, str) { @@ -102,17 +95,17 @@ class Hotp { { // algorithm details name: 'HMAC', - hash: { name: alg } + hash: { name: alg }, }, false, // export = false - ['sign'] // what this key can do - ); - const sig = await window.crypto.subtle.sign('HMAC', hmacKey, hextoBuf(str)); - return bufToHex(sig); + ['sign'], // what this key can do + ) + const sig = await window.crypto.subtle.sign('HMAC', hmacKey, hextoBuf(str)) + return bufToHex(sig) } } -export const hotp = new Hotp(); +export const hotp = new Hotp() class Totp { /** @@ -133,15 +126,15 @@ class Totp { * */ async gen(key, opt) { - opt = opt || {}; - var time = opt.time || 30; - var _t = Date.now(); + opt = opt || {} + var time = opt.time || 30 + var _t = Date.now() // Determine the value of the counter, C // This is the number of time steps in seconds since T0 - opt.counter = Math.floor(_t / 1000 / time); + opt.counter = Math.floor(_t / 1000 / time) - return hotp.gen(key, opt); + return hotp.gen(key, opt) } /** @@ -176,16 +169,16 @@ class Totp { * */ async verify(token, key, opt) { - opt = opt || {}; - var time = opt.time || 30; - var _t = Date.now(); + opt = opt || {} + var time = opt.time || 30 + var _t = Date.now() // Determine the value of the counter, C // This is the number of time steps in seconds since T0 - opt.counter = Math.floor(_t / 1000 / time); + opt.counter = Math.floor(_t / 1000 / time) - return hotp.verify(token, key, opt); + return hotp.verify(token, key, opt) } } -export const totp = new Totp(); +export const totp = new Totp() diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/lib/utils.js b/packages/components/src/packages/org.standardnotes.token-vault/app/lib/utils.js index 60f30b25e..0437cf530 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/lib/utils.js +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/lib/utils.js @@ -1,58 +1,56 @@ -const base32chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; -export const secretPattern = `^[${base32chars}]{16,}$`; +const base32chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' +export const secretPattern = `^[${base32chars}]{16,}$` export function hexToBytes(hex) { - var bytes = []; + var bytes = [] for (var c = 0, C = hex.length; c < C; c += 2) { - bytes.push(parseInt(hex.substr(c, 2), 16)); + bytes.push(parseInt(hex.substr(c, 2), 16)) } - return bytes; + return bytes } export function decToHex(s) { - return (s < 15.5 ? '0' : '') + Math.round(s).toString(16); + return (s < 15.5 ? '0' : '') + Math.round(s).toString(16) } export function bufToHex(buf) { - return Array.prototype.map - .call(new Uint8Array(buf), x => ('00' + x.toString(16)).slice(-2)) - .join(''); + return Array.prototype.map.call(new Uint8Array(buf), (x) => ('00' + x.toString(16)).slice(-2)).join('') } export function hextoBuf(hex) { - var view = new Uint8Array(hex.length / 2); + var view = new Uint8Array(hex.length / 2) for (var i = 0; i < hex.length; i += 2) { - view[i / 2] = parseInt(hex.substring(i, i + 2), 16); + view[i / 2] = parseInt(hex.substring(i, i + 2), 16) } - return view.buffer; + return view.buffer } export function base32ToHex(base32) { - var bits, chunk, hex, i, val; - bits = ''; - hex = ''; - i = 0; + var bits, chunk, hex, i, val + bits = '' + hex = '' + i = 0 while (i < base32.length) { - val = base32chars.indexOf(base32.charAt(i).toUpperCase()); - bits += leftpad(val.toString(2), 5, '0'); - i++; + val = base32chars.indexOf(base32.charAt(i).toUpperCase()) + bits += leftpad(val.toString(2), 5, '0') + i++ } - i = 0; + i = 0 while (i + 4 <= bits.length) { - chunk = bits.substr(i, 4); - hex = hex + parseInt(chunk, 2).toString(16); - i += 4; + chunk = bits.substr(i, 4) + hex = hex + parseInt(chunk, 2).toString(16) + i += 4 } - return hex; + return hex } export function leftpad(str, len, pad) { if (len + 1 >= str.length) { - str = Array(len + 1 - str.length).join(pad) + str; + str = Array(len + 1 - str.length).join(pad) + str } - return str; + return str } /** @@ -76,45 +74,45 @@ export function leftpad(str, len, pad) { */ export function parseKeyUri(uri) { // Quick sanity check - if (typeof uri !== 'string' || uri.length < 7) return null; + if (typeof uri !== 'string' || uri.length < 7) return null // I would like to just use new URL(), but the behavior is different between node and browsers, so // we have to do some of the work manually with regex. - const parts = /otpauth:\/\/([A-Za-z]+)\/([^?]+)\??(.*)?/i.exec(uri); + const parts = /otpauth:\/\/([A-Za-z]+)\/([^?]+)\??(.*)?/i.exec(uri) if (!parts || parts.length < 3) { - return null; + return null } // eslint-disable-next-line no-unused-vars - const [fullUri, type, fullLabel] = parts; + const [fullUri, type, fullLabel] = parts // Sanity check type and label if (!type || !fullLabel) { - return null; + return null } // Parse the label - const decodedLabel = decodeURIComponent(fullLabel); + const decodedLabel = decodeURIComponent(fullLabel) - const labelParts = decodedLabel.split(/: ?/); + const labelParts = decodedLabel.split(/: ?/) const label = labelParts && labelParts.length === 2 ? { issuer: labelParts[0], account: labelParts[1] } - : { issuer: '', account: decodedLabel }; + : { issuer: '', account: decodedLabel } // Parse query string - const qs = parts[3] ? new URLSearchParams(parts[3]) : []; + const qs = parts[3] ? new URLSearchParams(parts[3]) : [] const query = [...qs].reduce((acc, [key, value]) => { - acc[key] = value; + acc[key] = value - return acc; - }, {}); + return acc + }, {}) // Returned the parsed parts of the URI - return { type: type.toLowerCase(), label, query }; + return { type: type.toLowerCase(), label, query } } /** @@ -122,19 +120,21 @@ export function parseKeyUri(uri) { */ export function hexColorToRGB(hexColor) { // Expand the shorthand form (e.g. "0AB") to full form (e.g. "00AABB") - const shortHandFormRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hexColor = hexColor.replace(shortHandFormRegex, function(m, red, green, blue) { - return red + red + green + green + blue + blue; - }); - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hexColor); - return result ? { - red: parseInt(result[1], 16), - green: parseInt(result[2], 16), - blue: parseInt(result[3], 16) - } : null; + const shortHandFormRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i + hexColor = hexColor.replace(shortHandFormRegex, function (m, red, green, blue) { + return red + red + green + green + blue + blue + }) + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hexColor) + return result + ? { + red: parseInt(result[1], 16), + green: parseInt(result[2], 16), + blue: parseInt(result[3], 16), + } + : null } -export const defaultBgColor = '#FFF'; +export const defaultBgColor = '#FFF' /** * Gets the color variable to be used based on the calculated constrast of a color. @@ -142,48 +142,44 @@ export const defaultBgColor = '#FFF'; export function getVarColorForContrast(backgroundColor) { const styleKitColors = { foreground: '--sn-stylekit-contrast-foreground-color', - background: '--sn-stylekit-contrast-background-color' - }; - if (!backgroundColor) { - return styleKitColors.foreground; + background: '--sn-stylekit-contrast-background-color', } - const colorContrast = Math.round(((parseInt(backgroundColor.red) * 299) + (parseInt(backgroundColor.green) * 587) + (parseInt(backgroundColor.blue) * 114)) / 1000); - return (colorContrast > 70) ? styleKitColors.background : styleKitColors.foreground; + if (!backgroundColor) { + return styleKitColors.foreground + } + const colorContrast = Math.round( + (parseInt(backgroundColor.red) * 299 + + parseInt(backgroundColor.green) * 587 + + parseInt(backgroundColor.blue) * 114) / + 1000, + ) + return colorContrast > 70 ? styleKitColors.background : styleKitColors.foreground } function getPropertyValue(document, propertyName) { - return getComputedStyle(document.documentElement) - .getPropertyValue(propertyName).trim().toUpperCase(); + return getComputedStyle(document.documentElement).getPropertyValue(propertyName).trim().toUpperCase() } -export const contextualColors = [ - 'info', - 'success', - 'neutral', - 'warning' -]; +export const contextualColors = ['info', 'success', 'neutral', 'warning'] export function getContextualColor(document, colorName) { if (!contextualColors.includes(colorName)) { - return; + return } - return getPropertyValue( - document, - `--sn-stylekit-${colorName}-color` - ); + return getPropertyValue(document, `--sn-stylekit-${colorName}-color`) } export function getEntryColor(document, entry) { - const { color } = entry; + const { color } = entry if (!contextualColors.includes(color)) { - return color; + return color } - return getContextualColor(document, color); + return getContextualColor(document, color) } export function getAllContextualColors(document) { - return contextualColors.map((colorName) => getContextualColor(document, colorName)); + return contextualColors.map((colorName) => getContextualColor(document, colorName)) } diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/main.js b/packages/components/src/packages/org.standardnotes.token-vault/app/main.js deleted file mode 100644 index 30c97bd94..000000000 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/main.js +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -ReactDOM.render( - , - document.body.appendChild(document.createElement('div')) -); diff --git a/packages/components/src/packages/org.standardnotes.token-vault/app/stylesheets/main.scss b/packages/components/src/packages/org.standardnotes.token-vault/app/stylesheets/main.scss index 71b301691..ddeff30fd 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/app/stylesheets/main.scss +++ b/packages/components/src/packages/org.standardnotes.token-vault/app/stylesheets/main.scss @@ -1,25 +1,24 @@ -@import '~stylekit'; +@import '~@standardnotes/styles/src/Styles/main.scss'; body, html { - font-family: var(--sn-stylekit-sans-serif-font); - height: 100%; - width: 100%; - margin: 0; - padding: 0; - font-size: var(--sn-stylekit-base-font-size); - background-color: transparent; + background-color: var(--sn-stylekit-contrast-background-color); + padding: 0 !important; } * { // To prevent gray flash when focusing input on mobile Safari -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + font-family: var(--sn-stylekit-sans-serif-font); } .sn-component { display: flex; flex-direction: column; - height: 100vh; + + @media screen and (max-width: 420px) { + min-height: -webkit-fill-available; + } .sk-panel-content { height: inherit !important; @@ -127,7 +126,6 @@ html { } } - /* entry default styles */ .auth-entry { display: flex; @@ -291,7 +289,7 @@ html { padding: 5px; background: var(--sn-stylekit-contrast-background-color); border-radius: 1px; - box-shadow: 0 0 0 1px rgba(0,0,0,.1); + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1); display: inline-block; cursor: pointer; } @@ -409,9 +407,37 @@ html { } // Show palette icon on the first 4 color rectangles. -div.twitter-picker > div:nth-child(3) > span:nth-child(-n+4) > div { +div.twitter-picker > div:nth-child(3) > span:nth-child(-n + 4) > div { background-image: url('../assets/svg/palette.svg') !important; background-repeat: no-repeat !important; background-position: top 4px right 4px !important; background-size: 12px 12px !important; } + +.grab-cursor { + cursor: grab; +} + +.left-header { + display: flex; + + @media screen and (max-width: 600px) { + flex-direction: column; + flex-wrap: wrap; + } + + .sk-input-group { + > * { + display: inline-block; + vertical-align: middle; + + &:not(:first-child) { + margin-left: 0 !important; + } + + &:not(:last-child) { + margin-right: 0.73125rem; + } + } + } +} diff --git a/packages/components/src/packages/org.standardnotes.token-vault/package.json b/packages/components/src/packages/org.standardnotes.token-vault/package.json index a48993dfb..341a406d4 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/package.json +++ b/packages/components/src/packages/org.standardnotes.token-vault/package.json @@ -7,46 +7,55 @@ "components:compile": "webpack --config webpack.prod.js", "start": "webpack serve --config webpack.dev.js --progress --hot", "skip:components:lint": "eslint app/ --ext .js", - "components:lint:fix": "yarn lint --fix" + "components:lint:fix": "eslint --fix", + "components:lint": "prettier --write 'app/**/*.{html,css,scss,js,jsx,ts,tsx,json}' README.md" }, "sn": { "main": "dist/index.html" }, + "lint-staged": { + "README.md": [ + "prettier --write" + ], + "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [ + "prettier --write" + ] + }, "devDependencies": { - "@babel/core": "^7.13.10", - "@babel/eslint-parser": "^7.13.10", - "@babel/plugin-proposal-class-properties": "^7.13.0", - "@babel/plugin-transform-runtime": "^7.13.10", - "@babel/preset-env": "^7.13.10", - "@babel/preset-react": "^7.12.13", - "@standardnotes/editor-kit": "2.2.1", - "@standardnotes/eslint-config-extensions": "^1.0.1", - "@svgr/webpack": "^6.1.2", - "babel-loader": "^8.2.2", - "css-loader": "^5.1.3", - "eslint": "^7.21.0", - "eslint-plugin-react": "^7.22.0", - "html-webpack-plugin": "^5.3.1", - "immutability-helper": "^3.0.1", - "jsqr": "^1.2.0", - "mini-css-extract-plugin": "^1.3.9", + "@babel/core": "^7.18.5", + "@babel/eslint-parser": "^7.18.2", + "@babel/plugin-proposal-class-properties": "^7.17.12", + "@babel/plugin-transform-runtime": "^7.18.5", + "@babel/preset-env": "^7.18.2", + "@babel/preset-react": "^7.17.12", + "@standardnotes/editor-kit": "2.2.5", + "@standardnotes/eslint-config-extensions": "^1.0.4", + "@standardnotes/styles": "workspace:*", + "@svgr/webpack": "^6.2.1", + "babel-loader": "^8.2.5", + "css-loader": "^6.7.1", + "eslint": "*", + "eslint-plugin-react": "^7.30.0", + "html-webpack-plugin": "^5.5.0", + "immutability-helper": "^3.1.1", + "jsqr": "^1.4.0", + "mini-css-extract-plugin": "^2.6.1", "node-sass": "*", "notp": "^2.0.3", - "otplib": "^11.0.1", - "prop-types": "^15.7.2", - "react": "^17.0.1", + "otplib": "^12.0.1", + "prettier": "*", + "prop-types": "^15.8.1", + "react": "^18.2.0", "react-beautiful-dnd": "^13.1.0", "react-color": "^2.19.3", - "react-dom": "^17.0.1", - "regenerator-runtime": "^0.13.2", - "sass-loader": "^11.0.1", - "sn-stylekit": "2.1.0", - "style-loader": "~0.13.1", + "react-dom": "^18.2.0", + "sass-loader": "^13.0.0", + "style-loader": "~3.3.1", "svg-url-loader": "^7.1.1", - "terser-webpack-plugin": "^5.1.1", + "terser-webpack-plugin": "^5.3.3", "webpack": "*", "webpack-cli": "*", "webpack-dev-server": "*", - "webpack-merge": "^5.7.3" + "webpack-merge": "^5.8.0" } } diff --git a/packages/components/src/packages/org.standardnotes.token-vault/webpack.config.js b/packages/components/src/packages/org.standardnotes.token-vault/webpack.config.js index d6dc80e4e..de0a0ce55 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/webpack.config.js +++ b/packages/components/src/packages/org.standardnotes.token-vault/webpack.config.js @@ -1,11 +1,11 @@ -const path = require('path'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); +const path = require('path') +const MiniCssExtractPlugin = require('mini-css-extract-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { context: __dirname, entry: [ - path.resolve(__dirname, 'app/main.js'), + path.resolve(__dirname, 'app/index.js'), path.resolve(__dirname, 'app/stylesheets/main.scss') ], output: { @@ -30,11 +30,8 @@ module.exports = { }, { test: /\.js[x]?$/, - include: [ - path.resolve(__dirname, 'app') - ], exclude: /node_modules/, - use: ['babel-loader'] + use: ['babel-loader'], }, { test: /\.svg$/i, @@ -60,7 +57,6 @@ module.exports = { resolve: { extensions: ['.js', '.jsx'], alias: { - stylekit: require.resolve('sn-stylekit/dist/stylekit.css'), '@Components': path.resolve(__dirname, 'app/components'), '@Lib': path.resolve(__dirname, 'app/lib') } @@ -75,4 +71,4 @@ module.exports = { filename: 'index.html' }) ] -}; +} diff --git a/packages/components/src/packages/org.standardnotes.token-vault/webpack.dev.js b/packages/components/src/packages/org.standardnotes.token-vault/webpack.dev.js index 786c7d1df..6a7e2eea9 100644 --- a/packages/components/src/packages/org.standardnotes.token-vault/webpack.dev.js +++ b/packages/components/src/packages/org.standardnotes.token-vault/webpack.dev.js @@ -7,14 +7,17 @@ module.exports = merge(config, { devtool: 'cheap-source-map', devServer: { port: 8001, - contentBase: path.resolve(__dirname, 'dist'), - disableHostCheck: true, + static: path.resolve(__dirname, 'dist'), + allowedHosts: "all", historyApiFallback: true, - watchOptions: { aggregateTimeout: 300, poll: 1000 }, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS', 'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization' } }, + watchOptions: { + aggregateTimeout: 300, + poll: 1000 + }, }); diff --git a/packages/styles/src/Styles/utils/_padding.scss b/packages/styles/src/Styles/utils/_padding.scss index ff0c0c486..877fe2503 100644 --- a/packages/styles/src/Styles/utils/_padding.scss +++ b/packages/styles/src/Styles/utils/_padding.scss @@ -94,6 +94,10 @@ padding-right: 0.5rem; } +.pr-4 { + padding-right: 1rem; +} + .pl-1 { padding-left: 0.25rem; } diff --git a/yarn.lock b/yarn.lock index af6a195b6..5e00bff3d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -87,7 +87,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.1.0, @babel/core@npm:^7.1.6, @babel/core@npm:^7.11.1, @babel/core@npm:^7.12.10, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.10, @babel/core@npm:^7.13.13, @babel/core@npm:^7.13.14, @babel/core@npm:^7.13.8, @babel/core@npm:^7.14.0, @babel/core@npm:^7.14.6, @babel/core@npm:^7.15.5, @babel/core@npm:^7.16.0, @babel/core@npm:^7.17.10, @babel/core@npm:^7.17.9, @babel/core@npm:^7.18.2, @babel/core@npm:^7.7.2, @babel/core@npm:^7.7.7, @babel/core@npm:^7.8.0": +"@babel/core@npm:^7.1.0, @babel/core@npm:^7.1.6, @babel/core@npm:^7.11.1, @babel/core@npm:^7.12.10, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.10, @babel/core@npm:^7.13.13, @babel/core@npm:^7.13.14, @babel/core@npm:^7.13.8, @babel/core@npm:^7.14.0, @babel/core@npm:^7.14.6, @babel/core@npm:^7.15.5, @babel/core@npm:^7.16.0, @babel/core@npm:^7.17.10, @babel/core@npm:^7.17.9, @babel/core@npm:^7.18.2, @babel/core@npm:^7.18.5, @babel/core@npm:^7.7.2, @babel/core@npm:^7.7.7, @babel/core@npm:^7.8.0": version: 7.18.5 resolution: "@babel/core@npm:7.18.5" dependencies: @@ -110,7 +110,7 @@ __metadata: languageName: node linkType: hard -"@babel/eslint-parser@npm:^7.13.10, @babel/eslint-parser@npm:^7.13.14, @babel/eslint-parser@npm:^7.13.4, @babel/eslint-parser@npm:^7.13.8, @babel/eslint-parser@npm:^7.14.7, @babel/eslint-parser@npm:^7.16.3": +"@babel/eslint-parser@npm:^7.13.10, @babel/eslint-parser@npm:^7.13.14, @babel/eslint-parser@npm:^7.13.4, @babel/eslint-parser@npm:^7.13.8, @babel/eslint-parser@npm:^7.14.7, @babel/eslint-parser@npm:^7.16.3, @babel/eslint-parser@npm:^7.18.2": version: 7.18.2 resolution: "@babel/eslint-parser@npm:7.18.2" dependencies: @@ -1296,7 +1296,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-runtime@npm:^7.0.0, @babel/plugin-transform-runtime@npm:^7.10.1, @babel/plugin-transform-runtime@npm:^7.11.0, @babel/plugin-transform-runtime@npm:^7.13.0, @babel/plugin-transform-runtime@npm:^7.13.10, @babel/plugin-transform-runtime@npm:^7.16.4, @babel/plugin-transform-runtime@npm:^7.18.2": +"@babel/plugin-transform-runtime@npm:^7.0.0, @babel/plugin-transform-runtime@npm:^7.10.1, @babel/plugin-transform-runtime@npm:^7.11.0, @babel/plugin-transform-runtime@npm:^7.13.0, @babel/plugin-transform-runtime@npm:^7.16.4, @babel/plugin-transform-runtime@npm:^7.18.2, @babel/plugin-transform-runtime@npm:^7.18.5": version: 7.18.5 resolution: "@babel/plugin-transform-runtime@npm:7.18.5" dependencies: @@ -1517,7 +1517,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-react@npm:^7.10.1, @babel/preset-react@npm:^7.10.4, @babel/preset-react@npm:^7.12.12, @babel/preset-react@npm:^7.12.13, @babel/preset-react@npm:^7.12.5, @babel/preset-react@npm:^7.13.13, @babel/preset-react@npm:^7.14.5, @babel/preset-react@npm:^7.16.0": +"@babel/preset-react@npm:^7.10.1, @babel/preset-react@npm:^7.10.4, @babel/preset-react@npm:^7.12.12, @babel/preset-react@npm:^7.12.13, @babel/preset-react@npm:^7.12.5, @babel/preset-react@npm:^7.13.13, @babel/preset-react@npm:^7.14.5, @babel/preset-react@npm:^7.16.0, @babel/preset-react@npm:^7.17.12": version: 7.17.12 resolution: "@babel/preset-react@npm:7.17.12" dependencies: @@ -3905,6 +3905,54 @@ __metadata: languageName: node linkType: hard +"@otplib/core@npm:^12.0.1": + version: 12.0.1 + resolution: "@otplib/core@npm:12.0.1" + checksum: b3c34bc20b31bc3f49cc0dc3c0eb070491c0101e8c1efa83cec48ca94158bd736aaca8187df667fc0c4a239d4ac52076bc44084bee04a50c80c3630caf77affa + languageName: node + linkType: hard + +"@otplib/plugin-crypto@npm:^12.0.1": + version: 12.0.1 + resolution: "@otplib/plugin-crypto@npm:12.0.1" + dependencies: + "@otplib/core": ^12.0.1 + checksum: 6867c74ee8aca6c2db9670362cf51e44f3648602c39318bf537421242e33f0012a172acd43bbed9a21d706e535dc4c66aff965380673391e9fd74cf685b5b13a + languageName: node + linkType: hard + +"@otplib/plugin-thirty-two@npm:^12.0.1": + version: 12.0.1 + resolution: "@otplib/plugin-thirty-two@npm:12.0.1" + dependencies: + "@otplib/core": ^12.0.1 + thirty-two: ^1.0.2 + checksum: 920099e40d3e8c2941291c84c70064c2d86d0d1ed17230d650445d5463340e406bc413ddf2e40c374ddc4ee988ef1e3facacab9b5248b1ff361fd13df52bf88f + languageName: node + linkType: hard + +"@otplib/preset-default@npm:^12.0.1": + version: 12.0.1 + resolution: "@otplib/preset-default@npm:12.0.1" + dependencies: + "@otplib/core": ^12.0.1 + "@otplib/plugin-crypto": ^12.0.1 + "@otplib/plugin-thirty-two": ^12.0.1 + checksum: 8133231384f6277f77eb8e42ef83bc32a8b01059bef147d1c358d9e9bfd292e1c239f581fe008367a48489dd68952b7ac0948e6c41412fc06079da2c91b71d16 + languageName: node + linkType: hard + +"@otplib/preset-v11@npm:^12.0.1": + version: 12.0.1 + resolution: "@otplib/preset-v11@npm:12.0.1" + dependencies: + "@otplib/core": ^12.0.1 + "@otplib/plugin-crypto": ^12.0.1 + "@otplib/plugin-thirty-two": ^12.0.1 + checksum: 367cb09397e617c21ec748d54e920ab43f1c5dfba70cbfd88edf73aecca399cf0c09fefe32518f79c7ee8a06e7058d14b200da378cc7d46af3cac4e22a153e2f + languageName: node + linkType: hard + "@pmmmwh/react-refresh-webpack-plugin@npm:^0.5.3": version: 0.5.7 resolution: "@pmmmwh/react-refresh-webpack-plugin@npm:0.5.7" @@ -4949,41 +4997,41 @@ __metadata: version: 0.0.0-use.local resolution: "@standardnotes/authenticator@workspace:packages/components/src/packages/org.standardnotes.token-vault" dependencies: - "@babel/core": ^7.13.10 - "@babel/eslint-parser": ^7.13.10 - "@babel/plugin-proposal-class-properties": ^7.13.0 - "@babel/plugin-transform-runtime": ^7.13.10 - "@babel/preset-env": ^7.13.10 - "@babel/preset-react": ^7.12.13 - "@standardnotes/editor-kit": 2.2.1 - "@standardnotes/eslint-config-extensions": ^1.0.1 - "@svgr/webpack": ^6.1.2 - babel-loader: ^8.2.2 - css-loader: ^5.1.3 - eslint: ^7.21.0 - eslint-plugin-react: ^7.22.0 - html-webpack-plugin: ^5.3.1 - immutability-helper: ^3.0.1 - jsqr: ^1.2.0 - mini-css-extract-plugin: ^1.3.9 + "@babel/core": ^7.18.5 + "@babel/eslint-parser": ^7.18.2 + "@babel/plugin-proposal-class-properties": ^7.17.12 + "@babel/plugin-transform-runtime": ^7.18.5 + "@babel/preset-env": ^7.18.2 + "@babel/preset-react": ^7.17.12 + "@standardnotes/editor-kit": 2.2.5 + "@standardnotes/eslint-config-extensions": ^1.0.4 + "@standardnotes/styles": "workspace:*" + "@svgr/webpack": ^6.2.1 + babel-loader: ^8.2.5 + css-loader: ^6.7.1 + eslint: "*" + eslint-plugin-react: ^7.30.0 + html-webpack-plugin: ^5.5.0 + immutability-helper: ^3.1.1 + jsqr: ^1.4.0 + mini-css-extract-plugin: ^2.6.1 node-sass: "*" notp: ^2.0.3 - otplib: ^11.0.1 - prop-types: ^15.7.2 - react: ^17.0.1 + otplib: ^12.0.1 + prettier: "*" + prop-types: ^15.8.1 + react: ^18.2.0 react-beautiful-dnd: ^13.1.0 react-color: ^2.19.3 - react-dom: ^17.0.1 - regenerator-runtime: ^0.13.2 - sass-loader: ^11.0.1 - sn-stylekit: 2.1.0 - style-loader: ~0.13.1 + react-dom: ^18.2.0 + sass-loader: ^13.0.0 + style-loader: ~3.3.1 svg-url-loader: ^7.1.1 - terser-webpack-plugin: ^5.1.1 + terser-webpack-plugin: ^5.3.3 webpack: "*" webpack-cli: "*" webpack-dev-server: "*" - webpack-merge: ^5.7.3 + webpack-merge: ^5.8.0 languageName: unknown linkType: soft @@ -5178,13 +5226,6 @@ __metadata: languageName: unknown linkType: soft -"@standardnotes/editor-kit@npm:2.2.1": - version: 2.2.1 - resolution: "@standardnotes/editor-kit@npm:2.2.1" - checksum: d856714b8d7c1b7704d1644fc30edab3536b3e1f0f277a009f2b2381e7df07f827e5a68c764b58192d26ea5385e71379c410e5509d71570b317cda04d56c5010 - languageName: node - linkType: hard - "@standardnotes/editor-kit@npm:2.2.3": version: 2.2.3 resolution: "@standardnotes/editor-kit@npm:2.2.3" @@ -5256,7 +5297,7 @@ __metadata: languageName: node linkType: hard -"@standardnotes/eslint-config-extensions@npm:^1.0.0, @standardnotes/eslint-config-extensions@npm:^1.0.1, @standardnotes/eslint-config-extensions@npm:^1.0.2, @standardnotes/eslint-config-extensions@npm:^1.0.4": +"@standardnotes/eslint-config-extensions@npm:^1.0.0, @standardnotes/eslint-config-extensions@npm:^1.0.2, @standardnotes/eslint-config-extensions@npm:^1.0.4": version: 1.0.4 resolution: "@standardnotes/eslint-config-extensions@npm:1.0.4" peerDependencies: @@ -6373,7 +6414,7 @@ __metadata: languageName: node linkType: hard -"@svgr/webpack@npm:^6.1.2, @svgr/webpack@npm:^6.2.1": +"@svgr/webpack@npm:^6.2.1": version: 6.2.1 resolution: "@svgr/webpack@npm:6.2.1" dependencies: @@ -14789,7 +14830,7 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react@npm:*, eslint-plugin-react@npm:^7.22.0, eslint-plugin-react@npm:^7.23.1, eslint-plugin-react@npm:^7.24.0, eslint-plugin-react@npm:^7.26.1, eslint-plugin-react@npm:^7.27.1, eslint-plugin-react@npm:^7.29.4": +"eslint-plugin-react@npm:*, eslint-plugin-react@npm:^7.22.0, eslint-plugin-react@npm:^7.23.1, eslint-plugin-react@npm:^7.24.0, eslint-plugin-react@npm:^7.26.1, eslint-plugin-react@npm:^7.27.1, eslint-plugin-react@npm:^7.29.4, eslint-plugin-react@npm:^7.30.0": version: 7.30.0 resolution: "eslint-plugin-react@npm:7.30.0" dependencies: @@ -14953,7 +14994,7 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^7.20.0, eslint@npm:^7.21.0": +"eslint@npm:^7.20.0": version: 7.32.0 resolution: "eslint@npm:7.32.0" dependencies: @@ -17851,7 +17892,7 @@ __metadata: languageName: node linkType: hard -"immutability-helper@npm:^3.0.1": +"immutability-helper@npm:^3.1.1": version: 3.1.1 resolution: "immutability-helper@npm:3.1.1" checksum: 6fdbf6d2123efa567263e904bbaff07aca0e24560d270d34967b03aab8ec20bd3e4057f394d59e50eb6c4718c9415591a6281692bb0aafd522ad72cf4887133f @@ -20063,7 +20104,7 @@ __metadata: languageName: node linkType: hard -"jsqr@npm:^1.2.0": +"jsqr@npm:^1.4.0": version: 1.4.0 resolution: "jsqr@npm:1.4.0" checksum: 7c572971f90c42772e30d152bde63b84edf1164bde80c53942e6b2068ea31caf00ad704aa46cacc9e71645f52dbeddebc6e84ba15e883c678ee93cde690de339 @@ -20530,7 +20571,7 @@ __metadata: languageName: node linkType: hard -"loader-utils@npm:^1.0.2, loader-utils@npm:^1.1.0": +"loader-utils@npm:^1.1.0": version: 1.4.0 resolution: "loader-utils@npm:1.4.0" dependencies: @@ -22349,7 +22390,7 @@ __metadata: languageName: node linkType: hard -"mini-css-extract-plugin@npm:^2.0.0, mini-css-extract-plugin@npm:^2.4.5, mini-css-extract-plugin@npm:^2.5.3, mini-css-extract-plugin@npm:^2.6.0": +"mini-css-extract-plugin@npm:^2.0.0, mini-css-extract-plugin@npm:^2.4.5, mini-css-extract-plugin@npm:^2.5.3, mini-css-extract-plugin@npm:^2.6.0, mini-css-extract-plugin@npm:^2.6.1": version: 2.6.1 resolution: "mini-css-extract-plugin@npm:2.6.1" dependencies: @@ -23699,12 +23740,14 @@ __metadata: languageName: node linkType: hard -"otplib@npm:^11.0.1": - version: 11.0.1 - resolution: "otplib@npm:11.0.1" +"otplib@npm:^12.0.1": + version: 12.0.1 + resolution: "otplib@npm:12.0.1" dependencies: - thirty-two: 1.0.2 - checksum: 42225f1ccc4562fc062dfd0cbe4b0c527f56648775601175b638e54850c44a1dbe1770e5858a2e50216e5111bd4dd2776df3372a92f74a2fb41e7f3975dc0bbd + "@otplib/core": ^12.0.1 + "@otplib/preset-default": ^12.0.1 + "@otplib/preset-v11": ^12.0.1 + checksum: 4a1b91cf1b8e920b50ad4bac2ef2a89126630c62daf68e9b32ff15106b2551db905d3b979955cf5f8f114da0a8883cec3d636901d65e793c1745bb4174e2a572 languageName: node linkType: hard @@ -26285,7 +26328,7 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:^18.1.0": +"react-dom@npm:^18.1.0, react-dom@npm:^18.2.0": version: 18.2.0 resolution: "react-dom@npm:18.2.0" dependencies: @@ -27075,7 +27118,7 @@ __metadata: languageName: node linkType: hard -"react@npm:^18.1.0": +"react@npm:^18.1.0, react@npm:^18.2.0": version: 18.2.0 resolution: "react@npm:18.2.0" dependencies: @@ -28232,6 +28275,31 @@ __metadata: languageName: node linkType: hard +"sass-loader@npm:^13.0.0": + version: 13.0.0 + resolution: "sass-loader@npm:13.0.0" + dependencies: + klona: ^2.0.4 + neo-async: ^2.6.2 + peerDependencies: + fibers: ">= 3.1.0" + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + sass: ^1.3.0 + sass-embedded: "*" + webpack: ^5.0.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + sass-embedded: + optional: true + checksum: f7af03813dccf0405eb02917cd10c97571ab81f42e9ea1f3da6d9e96991e076521809a452ad319d57c1a63273ce07c23ddfdbda5cd071a56d261dc28913afdaa + languageName: node + linkType: hard + "sass-loader@npm:^9.0.3": version: 9.0.3 resolution: "sass-loader@npm:9.0.3" @@ -29798,15 +29866,6 @@ __metadata: languageName: node linkType: hard -"style-loader@npm:~0.13.1": - version: 0.13.2 - resolution: "style-loader@npm:0.13.2" - dependencies: - loader-utils: ^1.0.2 - checksum: 68bdfbf4e759abf6e5195880966ac9407b758ca9f1fd96dc2584554707d25d8dbbe1a9d6524a617513e7d28d9f51bdd754689c1e6ae12ac1c4ff62781f5e7ccc - languageName: node - linkType: hard - "style-loader@npm:~1.2.1": version: 1.2.1 resolution: "style-loader@npm:1.2.1" @@ -30276,7 +30335,7 @@ __metadata: languageName: node linkType: hard -"terser-webpack-plugin@npm:^5.1.1, terser-webpack-plugin@npm:^5.1.3, terser-webpack-plugin@npm:^5.1.4, terser-webpack-plugin@npm:^5.2.5, terser-webpack-plugin@npm:^5.3.1": +"terser-webpack-plugin@npm:^5.1.1, terser-webpack-plugin@npm:^5.1.3, terser-webpack-plugin@npm:^5.1.4, terser-webpack-plugin@npm:^5.2.5, terser-webpack-plugin@npm:^5.3.1, terser-webpack-plugin@npm:^5.3.3": version: 5.3.3 resolution: "terser-webpack-plugin@npm:5.3.3" dependencies: @@ -30337,7 +30396,7 @@ __metadata: languageName: node linkType: hard -"thirty-two@npm:1.0.2": +"thirty-two@npm:^1.0.2": version: 1.0.2 resolution: "thirty-two@npm:1.0.2" checksum: f6700b31d16ef942fdc0d14daed8a2f69ea8b60b0e85db8b83adf58d84bbeafe95a17d343ab55efaae571bb5148b62fc0ee12b04781323bf7af7d7e9693eec76