From 0dd53811522973f1960efe37fe6a9ce84bc35492 Mon Sep 17 00:00:00 2001 From: Vynnyk Dmytro Date: Tue, 2 Jul 2024 22:06:09 +0300 Subject: [PATCH] Release/1.11.0 (#1023) * added listener for window closing (#752) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fix displaying amount in CSPR only for Casper (#753) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * UI changed based on a new design (#754) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fixed skeleton for NFT image and made small changes (#755) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * feature: add remembering scroll position in the NFT, Deploys and Tokens list (#759) * added remembering scroll position in the NFT list and removed background fetch for nft * added remembering scroll position in the NFT list and removed background fetch for nft * added remembering scroll position in the Deploys list, removed background fetch for deploys and made some improvements * small improvements * added remembering scroll position in the Casper token activity list and small improvements * added remembering scroll position in the ERC20 token activity list and small improvements * removed duplication and small fix * fixed infinity scroll for NFT * fixed infinity scroll for Deploys tab * fixed infinity scroll for Casper token activity * fixed infinity scroll for ERC20 token activity * unused infinite scroll hook removed * updated error handler for account deploys and NFT tokens request --------- Co-authored-by: ost-ptk * illustrations are updated (#762) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fix: fix native transfer in Firefox (#763) * added a check for deploy hash before the extended deploys info request * updated manifest permission for Firefox * removed comment --------- Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * added tooltip on hover hash (#764) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * Redesign list of NFTs (#766) * Redesign list of NFTs * Fix PR issues and add useAsyncEffect hook * Fix issue with audio loading state * Fix issue with deploy timestamp (#768) * Release 1.5.2 version (#777) * feature: add NFT token transfer (#769) * added UI for NFT token transfer flow and business logic draft * added nft transfer logic * added fix for deploy timestamp issue * removed unused packages * removed back button from success screen --------- Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fix issues with the layout of a deploy list (#772) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * build(deps): bump sqlite3 from 5.0.8 to 5.1.6 (#694) Bumps [sqlite3](https://github.com/TryGhost/node-sqlite3) from 5.0.8 to 5.1.6. - [Release notes](https://github.com/TryGhost/node-sqlite3/releases) - [Commits](https://github.com/TryGhost/node-sqlite3/compare/v5.0.8...v5.1.6) --- updated-dependencies: - dependency-name: sqlite3 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump json5 from 1.0.1 to 1.0.2 (#695) Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump dns-packet from 5.3.1 to 5.6.0 (#696) Bumps [dns-packet](https://github.com/mafintosh/dns-packet) from 5.3.1 to 5.6.0. - [Changelog](https://github.com/mafintosh/dns-packet/blob/master/CHANGELOG.md) - [Commits](https://github.com/mafintosh/dns-packet/compare/v5.3.1...v5.6.0) --- updated-dependencies: - dependency-name: dns-packet dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump xml2js and web-ext (#697) Bumps [xml2js](https://github.com/Leonidas-from-XIV/node-xml2js) to 0.5.0 and updates ancestor dependency [web-ext](https://github.com/mozilla/web-ext). These dependencies need to be updated together. Updates `xml2js` from 0.4.23 to 0.5.0 - [Commits](https://github.com/Leonidas-from-XIV/node-xml2js/commits/0.5.0) Updates `web-ext` from 7.5.0 to 7.6.2 - [Release notes](https://github.com/mozilla/web-ext/releases) - [Commits](https://github.com/mozilla/web-ext/compare/7.5.0...7.6.2) --- updated-dependencies: - dependency-name: xml2js dependency-type: indirect - dependency-name: web-ext dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dmytro Vynnyk * build(deps-dev): bump eslint-plugin-jsx-a11y from 6.6.1 to 6.7.1 (#703) Bumps [eslint-plugin-jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y) from 6.6.1 to 6.7.1. - [Release notes](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/releases) - [Changelog](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/CHANGELOG.md) - [Commits](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/compare/v6.6.1...v6.7.1) --- updated-dependencies: - dependency-name: eslint-plugin-jsx-a11y dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * build(deps-dev): bump fs-extra from 11.1.0 to 11.1.1 (#705) Bumps [fs-extra](https://github.com/jprichardson/node-fs-extra) from 11.1.0 to 11.1.1. - [Changelog](https://github.com/jprichardson/node-fs-extra/blob/master/CHANGELOG.md) - [Commits](https://github.com/jprichardson/node-fs-extra/compare/11.1.0...11.1.1) --- updated-dependencies: - dependency-name: fs-extra dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump i18next-conv from 13.1.0 to 14.0.0 (#706) Bumps [i18next-conv](https://github.com/i18next/i18next-gettext-converter) from 13.1.0 to 14.0.0. - [Release notes](https://github.com/i18next/i18next-gettext-converter/releases) - [Changelog](https://github.com/i18next/i18next-gettext-converter/blob/master/CHANGELOG.md) - [Commits](https://github.com/i18next/i18next-gettext-converter/compare/v13.1.0...v14.0.0) --- updated-dependencies: - dependency-name: i18next-conv dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * build(deps): bump word-wrap from 1.2.3 to 1.2.4 (#731) Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4. - [Release notes](https://github.com/jonschlinkert/word-wrap/releases) - [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4) --- updated-dependencies: - dependency-name: word-wrap dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump apollo-server-core from 3.11.1 to 3.12.1 (#765) Bumps [apollo-server-core](https://github.com/apollographql/apollo-server/tree/HEAD/packages/apollo-server-core) from 3.11.1 to 3.12.1. - [Release notes](https://github.com/apollographql/apollo-server/releases) - [Commits](https://github.com/apollographql/apollo-server/commits/apollo-server-core@3.12.1/packages/apollo-server-core) --- updated-dependencies: - dependency-name: apollo-server-core dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump i18next from 21.10.0 to 23.5.1 (#773) Bumps [i18next](https://github.com/i18next/i18next) from 21.10.0 to 23.5.1. - [Release notes](https://github.com/i18next/i18next/releases) - [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md) - [Commits](https://github.com/i18next/i18next/compare/v21.10.0...v23.5.1) --- updated-dependencies: - dependency-name: i18next dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * fix account status indicator (#775) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * build(deps-dev): bump eslint from 8.28.0 to 8.49.0 (#776) Bumps [eslint](https://github.com/eslint/eslint) from 8.28.0 to 8.49.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.28.0...v8.49.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * build(deps): bump got and @redux-devtools/cli (#780) Bumps [got](https://github.com/sindresorhus/got) to 11.8.6 and updates ancestor dependency [@redux-devtools/cli](https://github.com/reduxjs/redux-devtools). These dependencies need to be updated together. Updates `got` from 9.6.0 to 11.8.6 - [Release notes](https://github.com/sindresorhus/got/releases) - [Commits](https://github.com/sindresorhus/got/compare/v9.6.0...v11.8.6) Updates `@redux-devtools/cli` from 1.0.7 to 3.0.1 - [Release notes](https://github.com/reduxjs/redux-devtools/releases) - [Commits](https://github.com/reduxjs/redux-devtools/compare/@redux-devtools/cli@1.0.7...v3.0.1) --- updated-dependencies: - dependency-name: got dependency-type: indirect - dependency-name: "@redux-devtools/cli" dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * build(deps): bump react-player from 2.12.0 to 2.13.0 (#785) Bumps [react-player](https://github.com/CookPete/react-player) from 2.12.0 to 2.13.0. - [Changelog](https://github.com/cookpete/react-player/blob/master/CHANGELOG.md) - [Commits](https://github.com/CookPete/react-player/compare/v2.12.0...v2.13.0) --- updated-dependencies: - dependency-name: react-player dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump prettier from 2.8.0 to 3.0.3 (#786) Bumps [prettier](https://github.com/prettier/prettier) from 2.8.0 to 3.0.3. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/2.8.0...3.0.3) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump redux from 4.2.0 to 4.2.1 (#787) Bumps [redux](https://github.com/reduxjs/redux) from 4.2.0 to 4.2.1. - [Release notes](https://github.com/reduxjs/redux/releases) - [Changelog](https://github.com/reduxjs/redux/blob/master/CHANGELOG.md) - [Commits](https://github.com/reduxjs/redux/compare/v4.2.0...v4.2.1) --- updated-dependencies: - dependency-name: redux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * build(deps-dev): bump webpack-dev-server from 4.11.1 to 4.15.1 (#788) Bumps [webpack-dev-server](https://github.com/webpack/webpack-dev-server) from 4.11.1 to 4.15.1. - [Release notes](https://github.com/webpack/webpack-dev-server/releases) - [Changelog](https://github.com/webpack/webpack-dev-server/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-server/compare/v4.11.1...v4.15.1) --- updated-dependencies: - dependency-name: webpack-dev-server dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump @types/chrome from 0.0.203 to 0.0.246 (#791) Bumps [@types/chrome](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/chrome) from 0.0.203 to 0.0.246. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/chrome) --- updated-dependencies: - dependency-name: "@types/chrome" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * added message for not supporting reverse look-up modality (#783) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * added a placeholder for the contract logo (#784) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fix: fix issue with prettier config (#792) * fixed issue with prettier config * fixed code style errors --------- Co-authored-by: ost-ptk * added new UI for erc-20 action displaying (#790) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * build(deps-dev): bump geckodriver from 3.2.0 to 4.2.1 (#793) Bumps [geckodriver](https://github.com/webdriverio-community/node-geckodriver) from 3.2.0 to 4.2.1. - [Release notes](https://github.com/webdriverio-community/node-geckodriver/releases) - [Changelog](https://github.com/webdriverio-community/node-geckodriver/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio-community/node-geckodriver/compare/v3.2.0...v4.2.1) --- updated-dependencies: - dependency-name: geckodriver dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump react-query from 3.39.2 to 3.39.3 (#794) Bumps [react-query](https://github.com/tannerlinsley/react-query) from 3.39.2 to 3.39.3. - [Release notes](https://github.com/tannerlinsley/react-query/releases) - [Commits](https://github.com/tannerlinsley/react-query/commits) --- updated-dependencies: - dependency-name: react-query dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * build(deps): bump react-router-dom from 6.4.4 to 6.16.0 (#795) Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.4.4 to 6.16.0. - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.16.0/packages/react-router-dom) --- updated-dependencies: - dependency-name: react-router-dom dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * build(deps): bump @scure/bip39 from 1.1.0 to 1.2.1 (#796) Bumps [@scure/bip39](https://github.com/paulmillr/scure-bip39) from 1.1.0 to 1.2.1. - [Release notes](https://github.com/paulmillr/scure-bip39/releases) - [Commits](https://github.com/paulmillr/scure-bip39/compare/1.1.0...1.2.1) --- updated-dependencies: - dependency-name: "@scure/bip39" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * build(deps-dev): bump electron from 25.8.0 to 25.8.1 (#798) Bumps [electron](https://github.com/electron/electron) from 25.8.0 to 25.8.1. - [Release notes](https://github.com/electron/electron/releases) - [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md) - [Commits](https://github.com/electron/electron/compare/v25.8.0...v25.8.1) --- updated-dependencies: - dependency-name: electron dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * build(deps-dev): bump lint-staged from 13.0.4 to 14.0.1 (#800) Bumps [lint-staged](https://github.com/okonet/lint-staged) from 13.0.4 to 14.0.1. - [Release notes](https://github.com/okonet/lint-staged/releases) - [Changelog](https://github.com/okonet/lint-staged/blob/master/CHANGELOG.md) - [Commits](https://github.com/okonet/lint-staged/compare/v13.0.4...v14.0.1) --- updated-dependencies: - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump tsconfig-paths-webpack-plugin from 4.0.0 to 4.1.0 (#801) Bumps [tsconfig-paths-webpack-plugin](https://github.com/dividab/tsconfig-paths-webpack-plugin) from 4.0.0 to 4.1.0. - [Changelog](https://github.com/dividab/tsconfig-paths-webpack-plugin/blob/master/CHANGELOG.md) - [Commits](https://github.com/dividab/tsconfig-paths-webpack-plugin/compare/v4.0.0...v4.1.0) --- updated-dependencies: - dependency-name: tsconfig-paths-webpack-plugin dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * build(deps-dev): bump @types/facepaint from 1.2.2 to 1.2.3 (#803) Bumps [@types/facepaint](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/facepaint) from 1.2.2 to 1.2.3. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/facepaint) --- updated-dependencies: - dependency-name: "@types/facepaint" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * build(deps-dev): bump webpack-cli from 5.0.0 to 5.1.4 (#804) Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 5.0.0 to 5.1.4. - [Release notes](https://github.com/webpack/webpack-cli/releases) - [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-cli/compare/webpack-cli@5.0.0...webpack-cli@5.1.4) --- updated-dependencies: - dependency-name: webpack-cli dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * removed input type from the transaction fee fields (#807) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * changed method of creating Keys for signing (#808) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fix: fix scope of bugs (#809) * fixed issue with transfer NFT error after wallet unlocking and some improvements * fixed issue with empty NFT details page after wallet unlocking and some improvements * fixed issue with home screen scroll * possible fix for an issue with a gap above the sticky tabs container * fixed an issue with the scrollable Import account screen --------- Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fixed issue with the navigation menu screen scroll (#818) Co-authored-by: ost-ptk * casper logo updated (#817) Co-authored-by: ost-ptk * removed the tooltip with the full public key when the user has only one account (#819) Co-authored-by: ost-ptk * build(deps-dev): bump electron from 25.8.1 to 25.9.0 (#822) Bumps [electron](https://github.com/electron/electron) from 25.8.1 to 25.9.0. - [Release notes](https://github.com/electron/electron/releases) - [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md) - [Commits](https://github.com/electron/electron/compare/v25.8.1...v25.9.0) --- updated-dependencies: - dependency-name: electron dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * disabled send button on NFT details page after this NFT was sent (#824) Co-authored-by: ost-ptk * fixed issue with the scroll on home page (#825) Co-authored-by: ost-ptk * Release 1.6.0 version (#826) * added countdown for user password length (#806) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * feature: add change password page (#799) * fixed issue with prettier config * fixed code style errors * added change password page and small refactor * merge conflict fixed * merge conflict issues fixed --------- Co-authored-by: ost-ptk * added a checkbox for setting the max transfer amount for CSPR transfer and some refactoring (#805) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fixed issue with words highlighting in the confirm secret phrase view (#823) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * feature: add dark mode (#821) * added dark mode * merge conflict issues fixed * changed png images to svg * Fix DefaultTheme interface --------- Co-authored-by: ost-ptk Co-authored-by: Dmytro Vynnyk * build(deps): bump @scure/bip32 from 1.1.1 to 1.3.2 (#812) Bumps [@scure/bip32](https://github.com/paulmillr/scure-bip32) from 1.1.1 to 1.3.2. - [Release notes](https://github.com/paulmillr/scure-bip32/releases) - [Commits](https://github.com/paulmillr/scure-bip32/compare/1.1.1...1.3.2) --- updated-dependencies: - dependency-name: "@scure/bip32" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump webpack from 5.76.0 to 5.88.2 (#813) Bumps [webpack](https://github.com/webpack/webpack) from 5.76.0 to 5.88.2. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.76.0...v5.88.2) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump eslint-plugin-react from 7.31.11 to 7.33.2 (#814) Bumps [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) from 7.31.11 to 7.33.2. - [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases) - [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/CHANGELOG.md) - [Commits](https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.31.11...v7.33.2) --- updated-dependencies: - dependency-name: eslint-plugin-react dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump @babel/preset-env from 7.20.2 to 7.23.2 (#828) Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.20.2 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-preset-env) --- updated-dependencies: - dependency-name: "@babel/preset-env" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Improve skeleton for dark mode (#835) * Release 1.6.1 version (#837) * fixed issue with insufficient data for displaying erc-20 tokens (#840) Co-authored-by: ost-ptk * Update README.md to add reference to CSPR.click for integration (#842) * Improve erc20 tokens fetching (#843) * Release/1.6.2 (#844) * Improve erc20 tokens fetching * Release 1.6.2 version * feature: add QR code option to the settings menu (#774) * added QR code option to the settings menu * added QR generation page and removed unused props * merge conflict issue fixed * temp commit * Get symbol and decimal optional * Add qr code generation for sync wallet * UI and UX fixes, and fixed validation rule * moved qr generation under password protection and removed one time password * Improve qrCode generation * Add multi qr code presenting --------- Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * Release 1.6.3 version (#852) * build(deps-dev): bump ts-jest from 29.0.3 to 29.1.1 (#831) Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 29.0.3 to 29.1.1. - [Release notes](https://github.com/kulshekhar/ts-jest/releases) - [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.0.3...v29.1.1) --- updated-dependencies: - dependency-name: ts-jest dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump redux-saga from 1.2.1 to 1.2.3 (#832) Bumps [redux-saga](https://github.com/redux-saga/redux-saga) from 1.2.1 to 1.2.3. - [Release notes](https://github.com/redux-saga/redux-saga/releases) - [Changelog](https://github.com/redux-saga/redux-saga/blob/main/CHANGELOG.md) - [Commits](https://github.com/redux-saga/redux-saga/compare/redux-saga@1.2.1...redux-saga@1.2.3) --- updated-dependencies: - dependency-name: redux-saga dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump css-loader from 6.7.2 to 6.8.1 (#833) Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 6.7.2 to 6.8.1. - [Release notes](https://github.com/webpack-contrib/css-loader/releases) - [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/css-loader/compare/v6.7.2...v6.8.1) --- updated-dependencies: - dependency-name: css-loader dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feature: add delegation and undelegation flow (#846) * added delegation and undelegation flow * fixed issue with sticky header in tabs * added a new modal window with buttons on the home page * added empty state UI for undelegation * fixed deploys tab and deploys details page for staking --------- Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * added active address in top nav (#847) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fixed issue with an open modal window (#848) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * build(deps-dev): bump @babel/core from 7.20.5 to 7.23.3 (#850) Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.20.5 to 7.23.3. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.3/packages/babel-core) --- updated-dependencies: - dependency-name: "@babel/core" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump @babel/traverse from 7.20.5 to 7.23.3 (#853) Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.20.5 to 7.23.3. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.3/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump axios from 1.1.3 to 1.6.1 (#854) Bumps [axios](https://github.com/axios/axios) from 1.1.3 to 1.6.1. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.1.3...v1.6.1) --- updated-dependencies: - dependency-name: axios dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump i18next-http-backend from 1.4.5 to 2.4.1 (#856) Bumps [i18next-http-backend](https://github.com/i18next/i18next-http-backend) from 1.4.5 to 2.4.1. - [Changelog](https://github.com/i18next/i18next-http-backend/blob/master/CHANGELOG.md) - [Commits](https://github.com/i18next/i18next-http-backend/compare/v1.4.5...v2.4.1) --- updated-dependencies: - dependency-name: i18next-http-backend dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump chromedriver from 107.0.3 to 119.0.1 (#851) Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 107.0.3 to 119.0.1. - [Commits](https://github.com/giggio/node-chromedriver/compare/107.0.3...119.0.1) --- updated-dependencies: - dependency-name: chromedriver dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Release 1.7.0 version (#864) * feature: add e2e test with playwright (#849) * added new e2e framework and CI config * change test dir * removed test example * added onboarding tests and changed CI script * removed CI scripts for selenium tests * fixed CI script and removed unused function * removed unused import * CI config improvements * added onboarding tests for firefox * increase workers for CI tests * added docker container for CI tests * fixing flaky test and removed docker container form CI * added headless mode for tests * updated PR template and fixed whitespaces in the CI config file * clean up playwright config * added a few e2e tests for popup * added a few e2e tests for popup * added CI config for popup tests * updated CI config for popup tests * skipped all tests except import account * fixing import account test * fixing import account test * fixing import account test * fixing import account test * fixing import account test * fixing import account test * fixing import account test * fixing import account test * fixing tests on CI * use config context * testing * use config context * testing * testing * testing * added docker container to CI config * testing * testing * testing * testing * testing * testing * testing * testing * testing * testing * testing * testing * testing * testing * enabled connect account test * enabled all popup tests * removed Firefox test from CI * cleanup * added container to CI config for onboarding tests * cleanup * testing * added signature tests for popup * added a few new tests * fix import issue * fixed issues with truncated keys * fixed test issue * updated playwright config * removed old e2e config and unused packages * added a new test and minor fixes * minor fix --------- Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * build(deps-dev): bump html-webpack-plugin from 5.5.0 to 5.5.3 (#858) Bumps [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) from 5.5.0 to 5.5.3. - [Release notes](https://github.com/jantimon/html-webpack-plugin/releases) - [Changelog](https://github.com/jantimon/html-webpack-plugin/blob/main/CHANGELOG.md) - [Commits](https://github.com/jantimon/html-webpack-plugin/compare/v5.5.0...v5.5.3) --- updated-dependencies: - dependency-name: html-webpack-plugin dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump prettier from 3.0.3 to 3.1.0 (#859) Bumps [prettier](https://github.com/prettier/prettier) from 3.0.3 to 3.1.0. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.0.3...3.1.0) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Vynnyk Dmytro * build(deps): bump react-loading-skeleton from 3.1.0 to 3.3.1 (#860) Bumps [react-loading-skeleton](https://github.com/dvtng/react-loading-skeleton) from 3.1.0 to 3.3.1. - [Release notes](https://github.com/dvtng/react-loading-skeleton/releases) - [Changelog](https://github.com/dvtng/react-loading-skeleton/blob/master/CHANGELOG.md) - [Commits](https://github.com/dvtng/react-loading-skeleton/compare/v3.1.0...v3.3.1) --- updated-dependencies: - dependency-name: react-loading-skeleton dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump @adobe/css-tools from 4.0.1 to 4.3.1 (#865) Bumps [@adobe/css-tools](https://github.com/adobe/css-tools) from 4.0.1 to 4.3.1. - [Changelog](https://github.com/adobe/css-tools/blob/main/History.md) - [Commits](https://github.com/adobe/css-tools/commits) --- updated-dependencies: - dependency-name: "@adobe/css-tools" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * added error handling for transfer and stakes (#867) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fix: fix wallet setup kick-off automatically in the Reset Wallet flow (#863) * fixed issue with auto launching onboarding after wallet reset * fixed prettier issues --------- Co-authored-by: ost-ptk * fix: fix truncated public key size (#868) * fixed issue with truncated public key size * fixed prettier issue --------- Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fix: fix the moon icon (#869) * updated the moon icon * updated the moon icon * fixed issue with same keys --------- Co-authored-by: ost-ptk * spacing between buttons was fixed (#870) Co-authored-by: ost-ptk * build(deps): bump react-hook-form from 7.40.0 to 7.48.2 (#871) Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.40.0 to 7.48.2. - [Release notes](https://github.com/react-hook-form/react-hook-form/releases) - [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md) - [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.40.0...v7.48.2) --- updated-dependencies: - dependency-name: react-hook-form dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump web-ext from 7.6.2 to 7.8.0 (#874) Bumps [web-ext](https://github.com/mozilla/web-ext) from 7.6.2 to 7.8.0. - [Release notes](https://github.com/mozilla/web-ext/releases) - [Commits](https://github.com/mozilla/web-ext/compare/7.6.2...7.8.0) --- updated-dependencies: - dependency-name: web-ext dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump @adobe/css-tools from 4.3.1 to 4.3.2 (#875) Bumps [@adobe/css-tools](https://github.com/adobe/css-tools) from 4.3.1 to 4.3.2. - [Changelog](https://github.com/adobe/css-tools/blob/main/History.md) - [Commits](https://github.com/adobe/css-tools/commits) --- updated-dependencies: - dependency-name: "@adobe/css-tools" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Release/1.7.1 (#881) * removed the button from the footer and fixed indent * Release 1.7.1 version --------- Co-authored-by: ost-ptk * feature: add contact book to the wallet (#878) * added contact book to the wallet * added contact sort and fixed the issue with updating contact --------- Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * showed user stake amount for validator on undelegate page (#879) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * added new UI for account switcher (#883) Co-authored-by: ost-ptk * updated header UI (#884) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * updated the theme switcher (#885) Co-authored-by: ost-ptk * feature: merge recent recipients with contacts list (#886) * added contact book to the wallet * added contact sort and fixed the issue with updating contact * merged recent recipient with contacts * list height fix --------- Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fix: fix issue with empty contact screen (#888) * fixed issue with not trimmed names for contacts * minor fix * minor code improvements --------- Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fix: fix contact list preserving after the wallet reset (#887) * added contact book to the wallet * added contact sort and fixed the issue with updating contact * merged recent recipient with contacts * list height fix * added reset actions for vault settings, contacts and recent recipient --------- Co-authored-by: ost-ptk * opening of the account import window has been updated (#889) Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * fixed an issue with a scrolled-down window in Firefox (#892) Co-authored-by: ost-ptk * fixed validation issue in recipient dropdown input (#893) Co-authored-by: ost-ptk * updated UI for recipient dropdown input (#894) Co-authored-by: ost-ptk * removed the System option for Safari from the theme switcher (#895) Co-authored-by: ost-ptk * changed 'Contacts list' to 'Contacts' (#896) Co-authored-by: ost-ptk * removed the 'max delegator' rule for the validator field from the Undelegate flow (#897) Co-authored-by: ost-ptk * removed hover tooltip from the public key in the account switcher (#898) Co-authored-by: ost-ptk * feature: add unlock wallet animation (#640) * Added animations lib and lottie dependency Added new unlock animation in the unlock vault view * Changed animation layout * Fixed review comments * Updated unlock animation to the linked animation file. * Optimized build all script * Optimized layout * Optimized animation * fix merge conflict issues * fixed version in package lock * updated unlock animation * updated unlock e2e test --------- Co-authored-by: ost-ptk Co-authored-by: Ostap Piatkovskyi <44294945+ost-ptk@users.noreply.github.com> Co-authored-by: Dmytro Vynnyk * Release 1.8.0 version (#900) * build(deps): bump postcss and web-ext (#899) Bumps [postcss](https://github.com/postcss/postcss) to 8.4.32 and updates ancestor dependency [web-ext](https://github.com/mozilla/web-ext). These dependencies need to be updated together. Updates `postcss` from 8.4.29 to 8.4.32 - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.4.29...8.4.32) Updates `web-ext` from 7.8.0 to 7.9.0 - [Release notes](https://github.com/mozilla/web-ext/releases) - [Commits](https://github.com/mozilla/web-ext/compare/7.8.0...7.9.0) --- updated-dependencies: - dependency-name: postcss dependency-type: indirect - dependency-name: web-ext dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feature: create tests for Contacts (#903) * added tests for Contacts * test description fixed --------- Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * displaying validators icon fixed (#905) Co-authored-by: ost-ptk * build(deps-dev): bump follow-redirects from 1.15.0 to 1.15.4 (#907) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.0 to 1.15.4. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.0...v1.15.4) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feature: update eslint config (#906) * added sort imports and removed unused files * fixed eslint errors * content URLs fixed * code refactor to fix eslint error * Refactor code to fix ESLint errors and add new import rules --------- Co-authored-by: ost-ptk * feature: sign view improvements (#908) * Add contract hash and name to deploy details * Remove account switcher from PopupHeader in signature request page --------- Co-authored-by: ost-ptk * moved the timeout section to the security block (#909) Co-authored-by: ost-ptk * added a disabled attribute to the unlock button (#910) Co-authored-by: ost-ptk * fixed UI bugs (#911) Co-authored-by: ost-ptk * Add support for NFT transfers (#912) The update adds a new activity type called 'TransferNFT' to handle NFT transfers. Now, any transaction case labeled with 'Transfer' will be processed correctly. Also, the naming standard in constants was updated for different activity types from 'Mint' and 'Burn' to 'Mint NFT' and 'Burn NFT' respectively to better reflect the actions. Co-authored-by: ost-ptk * updated illustration for the contact deleting page (#913) Co-authored-by: ost-ptk * feature: show staked balance on home page (#917) * added staked balance to home page * Update account balance fetching and add new hosts * Replace CasperCloudApiUrl with CasperWalletApiUrl in settings and services --------- Co-authored-by: ost-ptk * Add redelegation feature and enhance form validation rules (#916) Co-authored-by: ost-ptk * feature: update secret phrase validation and recovery for 12 and 24 words (#918) * Update secret phrase validation and recovery for 12 and 24 words * Update e2e tests to include 12-words secret phrase recovery * Update CORS to fix issue for Firefox --------- Co-authored-by: ost-ptk * Update a success message for NFT transfer (#919) Co-authored-by: ost-ptk * Update warning color in theme configuration (#920) Co-authored-by: ost-ptk * fix account hash hint is overlapped by loading animation of stakes balance (#921) Co-authored-by: ost-ptk * fix screen twitch upon clicking on recipient's field (#922) Co-authored-by: ost-ptk * Fix checkbox behavior in transfer amount step (#923) Co-authored-by: ost-ptk * Release 1.8.1 version (#926) * fix: fix issue with redux devtools and clean up all other issues (#924) * Set Content Security Policy for Safari dynamically and update it for Chrome and Firefox * Update casper-js-sdk version and refactor Redux devtools * Wrap localStorage data retrieval in try-catch block --------- Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * Add case-insensitive account matching utility function (#925) Co-authored-by: ost-ptk * feature: update 'Sign:Response' with additional error handling (#929) * Update 'Sign:Response' with additional error handling * Export isEqualCaseInsensitive util and optimize message property --------- Co-authored-by: ost-ptk * feature: update package versions and remove unused dependencies (#931) * Update package versions and remove unused dependencies * Update testing-library/jest-dom import paths --------- Co-authored-by: ost-ptk * feature: add rate app (#933) * create a rate app page and functionality * Add review request after successful transaction --------- Co-authored-by: ost-ptk Co-authored-by: Vynnyk Dmytro * feature: add support for importing from .cer file (#934) * add support for importing from .cer file * Add support to import .cer account in e2e tests * rename secret key files for tests --------- Co-authored-by: ost-ptk * Update casper-js-sdk version (#935) Co-authored-by: ost-ptk * refactor the validator loading logic on staking and add spinner (#937) * feature: improve error handling (#936) * Improve error handling for transfers * Improve error handling in stake operation * Improve error handling for transfer nft * Improve error handling in staking and NFT transfer actions * Fix native transfer --------- Co-authored-by: ost-ptk * feature: update URLs for CasperWallet API endpoints (#938) * Improve error handling in staking and NFT transfer actions * Update URLs for CasperWallet API endpoints The URLs for both the mainnet and testnet CasperWallet API endpoints have been updated in various files, including "constants.ts", "declarative_net_request_rules.json", "manifest.v2.json", "manifest.v2.safari.json", "manifest.v3.json", "utils.ts", and "webpack.config.js". No changes were made to error handling in staking or NFT transfer actions as the original commit message suggested. The update was a necessary adjustment to ensure that the application connects to the correct API endpoints. * Add support for JPG format in token icons (#949) * fix: add validation for the transaction fee field (#950) * Add validation for transaction fee field * Update UI to handle large balance and name in account plate and fix id for token activity list * fix: fix issue with big numbers (#951) * Fix issue with big numbers * update UI to handle large value amount in activity details page and activity plate * Update validation message (#939) * Refactor file upload input accept attribute (#940) * Update formatting for an amount input value (#941) * Corrected review request logic (#943) * fix stake action texts (#944) * build(deps-dev): bump ip from 1.1.8 to 1.1.9 (#945) Bumps [ip](https://github.com/indutny/node-ip) from 1.1.8 to 1.1.9. - [Commits](https://github.com/indutny/node-ip/compare/v1.1.8...v1.1.9) --- updated-dependencies: - dependency-name: ip dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: add handle for large fiat amount (#947) * Refactor the `motesToCurrency` function to handle large values in `formatters.ts`. Previously, the function would return large numbers directly but now, if the calculated amount equals or surpasses 10^9, it returns an empty string. This is a temporary solution, a clarification on future behavior for handling large amounts is still needed. * Update handling and formatting of large values * fix: add notification for accounts that try to undelegate with no liquid balance (#957) * Add error for staking when a user has not enough balances to cover min amount and transaction fee * update CSPR error UI in transfer flow and checkbox cursor behavior * Add support for hidden accounts and improve account listing (#953) Co-authored-by: Vynnyk Dmytro * Add translate property to Typography component (#960) * build(deps-dev): bump follow-redirects from 1.15.4 to 1.15.6 (#959) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feature: add account balance to account switcher and all accounts list (#958) * Add support for hidden accounts and improve account listing * Add account balance to account switcher and all accounts list * Update transfer and deploy functionality, refactor code (#961) The changes centralize all deploy-making methods into the deployer service to improve maintainability and anticipate future modifications. Asynchronous calls replace synchronous for improved performance, addressing the casper-node synchronization issue by fetching the timestamp directly from the node instead of the date library. * feature: add Torus Wallet account import feature (#964) * Add Torus Wallet account import feature * Improve validation of Torus Wallet secret key imports * Update Torus Wallet secret and public keys for E2E tests * Replace ERC20 icon with CEP18 contract icon in UI components (#962) * build(deps-dev): bump webpack-dev-middleware from 5.3.1 to 5.3.4 (#963) Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.1 to 5.3.4. - [Release notes](https://github.com/webpack/webpack-dev-middleware/releases) - [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.1...v5.3.4) --- updated-dependencies: - dependency-name: webpack-dev-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feature: add functionality for buying CSPR from within the extension (#966) * Add functionality for buying CSPR from within the extension * Reorder illustration and text in no-provider page * fix: fix UI glitch after create account (#967) * Separate account balances from account data in Redux * Revert minimum password length --------- Co-authored-by: Vynnyk Dmytro * fix: Update event handling for numeric input fields (#968) * Update event handling for numeric input fields * Remove unused import * fix: Adjust error handling and introduce node status URL (#969) * Adjust error handling and introduce node status URL * Update URL references and deployments to match updated Node API * Update condition for composeEnhancers in Redux * Remove unused import in transfer page * Add functionality to download account keys (#970) * fix: fix crash on onboarding flow (#971) * Update all i18next packages and code to new style * Fix issue with removeChild on node --------- Co-authored-by: Dmytro Vynnyk * build(deps-dev): bump markdownlint from 0.26.2 to 0.34.0 (#972) Bumps [markdownlint](https://github.com/DavidAnson/markdownlint) from 0.26.2 to 0.34.0. - [Changelog](https://github.com/DavidAnson/markdownlint/blob/main/CHANGELOG.md) - [Commits](https://github.com/DavidAnson/markdownlint/compare/v0.26.2...v0.34.0) --- updated-dependencies: - dependency-name: markdownlint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: fix sending deploys for Safari (#974) * Fix sending deploys for Safari * Add error handling for failed deploy submissions * Replace Link components with window.open for external URLs (#975) * Add e2e tests for buying CSPR and make minor code adjustments (#976) * Release 1.9.0 version (#978) * fix: Disable Redux devtools in development mode (#977) * Disable Redux devtools in development mode * Update WebSocket URL in webpack.config.js * fix issue with infinity connection to WebSocket for devtools * Release 1.9.1 version (#980) * Improve handling of onboarding URL navigation (#983) * feature: ledger integration (#955) * Add connection ledger UI and update signing flow for using the ledger * Add animation for ledger, refactor code for dark mode * Remove unused imports in avatar and ledger accounts list * Update cursor behavior and colors for disabled checkboxes The code modifies the Cursor behavior and colors for disabled checkboxes and buttons. It also introduces a check to prevent duplicate Ledger accounts from being connected by deactivating the selection and changing the cursor to 'not-allowed' when an account is already connected. The checkbox colors have been adjusted accordingly. * Adjust spinner animation colors and tidy up layout styling * Update how imported account visibility is checked in tests Replaced the method of checking the visibility of imported accounts in e2e tests. Instead of searching for the exact text 'Imported', the test now looks for a specific dataTestId ('import-account-icon'). Also, added the 'import-account-icon' dataTestId to the SvgIcon component in the hash.tsx file. * Update SVG illustration for ledger * Add Ledger integration business logic * Update Ledger integration UI * Refactor account import functionality from ledger (#984) * Update maxHeight property in account-list component (#985) * Add useEffect to scrollToTop in Ledger error components (#986) * Remove lodash.debounce (#987) * Handle issue with browser's device permission window * Update global styles and layout parameters This commit includes a change in the global styles file to allow elements to display in a flex layout and centrally align their content. In addition, modifications have been made to the layout-window file to ensure elements take up 100% width, enhancing the app's layout flexibility. * Fix small issues --------- Co-authored-by: Dmytro Vynnyk * Release 1.10.0 version (#989) * Add PNG token icon support and transaction amount handling (#992) * feature: update casper, erc-20 and nft transfer flow (#982) * Update casper, erc-20 and nft transfer flow * Remove unused useCasperToken hook from home page * Fix issue with multi-click on confirmation button and code refactoring * feature: improve extension in-tab view (#988) * Improve extension in-tab view * Update Overlay component and add delays in e2e tests The Overlay component's position and size properties were adjusted to fit its new design requirements. The width is now fixed, and it is centered using transform. Meanwhile, explicit delays were added in the end-to-end tests for 'buy-cspr.spec.ts' to ensure proper closing of modal windows before continuing with the tests. * build(deps-dev): bump eslint-config-prettier from 8.5.0 to 9.1.0 (#877) Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 8.5.0 to 9.1.0. - [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-config-prettier/compare/v8.5.0...v9.1.0) --- updated-dependencies: - dependency-name: eslint-config-prettier dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ostap Piatkovskyi <44294945+ost-ptk@users.noreply.github.com> * build(deps-dev): bump @redux-devtools/cli from 3.0.1 to 4.0.0 (#904) * build(deps-dev): bump @redux-devtools/cli from 3.0.1 to 4.0.0 Bumps [@redux-devtools/cli](https://github.com/reduxjs/redux-devtools) from 3.0.1 to 4.0.0. - [Release notes](https://github.com/reduxjs/redux-devtools/releases) - [Commits](https://github.com/reduxjs/redux-devtools/compare/v3.0.1...@redux-devtools/cli@4.0.0) --- updated-dependencies: - dependency-name: "@redux-devtools/cli" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Update Node version and various dependencies This update includes bumping the Node version used in the project and updating several dependencies. The update on dependencies includes changes in version for dev tools resulting in improved performance and potentially fixing bugs from older versions. It's important to keep your local node version aligned with this update. * Update GitHub Actions to newer versions This commit updates the used version of multiple GitHub Actions including checkout, setup-node, upload-artifact, and cache in multiple workflow files. This ensures that our CI/CD processes utilize the latest features and improvements introduced in these updated actions. --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: ost-ptk * feature: update account recovery with multiple account selection (#991) * Update account recovery with multiple account selection * Remove unused import in onboarding success page * build(deps-dev): bump html-webpack-plugin from 5.5.3 to 5.6.0 (#999) Bumps [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) from 5.5.3 to 5.6.0. - [Release notes](https://github.com/jantimon/html-webpack-plugin/releases) - [Changelog](https://github.com/jantimon/html-webpack-plugin/blob/main/CHANGELOG.md) - [Commits](https://github.com/jantimon/html-webpack-plugin/compare/v5.5.3...v5.6.0) --- updated-dependencies: - dependency-name: html-webpack-plugin dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix condition for 'Add recipient to list of contacts' button (#1000) * Fix the issue with unlocking the wallet after a recovering account (#996) * Fix UI for transfer flow (#1002) * return min password length (#1003) * feature: add e2e tests for transfers and staking (#1004) * Add e2e tests for transfers and staking, and small refactoring * update tests for erc-20 * added overflow property to the Overlay component (#1005) * change user stake to total stake (#1006) * added password protection page for change password flow (#1007) * fix confirm send button for transfer and staking (#1008) * move change password and generate QR functionality to service worker (#1009) * added clipboard read and write permission for chrome (#1010) * fix: fix e2e tests for staking (#1011) * added clipboard read and write permission for chrome * update e2e tests for staking * update validation rule for max delegators (#1012) * build(deps-dev): bump braces from 3.0.2 to 3.0.3 (#1013) Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3. - [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3) --- updated-dependencies: - dependency-name: braces dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * move password verification to service worker (#1014) * update hide account functionality (#1015) * update transfer flow from the token page (#1016) * update icon generation (#1017) * add validation for amount decimal part for token transfer (#1018) * Release 1.11.0 version --------- Signed-off-by: dependabot[bot] Co-authored-by: Ostap Piatkovskyi <44294945+ost-ptk@users.noreply.github.com> Co-authored-by: ost-ptk Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Muhammet Kara Co-authored-by: Piotr Witek <739075+piotrwitek@users.noreply.github.com> Co-authored-by: ost-ptk --- .github/workflows/ci-cd-manual.yml | 6 +- .github/workflows/ci-check.yml | 8 +- .github/workflows/cleanup-caches.yml | 2 +- .github/workflows/e2e-onboarding-tests.yml | 8 +- .github/workflows/e2e-popup-tests.yml | 8 +- .nvmrc | 2 +- e2e-tests/constants.ts | 55 +- .../recover-secret-phrase-flow.spec.ts | 175 +- e2e-tests/popup/buy-cspr/buy-cspr.spec.ts | 8 +- .../signature-request-scenarios.spec.ts | 34 +- e2e-tests/popup/stakes/delegate.spec.ts | 64 + e2e-tests/popup/stakes/redelagation.spec.ts | 95 + e2e-tests/popup/stakes/undelegate.spec.ts | 64 + .../transfers/casper-native-transfer.spec.ts | 146 + .../popup/transfers/erc-20-transfer.spec.ts | 185 ++ .../popup/transfers/nft-transfer.spec.ts | 65 + package-lock.json | 2733 ++++++++--------- package.json | 8 +- scripts/build_all.sh | 2 +- .../import-account-with-file/app-router.tsx | 19 +- .../content.tsx | 44 + .../index.tsx | 69 +- .../content.tsx | 39 + .../index.tsx | 54 +- src/apps/onboarding/app-router.tsx | 5 + .../confirm-secret-phrase-success/content.tsx | 9 +- .../confirm-secret-phrase-success/index.tsx | 2 +- .../pages/confirm-secret-phrase/content.tsx | 16 +- .../pages/confirm-secret-phrase/index.tsx | 2 +- .../content.tsx | 13 +- .../index.tsx | 3 +- .../pages/create-secret-phrase/content.tsx | 16 +- .../pages/create-vault-password/content.tsx | 16 +- .../pages/create-vault-password/index.tsx | 2 +- .../pages/onboarding-success/content.tsx | 11 +- .../recover-from-secret-phrase/content.tsx | 13 +- .../recover-from-secret-phrase/index.tsx | 16 +- .../onboarding/pages/reset-wallet/content.tsx | 2 +- .../select-accounts-to-recover/content.tsx | 106 + .../select-accounts-to-recover/index.tsx | 182 ++ .../pages/select-accounts-to-recover/utils.ts | 24 + .../pages/unlock-wallet/content.tsx | 5 +- .../onboarding/pages/unlock-wallet/index.tsx | 1 + src/apps/onboarding/pages/welcome/content.tsx | 13 +- .../write-down-secret-phrase/content.tsx | 16 +- .../pages/write-down-secret-phrase/index.tsx | 3 +- src/apps/onboarding/router/paths.ts | 3 +- src/apps/onboarding/router/types.ts | 4 +- src/apps/popup/app-router.tsx | 53 +- .../popup/pages/account-settings/content.tsx | 4 +- src/apps/popup/pages/buy-cspr/amount.tsx | 29 +- src/apps/popup/pages/buy-cspr/country.tsx | 21 +- .../popup/pages/change-password/index.tsx | 76 +- src/apps/popup/pages/create-account/index.tsx | 4 +- .../more-buttons-modal/modal-buttons.tsx | 14 +- .../components/nft-list/nft-token-card.tsx | 1 + src/apps/popup/pages/home/index.tsx | 18 +- .../connected-ledger.tsx | 13 +- .../pages/no-connected-account/content.tsx | 39 + .../pages/no-connected-account/index.tsx | 57 +- .../pages/password-protection-page/index.tsx | 117 +- .../popup/pages/remove-account/content.tsx | 39 + src/apps/popup/pages/remove-account/index.tsx | 70 +- .../popup/pages/rename-account/content.tsx | 51 + src/apps/popup/pages/rename-account/index.tsx | 64 +- src/apps/popup/pages/stakes/amount-step.tsx | 10 +- src/apps/popup/pages/stakes/confirm-step.tsx | 18 +- src/apps/popup/pages/stakes/content.tsx | 164 - src/apps/popup/pages/stakes/index.tsx | 624 ++-- .../redelegate-validator-dropdown-input.tsx | 4 +- src/apps/popup/pages/stakes/utils.ts | 119 +- .../pages/stakes/validator-dropdown-input.tsx | 16 +- src/apps/popup/pages/token-details/token.tsx | 13 +- .../transfer-nft/components/nft-plate.tsx | 76 + .../popup/pages/transfer-nft/confirm-step.tsx | 60 + src/apps/popup/pages/transfer-nft/content.tsx | 167 - src/apps/popup/pages/transfer-nft/index.tsx | 304 +- .../pages/transfer-nft/recipient-step.tsx | 42 + .../popup/pages/transfer-nft/review-step.tsx | 96 + src/apps/popup/pages/transfer-nft/utils.ts | 13 + src/apps/popup/pages/transfer/amount-step.tsx | 157 +- .../pages/transfer/components/token-row.tsx | 57 + .../components/token-swticher-row.tsx | 56 + .../popup/pages/transfer/confirm-step.tsx | 10 +- src/apps/popup/pages/transfer/content.tsx | 76 - src/apps/popup/pages/transfer/index.tsx | 551 ++-- .../popup/pages/transfer/recipient-step.tsx | 45 +- src/apps/popup/pages/transfer/token-step.tsx | 110 + src/apps/popup/pages/transfer/utils.ts | 9 +- src/apps/popup/pages/wallet-qr-code/index.tsx | 42 +- src/apps/popup/router/paths.ts | 3 +- src/apps/popup/router/types.ts | 2 +- src/assets/icons/paste.svg | 3 + src/background/index.ts | 10 +- src/background/open-onboarding-flow.ts | 1 - src/background/redux/contacts/types.ts | 4 + src/background/redux/sagas/actions.ts | 10 +- .../redux/sagas/onboarding-sagas.ts | 21 +- src/background/redux/sagas/vault-sagas.ts | 89 +- src/background/redux/vault/actions.ts | 6 +- src/background/redux/vault/reducer.ts | 18 +- .../workers/create-password-worker.ts | 39 + .../generate-sync-wallet-qr-data-worker.ts} | 28 +- ...kVaultWorker.ts => unlock-vault-worker.ts} | 0 .../workers/verify-password-worker.ts | 22 + src/constants.ts | 1 + src/hooks/use-submit-button.ts | 51 + src/libs/crypto/index.ts | 1 - src/libs/layout/containers.ts | 32 +- src/libs/layout/error/content.tsx | 10 +- src/libs/layout/error/tab-page.tsx | 2 +- src/libs/layout/error/window-page.tsx | 2 +- .../header-submenu-bar-buy-cspr-link.tsx | 19 + src/libs/layout/layout-tab.tsx | 21 +- src/libs/layout/locked-router/index.tsx | 63 +- src/libs/layout/reset-vault/content.tsx | 40 + src/libs/layout/reset-vault/index.tsx | 106 +- src/libs/layout/unlock-vault/content.tsx | 114 + src/libs/layout/unlock-vault/index.tsx | 268 +- .../account-activity-service/types.ts | 1 + .../account-activity-plate.tsx | 35 +- .../account-popover/account-popover.tsx | 4 +- .../active-account-plate.tsx | 8 +- src/libs/ui/components/avatar/avatar.tsx | 6 +- .../dynamic-accounts-list-with-select.tsx} | 37 +- .../erc20-token-activity-list.tsx | 1 + src/libs/ui/components/index.ts | 4 + src/libs/ui/components/input/input.tsx | 12 +- .../ui/components/modal/modal-switcher.tsx} | 26 +- src/libs/ui/components/modal/modal.tsx | 2 +- .../components/contacts-list.tsx | 60 + .../components/my-accounts-list.tsx | 52 + .../recipient-tabs/components/recent-list.tsx | 82 + .../recipient-tabs/recipient-tabs.tsx | 157 + .../ui/components/recipient-tabs/utils.ts | 5 + src/libs/ui/components/tabs/tabs.tsx | 2 +- .../ui/components/token-plate/token-plate.tsx | 3 +- .../transaction-fee-plate.tsx | 56 + .../ui/components/typography/typography.tsx | 33 +- src/libs/ui/forms/create-account.ts | 25 +- src/libs/ui/forms/form-validation-rules.ts | 75 +- src/libs/ui/forms/stakes-form.ts | 17 +- src/libs/ui/forms/transfer.ts | 26 +- src/manifest.v3.json | 4 +- src/utils.ts | 5 + .../Casper Wallet.xcodeproj/project.pbxproj | 44 +- 146 files changed, 6014 insertions(+), 3639 deletions(-) create mode 100644 e2e-tests/popup/stakes/delegate.spec.ts create mode 100644 e2e-tests/popup/stakes/redelagation.spec.ts create mode 100644 e2e-tests/popup/stakes/undelegate.spec.ts create mode 100644 e2e-tests/popup/transfers/casper-native-transfer.spec.ts create mode 100644 e2e-tests/popup/transfers/erc-20-transfer.spec.ts create mode 100644 e2e-tests/popup/transfers/nft-transfer.spec.ts create mode 100644 src/apps/import-account-with-file/pages/import-account-with-file-failure/content.tsx create mode 100644 src/apps/import-account-with-file/pages/import-account-with-file-success/content.tsx create mode 100644 src/apps/onboarding/pages/select-accounts-to-recover/content.tsx create mode 100644 src/apps/onboarding/pages/select-accounts-to-recover/index.tsx create mode 100644 src/apps/onboarding/pages/select-accounts-to-recover/utils.ts create mode 100644 src/apps/popup/pages/no-connected-account/content.tsx create mode 100644 src/apps/popup/pages/remove-account/content.tsx create mode 100644 src/apps/popup/pages/rename-account/content.tsx create mode 100644 src/apps/popup/pages/transfer-nft/components/nft-plate.tsx create mode 100644 src/apps/popup/pages/transfer-nft/confirm-step.tsx delete mode 100644 src/apps/popup/pages/transfer-nft/content.tsx create mode 100644 src/apps/popup/pages/transfer-nft/recipient-step.tsx create mode 100644 src/apps/popup/pages/transfer-nft/review-step.tsx create mode 100644 src/apps/popup/pages/transfer/components/token-row.tsx create mode 100644 src/apps/popup/pages/transfer/components/token-swticher-row.tsx create mode 100644 src/apps/popup/pages/transfer/token-step.tsx create mode 100644 src/assets/icons/paste.svg create mode 100644 src/background/workers/create-password-worker.ts rename src/{libs/crypto/sync-wallet-qr.ts => background/workers/generate-sync-wallet-qr-data-worker.ts} (68%) rename src/background/workers/{unlockVaultWorker.ts => unlock-vault-worker.ts} (100%) create mode 100644 src/background/workers/verify-password-worker.ts create mode 100644 src/hooks/use-submit-button.ts create mode 100644 src/libs/layout/header/header-submenu-bar-buy-cspr-link.tsx create mode 100644 src/libs/layout/reset-vault/content.tsx create mode 100644 src/libs/layout/unlock-vault/content.tsx rename src/{apps/popup/pages/import-account-from-ledger/ledger-accounts-list.tsx => libs/ui/components/dynamic-accounts-list-with-select/dynamic-accounts-list-with-select.tsx} (94%) rename src/{apps/popup/pages/buy-cspr/components/switcher.tsx => libs/ui/components/modal/modal-switcher.tsx} (75%) create mode 100644 src/libs/ui/components/recipient-tabs/components/contacts-list.tsx create mode 100644 src/libs/ui/components/recipient-tabs/components/my-accounts-list.tsx create mode 100644 src/libs/ui/components/recipient-tabs/components/recent-list.tsx create mode 100644 src/libs/ui/components/recipient-tabs/recipient-tabs.tsx create mode 100644 src/libs/ui/components/recipient-tabs/utils.ts create mode 100644 src/libs/ui/components/transaction-fee-plate/transaction-fee-plate.tsx diff --git a/.github/workflows/ci-cd-manual.yml b/.github/workflows/ci-cd-manual.yml index fa018333a..30df949b3 100644 --- a/.github/workflows/ci-cd-manual.yml +++ b/.github/workflows/ci-cd-manual.yml @@ -28,13 +28,13 @@ jobs: strategy: matrix: - node-version: [16.x] + node-version: [18.x] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} diff --git a/.github/workflows/ci-check.yml b/.github/workflows/ci-check.yml index f6b31ce92..eb59f5a41 100644 --- a/.github/workflows/ci-check.yml +++ b/.github/workflows/ci-check.yml @@ -12,14 +12,14 @@ jobs: strategy: matrix: - node-version: [16.x] + node-version: [18.x] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Cache npm dependencies id: cache-npm - uses: actions/cache@v3 + uses: actions/cache@v4 env: cache-name: cache-node-modules with: @@ -32,7 +32,7 @@ jobs: ${{ runner.os }}- - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} diff --git a/.github/workflows/cleanup-caches.yml b/.github/workflows/cleanup-caches.yml index 66a50b336..b6a753d2d 100644 --- a/.github/workflows/cleanup-caches.yml +++ b/.github/workflows/cleanup-caches.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Cleanup run: | diff --git a/.github/workflows/e2e-onboarding-tests.yml b/.github/workflows/e2e-onboarding-tests.yml index f81a8a7b2..beb816132 100644 --- a/.github/workflows/e2e-onboarding-tests.yml +++ b/.github/workflows/e2e-onboarding-tests.yml @@ -14,8 +14,8 @@ jobs: image: mcr.microsoft.com/playwright:v1.39.0-jammy steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 @@ -28,14 +28,14 @@ jobs: - name: Run Chrome tests run: npm run e2e:chrome:headless:onboarding - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: playwright-report path: playwright-report/ retention-days: 30 - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: test-results diff --git a/.github/workflows/e2e-popup-tests.yml b/.github/workflows/e2e-popup-tests.yml index dc55998c7..b6174c6f2 100644 --- a/.github/workflows/e2e-popup-tests.yml +++ b/.github/workflows/e2e-popup-tests.yml @@ -14,8 +14,8 @@ jobs: image: mcr.microsoft.com/playwright:v1.39.0-jammy steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 @@ -28,14 +28,14 @@ jobs: - name: Run Chrome tests run: npm run e2e:chrome:headless:popup - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: playwright-report path: playwright-report/ retention-days: 30 - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: test-results diff --git a/.nvmrc b/.nvmrc index 9e15be387..5f09eed8d 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.20.0 +v18.20.3 diff --git a/e2e-tests/constants.ts b/e2e-tests/constants.ts index 04534c06d..05b7d7c91 100644 --- a/e2e-tests/constants.ts +++ b/e2e-tests/constants.ts @@ -70,19 +70,68 @@ export const DEFAULT_SECOND_ACCOUNT = { mediumTruncatedPublicKey: '0203b2e05f...8e91aa7724' }; -export const RECOVER_ACCOUNT_FROM_TWELVE_WORDS = { +export const RECOVER_FIRST_ACCOUNT_FROM_TWELVE_WORDS = { accountName: 'Account 1', publicKey: '0202b869dbed03ef2cc6a76e54e1a5c588fbe6198f80937994f9a2c1fd3aff4adc1b', truncatedPublicKey: '0202b...adc1b' }; -export const VALIDATOR = { +export const RECOVER_SECOND_ACCOUNT_FROM_TWELVE_WORDS = { + accountName: 'Account 1', + publicKey: + '02022cafccfb61ffc4e4221e4d2c38eec6035d579d04c1396b1c2027dc0729c53589', + truncatedPublicKey: '02022...53589' +}; + +export const VALIDATOR_FOR_SIGNATURE_REQUEST = { name: 'Validator', truncatedPublicKey: '0106c...ca2ca' }; -export const NEW_VALIDATOR = { +export const NEW_VALIDATOR_FOR_SIGNATURE_REQUEST = { name: 'New validator', truncatedPublicKey: '017d9...2009e' }; + +export const VALIDATOR_FOR_STAKE = { + publicKey: + '010e5669b070545e2b32bc66363b9d3d4390fca56bf52a05f1411b7fa18ca311c7', + truncatedPublicKey: '010e...11c7' +}; + +export const NEW_VALIDATOR_FOR_STAKE = { + publicKey: + '01ad002e37667f90aa982396ebdfcc7d3eea99731241eaad8a0dc20f453f72975a', + truncatedPublicKey: '01ad...975a' +}; + +export const URLS = { + rpc: 'https://node.testnet.cspr.cloud/rpc' +}; + +export const RPC_RESPONSE = { + success: { + status: 200, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1717761373590, + result: { + api_version: '1.5.6', + deploy_hash: 'deploy hash' + } + }) + }, + failure: { + status: 500, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1717761373590, + error: { + code: '', + data: 'Error description', + message: 'Error message' + } + }) + } +}; diff --git a/e2e-tests/onboarding-flow/recover-secret-phrase-flow.spec.ts b/e2e-tests/onboarding-flow/recover-secret-phrase-flow.spec.ts index b3d030e70..45d22d064 100644 --- a/e2e-tests/onboarding-flow/recover-secret-phrase-flow.spec.ts +++ b/e2e-tests/onboarding-flow/recover-secret-phrase-flow.spec.ts @@ -1,6 +1,8 @@ import { DEFAULT_FIRST_ACCOUNT, - RECOVER_ACCOUNT_FROM_TWELVE_WORDS, + DEFAULT_SECOND_ACCOUNT, + RECOVER_FIRST_ACCOUNT_FROM_TWELVE_WORDS, + RECOVER_SECOND_ACCOUNT_FROM_TWELVE_WORDS, twelveWordsSecretPhrase, twentyFourWordsSecretPhrase } from '../constants'; @@ -8,7 +10,7 @@ import { onboarding, onboardingExpect } from '../fixtures'; onboarding.describe('Onboarding UI: recover secret phrase flow', () => { onboarding( - 'should recover account via 24 words secret phrase', + 'should recover one account via 24 words secret phrase', async ({ page, createOnboardingPassword, extensionId }) => { await createOnboardingPassword(); @@ -26,10 +28,27 @@ onboarding.describe('Onboarding UI: recover secret phrase flow', () => { .getByPlaceholder('e.g. Bobcat Lemon Blanket…') .fill(twentyFourWordsSecretPhrase); - await page.getByRole('button', { name: 'Recover my wallet' }).click(); + await page.getByRole('button', { name: 'Next' }).click(); + + await onboardingExpect( + page.getByText('Select accounts to recover') + ).toBeVisible(); + + await onboardingExpect( + page.getByRole('button', { name: 'Recover selected accounts' }) + ).toBeDisabled(); + + await page.getByTestId('select-account-0').click(); + + await page + .getByRole('button', { name: 'Recover selected accounts' }) + .click(); await page.goto(`chrome-extension://${extensionId}/popup.html`); + await page.getByTestId('menu-open-icon').click(); + await page.getByText('All accounts').click(); + await onboardingExpect( page.getByText(DEFAULT_FIRST_ACCOUNT.accountName) ).toBeVisible(); @@ -38,8 +57,64 @@ onboarding.describe('Onboarding UI: recover secret phrase flow', () => { ).toBeVisible(); } ); + onboarding( - 'should recover account via 12 words secret phrase', + 'should recover two accounts via 24 words secret phrase', + async ({ page, createOnboardingPassword, extensionId }) => { + await createOnboardingPassword(); + + await page + .getByRole('button', { + name: 'Import an existing secret recovery phrase' + }) + .click(); + + await onboardingExpect( + page.getByText('Please enter your secret recovery phrase') + ).toBeVisible(); + + await page + .getByPlaceholder('e.g. Bobcat Lemon Blanket…') + .fill(twentyFourWordsSecretPhrase); + + await page.getByRole('button', { name: 'Next' }).click(); + + await onboardingExpect( + page.getByText('Select accounts to recover') + ).toBeVisible(); + + await onboardingExpect( + page.getByRole('button', { name: 'Recover selected accounts' }) + ).toBeDisabled(); + + await page.getByTestId('select-account-0').click(); + await page.getByTestId('select-account-1').click(); + + await page + .getByRole('button', { name: 'Recover selected accounts' }) + .click(); + + await page.goto(`chrome-extension://${extensionId}/popup.html`); + + await page.getByTestId('menu-open-icon').click(); + await page.getByText('All accounts').click(); + + await onboardingExpect( + page.getByText(DEFAULT_FIRST_ACCOUNT.accountName) + ).toBeVisible(); + await onboardingExpect( + page.getByText(DEFAULT_FIRST_ACCOUNT.truncatedPublicKey).nth(0) + ).toBeVisible(); + await onboardingExpect( + page.getByText(DEFAULT_SECOND_ACCOUNT.accountName) + ).toBeVisible(); + await onboardingExpect( + page.getByText(DEFAULT_SECOND_ACCOUNT.truncatedPublicKey).nth(0) + ).toBeVisible(); + } + ); + onboarding( + 'should recover one account via 12 words secret phrase', async ({ page, createOnboardingPassword, extensionId }) => { await createOnboardingPassword(); @@ -57,16 +132,102 @@ onboarding.describe('Onboarding UI: recover secret phrase flow', () => { .getByPlaceholder('e.g. Bobcat Lemon Blanket…') .fill(twelveWordsSecretPhrase); - await page.getByRole('button', { name: 'Recover my wallet' }).click(); + await page.getByRole('button', { name: 'Next' }).click(); + + await onboardingExpect( + page.getByText('Select accounts to recover') + ).toBeVisible(); + + await onboardingExpect( + page.getByRole('button', { name: 'Recover selected accounts' }) + ).toBeDisabled(); + + await page.getByTestId('select-account-0').click(); + + await onboardingExpect( + page.getByRole('button', { name: 'Recover selected accounts' }) + ).not.toBeDisabled(); + + await page + .getByRole('button', { name: 'Recover selected accounts' }) + .click(); + + await page.goto(`chrome-extension://${extensionId}/popup.html`); + + await page.getByTestId('menu-open-icon').click(); + await page.getByText('All accounts').click(); + + await onboardingExpect( + page.getByText(RECOVER_FIRST_ACCOUNT_FROM_TWELVE_WORDS.accountName) + ).toBeVisible(); + await onboardingExpect( + page + .getByText(RECOVER_FIRST_ACCOUNT_FROM_TWELVE_WORDS.truncatedPublicKey) + .nth(0) + ).toBeVisible(); + } + ); + onboarding( + 'should recover two accounts via 12 words secret phrase', + async ({ page, createOnboardingPassword, extensionId }) => { + await createOnboardingPassword(); + + await page + .getByRole('button', { + name: 'Import an existing secret recovery phrase' + }) + .click(); + + await onboardingExpect( + page.getByText('Please enter your secret recovery phrase') + ).toBeVisible(); + + await page + .getByPlaceholder('e.g. Bobcat Lemon Blanket…') + .fill(twelveWordsSecretPhrase); + + await page.getByRole('button', { name: 'Next' }).click(); + + await onboardingExpect( + page.getByText('Select accounts to recover') + ).toBeVisible(); + + await onboardingExpect( + page.getByRole('button', { name: 'Recover selected accounts' }) + ).toBeDisabled(); + + await page.getByTestId('select-account-0').click(); + await page.getByTestId('select-account-1').click(); + + await onboardingExpect( + page.getByRole('button', { name: 'Recover selected accounts' }) + ).not.toBeDisabled(); + + await page + .getByRole('button', { name: 'Recover selected accounts' }) + .click(); await page.goto(`chrome-extension://${extensionId}/popup.html`); + await page.getByTestId('menu-open-icon').click(); + await page.getByText('All accounts').click(); + + await onboardingExpect( + page.getByText(RECOVER_FIRST_ACCOUNT_FROM_TWELVE_WORDS.accountName) + ).toBeVisible(); + await onboardingExpect( + page + .getByText(RECOVER_FIRST_ACCOUNT_FROM_TWELVE_WORDS.truncatedPublicKey) + .nth(0) + ).toBeVisible(); await onboardingExpect( - page.getByText(RECOVER_ACCOUNT_FROM_TWELVE_WORDS.accountName) + page.getByText(RECOVER_SECOND_ACCOUNT_FROM_TWELVE_WORDS.accountName) ).toBeVisible(); await onboardingExpect( page - .getByText(RECOVER_ACCOUNT_FROM_TWELVE_WORDS.truncatedPublicKey) + .getByText( + RECOVER_SECOND_ACCOUNT_FROM_TWELVE_WORDS.truncatedPublicKey + ) .nth(0) ).toBeVisible(); } diff --git a/e2e-tests/popup/buy-cspr/buy-cspr.spec.ts b/e2e-tests/popup/buy-cspr/buy-cspr.spec.ts index 420f74246..6cd24a872 100644 --- a/e2e-tests/popup/buy-cspr/buy-cspr.spec.ts +++ b/e2e-tests/popup/buy-cspr/buy-cspr.spec.ts @@ -43,7 +43,6 @@ popup.describe('Popup UI: buy cspr', () => { await new Promise(r => setTimeout(r, 2000)); - console.log(torusPage.url()); popupExpect(torusPage.url()).toContain('https://app.topperpay.com/'); } ); @@ -90,7 +89,6 @@ popup.describe('Popup UI: buy cspr', () => { await new Promise(r => setTimeout(r, 2000)); - console.log(rampPage.url()); popupExpect(rampPage.url()).toContain('https://app.ramp.network/'); } ); @@ -117,6 +115,9 @@ popup.describe('Popup UI: buy cspr', () => { await popupPage.getByText('Ukraine').click(); + // wait until a modal window closed + await new Promise(r => setTimeout(r, 2000)); + await popupPage.getByRole('button', { name: 'Next' }).click(); await popupExpect( @@ -128,6 +129,9 @@ popup.describe('Popup UI: buy cspr', () => { await popupPage.getByText('UAH').click(); + // wait until a modal window closed + await new Promise(r => setTimeout(r, 2000)); + await popupPage.getByRole('button', { name: 'Next' }).click(); await popupExpect( diff --git a/e2e-tests/popup/signature-request-scenarios/signature-request-scenarios.spec.ts b/e2e-tests/popup/signature-request-scenarios/signature-request-scenarios.spec.ts index 26707e9a3..7d8929f11 100644 --- a/e2e-tests/popup/signature-request-scenarios/signature-request-scenarios.spec.ts +++ b/e2e-tests/popup/signature-request-scenarios/signature-request-scenarios.spec.ts @@ -1,8 +1,8 @@ import { DEFAULT_FIRST_ACCOUNT, - NEW_VALIDATOR, + NEW_VALIDATOR_FOR_SIGNATURE_REQUEST, PLAYGROUND_URL, - VALIDATOR + VALIDATOR_FOR_SIGNATURE_REQUEST } from '../../constants'; import { popup, popupExpect } from '../../fixtures'; @@ -35,7 +35,9 @@ popup.describe('Popup UI: signature request scenarios', () => { signatureRequestPage.getByText('Recipient (Key)') ).toBeVisible(); await popupExpect( - signatureRequestPage.getByText(VALIDATOR.truncatedPublicKey) + signatureRequestPage.getByText( + VALIDATOR_FOR_SIGNATURE_REQUEST.truncatedPublicKey + ) ).toBeVisible(); await popupExpect(signatureRequestPage.getByText('Amount')).toBeVisible(); await popupExpect(signatureRequestPage.getByText('2.5 CSPR')).toBeVisible(); @@ -81,10 +83,12 @@ popup.describe('Popup UI: signature request scenarios', () => { .nth(2) ).toBeVisible(); await popupExpect( - signatureRequestPage.getByText(VALIDATOR.name) + signatureRequestPage.getByText(VALIDATOR_FOR_SIGNATURE_REQUEST.name) ).toBeVisible(); await popupExpect( - signatureRequestPage.getByText(VALIDATOR.truncatedPublicKey) + signatureRequestPage.getByText( + VALIDATOR_FOR_SIGNATURE_REQUEST.truncatedPublicKey + ) ).toBeVisible(); await popupExpect(signatureRequestPage.getByText('Amount')).toBeVisible(); await popupExpect( @@ -130,10 +134,12 @@ popup.describe('Popup UI: signature request scenarios', () => { .nth(2) ).toBeVisible(); await popupExpect( - signatureRequestPage.getByText(VALIDATOR.name) + signatureRequestPage.getByText(VALIDATOR_FOR_SIGNATURE_REQUEST.name) ).toBeVisible(); await popupExpect( - signatureRequestPage.getByText(VALIDATOR.truncatedPublicKey) + signatureRequestPage.getByText( + VALIDATOR_FOR_SIGNATURE_REQUEST.truncatedPublicKey + ) ).toBeVisible(); await popupExpect(signatureRequestPage.getByText('Amount')).toBeVisible(); await popupExpect(signatureRequestPage.getByText('2.5 CSPR')).toBeVisible(); @@ -177,18 +183,24 @@ popup.describe('Popup UI: signature request scenarios', () => { .nth(2) ).toBeVisible(); await popupExpect( - signatureRequestPage.getByText(VALIDATOR.name, { exact: true }) + signatureRequestPage.getByText(VALIDATOR_FOR_SIGNATURE_REQUEST.name, { + exact: true + }) ).toBeVisible(); await popupExpect( - signatureRequestPage.getByText(VALIDATOR.truncatedPublicKey) + signatureRequestPage.getByText( + VALIDATOR_FOR_SIGNATURE_REQUEST.truncatedPublicKey + ) ).toBeVisible(); await popupExpect(signatureRequestPage.getByText('Amount')).toBeVisible(); await popupExpect(signatureRequestPage.getByText('2.5 CSPR')).toBeVisible(); await popupExpect( - signatureRequestPage.getByText(NEW_VALIDATOR.name) + signatureRequestPage.getByText(NEW_VALIDATOR_FOR_SIGNATURE_REQUEST.name) ).toBeVisible(); await popupExpect( - signatureRequestPage.getByText(NEW_VALIDATOR.truncatedPublicKey) + signatureRequestPage.getByText( + NEW_VALIDATOR_FOR_SIGNATURE_REQUEST.truncatedPublicKey + ) ).toBeVisible(); page.on('dialog', async dialog => { diff --git a/e2e-tests/popup/stakes/delegate.spec.ts b/e2e-tests/popup/stakes/delegate.spec.ts new file mode 100644 index 000000000..01a2bc196 --- /dev/null +++ b/e2e-tests/popup/stakes/delegate.spec.ts @@ -0,0 +1,64 @@ +import { RPC_RESPONSE, URLS, VALIDATOR_FOR_STAKE } from '../../constants'; +import { popup, popupExpect } from '../../fixtures'; + +popup.describe('Popup UI: Delegation', () => { + popup( + 'should made a successful delegation', + async ({ popupPage, unlockVault }) => { + await unlockVault(); + + await popupPage.route(URLS.rpc, route => + route.fulfill(RPC_RESPONSE.success) + ); + + await popupPage.getByText('More').click(); + + await popupPage.getByText('Delegate', { exact: true }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Delegate' }) + ).toBeVisible(); + + await popupPage + .getByPlaceholder('Validator public address', { exact: true }) + .fill(VALIDATOR_FOR_STAKE.publicKey); + + await new Promise(r => setTimeout(r, 2000)); + + await popupPage + .getByText(VALIDATOR_FOR_STAKE.truncatedPublicKey, { exact: true }) + .click(); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Delegate amount' }) + ).toBeVisible(); + + await popupPage.getByPlaceholder('0.00', { exact: true }).fill('500'); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + // Scroll to the bottom + await popupPage.evaluate(() => { + const container = document.querySelector('#ms-container'); + + container?.scrollTo(0, 1000); + }); + + await popupExpect( + popupPage.getByRole('button', { name: 'Confirm delegation' }) + ).not.toBeDisabled(); + + await popupPage + .getByRole('button', { name: 'Confirm delegation' }) + .click(); + + await popupExpect( + popupPage.getByRole('heading', { + name: 'You’ve submitted a delegation' + }) + ).toBeVisible(); + } + ); +}); diff --git a/e2e-tests/popup/stakes/redelagation.spec.ts b/e2e-tests/popup/stakes/redelagation.spec.ts new file mode 100644 index 000000000..dc7b53f52 --- /dev/null +++ b/e2e-tests/popup/stakes/redelagation.spec.ts @@ -0,0 +1,95 @@ +import { + NEW_VALIDATOR_FOR_STAKE, + RPC_RESPONSE, + URLS, + VALIDATOR_FOR_STAKE +} from '../../constants'; +import { popup, popupExpect } from '../../fixtures'; + +popup.describe('Popup UI: Redelegation', () => { + popup( + 'should made a successful redelegation', + async ({ popupPage, unlockVault }) => { + await unlockVault(); + + await popupPage.route(URLS.rpc, route => + route.fulfill(RPC_RESPONSE.success) + ); + + await popupPage.getByText('More').click(); + + await popupPage.getByText('Redelegate', { exact: true }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Redelegate' }) + ).toBeVisible(); + + await new Promise(r => setTimeout(r, 2000)); + + await popupPage + .getByText(VALIDATOR_FOR_STAKE.truncatedPublicKey, { exact: true }) + .click(); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Redelegate amount' }) + ).toBeVisible(); + + await popupPage.getByPlaceholder('0.00', { exact: true }).fill('500'); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupPage.getByText('Delegate', { exact: true }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Delegate' }) + ).toBeVisible(); + + await popupPage + .getByPlaceholder('Validator public address', { exact: true }) + .fill(NEW_VALIDATOR_FOR_STAKE.publicKey); + + await new Promise(r => setTimeout(r, 2000)); + + await popupPage + .getByText(NEW_VALIDATOR_FOR_STAKE.truncatedPublicKey, { exact: true }) + .click(); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('button', { name: 'Confirm redelegation' }) + ).toBeDisabled(); + + await popupExpect( + popupPage.getByText(VALIDATOR_FOR_STAKE.publicKey) + ).toBeVisible(); + + await popupExpect( + popupPage.getByText(NEW_VALIDATOR_FOR_STAKE.publicKey) + ).toBeVisible(); + + // Scroll to the bottom + await popupPage.evaluate(() => { + const container = document.querySelector('#ms-container'); + + container?.scrollTo(0, 1000); + }); + + await popupExpect( + popupPage.getByRole('button', { name: 'Confirm redelegation' }) + ).not.toBeDisabled(); + + await popupPage + .getByRole('button', { name: 'Confirm redelegation' }) + .click(); + + await popupExpect( + popupPage.getByRole('heading', { + name: 'You’ve submitted a redelegation' + }) + ).toBeVisible(); + } + ); +}); diff --git a/e2e-tests/popup/stakes/undelegate.spec.ts b/e2e-tests/popup/stakes/undelegate.spec.ts new file mode 100644 index 000000000..7ff608e24 --- /dev/null +++ b/e2e-tests/popup/stakes/undelegate.spec.ts @@ -0,0 +1,64 @@ +import { RPC_RESPONSE, URLS, VALIDATOR_FOR_STAKE } from '../../constants'; +import { popup, popupExpect } from '../../fixtures'; + +popup.describe('Popup UI: Undelegation', () => { + popup( + 'should made a successful undelegation', + async ({ popupPage, unlockVault }) => { + await unlockVault(); + + await popupPage.route(URLS.rpc, route => + route.fulfill(RPC_RESPONSE.success) + ); + + await popupPage.getByText('More').click(); + + await popupPage.getByText('Undelegate', { exact: true }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Undelegate' }) + ).toBeVisible(); + + await new Promise(r => setTimeout(r, 2000)); + + await popupPage + .getByText(VALIDATOR_FOR_STAKE.truncatedPublicKey, { exact: true }) + .click(); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Undelegate amount' }) + ).toBeVisible(); + + await popupPage.getByPlaceholder('0.00', { exact: true }).fill('500'); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByText(VALIDATOR_FOR_STAKE.publicKey) + ).toBeVisible(); + + // Scroll to the bottom + await popupPage.evaluate(() => { + const container = document.querySelector('#ms-container'); + + container?.scrollTo(0, 1000); + }); + + await popupExpect( + popupPage.getByRole('button', { name: 'Confirm undelegation' }) + ).not.toBeDisabled(); + + await popupPage + .getByRole('button', { name: 'Confirm undelegation' }) + .click(); + + await popupExpect( + popupPage.getByRole('heading', { + name: 'You’ve submitted an undelegation' + }) + ).toBeVisible(); + } + ); +}); diff --git a/e2e-tests/popup/transfers/casper-native-transfer.spec.ts b/e2e-tests/popup/transfers/casper-native-transfer.spec.ts new file mode 100644 index 000000000..75c2103b2 --- /dev/null +++ b/e2e-tests/popup/transfers/casper-native-transfer.spec.ts @@ -0,0 +1,146 @@ +import { DEFAULT_SECOND_ACCOUNT, RPC_RESPONSE, URLS } from '../../constants'; +import { popup, popupExpect } from '../../fixtures'; + +popup.describe('Popup UI: Casper Native Transfer', () => { + popup( + 'should made a successful transfer', + async ({ popupPage, unlockVault }) => { + await unlockVault(); + + await popupPage.route(URLS.rpc, route => + route.fulfill(RPC_RESPONSE.success) + ); + + await new Promise(r => setTimeout(r, 5000)); + + await popupPage.getByText('Send').click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Select token and account' }) + ).toBeVisible(); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Specify recipient' }) + ).toBeVisible(); + + await popupExpect( + popupPage.getByRole('button', { name: 'Next' }) + ).toBeDisabled(); + + await popupPage + .getByPlaceholder('Public key', { exact: true }) + .fill(DEFAULT_SECOND_ACCOUNT.publicKey); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Enter amount' }) + ).toBeVisible(); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Confirm sending' }) + ).toBeVisible(); + + await popupExpect( + popupPage.getByText(DEFAULT_SECOND_ACCOUNT.publicKey) + ).toBeVisible(); + + await popupExpect( + popupPage.getByRole('button', { name: 'Confirm send' }) + ).toBeDisabled(); + + // Scroll to the bottom + await popupPage.evaluate(() => { + const container = document.querySelector('#ms-container'); + + container?.scrollTo(0, 1000); + }); + + await popupExpect( + popupPage.getByRole('button', { name: 'Confirm send' }) + ).not.toBeDisabled(); + + await popupPage.getByRole('button', { name: 'Confirm send' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'You submitted a transaction' }) + ).toBeVisible(); + + await popupPage.getByRole('button', { name: 'Done' }).click(); + } + ); + + popup('should made a failed transfer', async ({ popupPage, unlockVault }) => { + await unlockVault(); + + await popupPage.route(URLS.rpc, route => + route.fulfill(RPC_RESPONSE.failure) + ); + + await new Promise(r => setTimeout(r, 5000)); + + await popupPage.getByText('Send').click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Select token and account' }) + ).toBeVisible(); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Specify recipient' }) + ).toBeVisible(); + + await popupExpect( + popupPage.getByRole('button', { name: 'Next' }) + ).toBeDisabled(); + + await popupPage + .getByPlaceholder('Public key', { exact: true }) + .fill(DEFAULT_SECOND_ACCOUNT.publicKey); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Enter amount' }) + ).toBeVisible(); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Confirm sending' }) + ).toBeVisible(); + + await popupExpect( + popupPage.getByText(DEFAULT_SECOND_ACCOUNT.publicKey) + ).toBeVisible(); + + await popupExpect( + popupPage.getByRole('button', { name: 'Confirm send' }) + ).toBeDisabled(); + + // Scroll to the bottom + await popupPage.evaluate(() => { + const container = document.querySelector('#ms-container'); + + container?.scrollTo(0, 1000); + }); + + await popupExpect( + popupPage.getByRole('button', { name: 'Confirm send' }) + ).not.toBeDisabled(); + + await popupPage.getByRole('button', { name: 'Confirm send' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Error message' }) + ).toBeVisible(); + await popupExpect(popupPage.getByText('Error description')).toBeVisible(); + + await popupPage.getByRole('button', { name: 'Close' }).click(); + }); +}); diff --git a/e2e-tests/popup/transfers/erc-20-transfer.spec.ts b/e2e-tests/popup/transfers/erc-20-transfer.spec.ts new file mode 100644 index 000000000..7bfd4ae8e --- /dev/null +++ b/e2e-tests/popup/transfers/erc-20-transfer.spec.ts @@ -0,0 +1,185 @@ +import { DEFAULT_SECOND_ACCOUNT } from '../../constants'; +import { popup, popupExpect } from '../../fixtures'; + +popup.describe('Popup UI: ERC-20 transfer', () => { + popup( + 'should made a successful transfer', + async ({ unlockVault, popupPage }) => { + await unlockVault(); + + await popupPage.route('https://node.testnet.cspr.cloud/rpc', route => + route.fulfill({ + status: 200, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1717761373590, + result: { + api_version: '1.5.6', + deploy_hash: 'deploy hash' + } + }) + }) + ); + + await new Promise(r => setTimeout(r, 5000)); + + await popupPage.getByText('Send').click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Select token and account' }) + ).toBeVisible(); + + await popupPage.getByTestId('token-row').click(); + + await popupPage.getByText('CSPR.click tests').click(); + + // wait until a modal window closed + await new Promise(r => setTimeout(r, 2000)); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Specify recipient' }) + ).toBeVisible(); + + await popupExpect( + popupPage.getByRole('button', { name: 'Next' }) + ).toBeDisabled(); + + await popupPage + .getByPlaceholder('Public key', { exact: true }) + .fill(DEFAULT_SECOND_ACCOUNT.publicKey); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Enter amount' }) + ).toBeVisible(); + + await popupPage.getByPlaceholder('0.00', { exact: true }).fill('10'); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Confirm sending' }) + ).toBeVisible(); + + await popupExpect( + popupPage.getByText(DEFAULT_SECOND_ACCOUNT.publicKey) + ).toBeVisible(); + + await popupExpect( + popupPage.getByRole('button', { name: 'Confirm send' }) + ).toBeDisabled(); + + // Scroll to the bottom + await popupPage.evaluate(() => { + const container = document.querySelector('#ms-container'); + + container?.scrollTo(0, 1000); + }); + + await popupExpect( + popupPage.getByRole('button', { name: 'Confirm send' }) + ).not.toBeDisabled(); + + await popupPage.getByRole('button', { name: 'Confirm send' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'You submitted a transaction' }) + ).toBeVisible(); + + await popupPage.getByRole('button', { name: 'Done' }).click(); + } + ); + + popup('should made a failed transfer', async ({ unlockVault, popupPage }) => { + await unlockVault(); + + await popupPage.route('https://node.testnet.cspr.cloud/rpc', route => + route.fulfill({ + status: 500, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1717761373590, + error: { + code: '', + data: 'Error description', + message: 'Error message' + } + }) + }) + ); + + await new Promise(r => setTimeout(r, 5000)); + + await popupPage.getByText('Send').click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Select token and account' }) + ).toBeVisible(); + + await popupPage.getByTestId('token-row').click(); + + await popupPage.getByText('CSPR.click tests').click(); + + // wait until a modal window closed + await new Promise(r => setTimeout(r, 2000)); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Specify recipient' }) + ).toBeVisible(); + + await popupExpect( + popupPage.getByRole('button', { name: 'Next' }) + ).toBeDisabled(); + + await popupPage + .getByPlaceholder('Public key', { exact: true }) + .fill(DEFAULT_SECOND_ACCOUNT.publicKey); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Enter amount' }) + ).toBeVisible(); + + await popupPage.getByPlaceholder('0.00', { exact: true }).fill('10'); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Confirm sending' }) + ).toBeVisible(); + + await popupExpect( + popupPage.getByText(DEFAULT_SECOND_ACCOUNT.publicKey) + ).toBeVisible(); + + await popupExpect( + popupPage.getByRole('button', { name: 'Confirm send' }) + ).toBeDisabled(); + + // Scroll to the bottom + await popupPage.evaluate(() => { + const container = document.querySelector('#ms-container'); + + container?.scrollTo(0, 1000); + }); + + await popupExpect( + popupPage.getByRole('button', { name: 'Confirm send' }) + ).not.toBeDisabled(); + + await popupPage.getByRole('button', { name: 'Confirm send' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Error message' }) + ).toBeVisible(); + await popupExpect(popupPage.getByText('Error description')).toBeVisible(); + + await popupPage.getByRole('button', { name: 'Close' }).click(); + }); +}); diff --git a/e2e-tests/popup/transfers/nft-transfer.spec.ts b/e2e-tests/popup/transfers/nft-transfer.spec.ts new file mode 100644 index 000000000..3a2c5eb98 --- /dev/null +++ b/e2e-tests/popup/transfers/nft-transfer.spec.ts @@ -0,0 +1,65 @@ +import { DEFAULT_SECOND_ACCOUNT, RPC_RESPONSE, URLS } from '../../constants'; +import { popup, popupExpect } from '../../fixtures'; + +popup.describe('Popup UI: NFT Transfer', () => { + popup( + 'should made a successful nft transfer', + async ({ popupPage, unlockVault }) => { + await unlockVault(); + + await popupPage.route(URLS.rpc, route => + route.fulfill(RPC_RESPONSE.success) + ); + + await new Promise(r => setTimeout(r, 2000)); + + await popupPage.getByText('NFTs').click(); + + await new Promise(r => setTimeout(r, 2000)); + + await popupPage.getByTestId('nft-token-card').click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'west' }) + ).toBeVisible(); + + await popupPage.getByText('Send').click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Review details' }) + ).toBeVisible(); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Select recipient' }) + ).toBeVisible(); + + await popupExpect( + popupPage.getByRole('button', { name: 'Next' }) + ).toBeDisabled(); + + await popupPage + .getByPlaceholder('Public key', { exact: true }) + .fill(DEFAULT_SECOND_ACCOUNT.publicKey); + + await popupPage.getByRole('button', { name: 'Next' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'Confirm sending' }) + ).toBeVisible(); + + await popupExpect( + popupPage.getByText(DEFAULT_SECOND_ACCOUNT.publicKey) + ).toBeVisible(); + + await popupPage.getByRole('button', { name: 'Confirm send' }).click(); + + await popupExpect( + popupPage.getByRole('heading', { name: 'You submitted a transaction' }) + ).toBeVisible(); + + await popupPage.getByRole('button', { name: 'Done' }).click(); + } + ); +}); diff --git a/package-lock.json b/package-lock.json index 52f51f57e..0d8b27cda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "Casper Wallet", - "version": "1.10.0", + "version": "1.11.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "Casper Wallet", - "version": "1.10.0", + "version": "1.11.0", "dependencies": { "@formatjs/intl": "2.6.2", "@hookform/resolvers": "2.9.10", @@ -62,7 +62,7 @@ "@babel/preset-react": "7.18.6", "@babel/preset-typescript": "^7.23.3", "@playwright/test": "^1.39.0", - "@redux-devtools/cli": "^3.0.1", + "@redux-devtools/cli": "^4.0.0", "@redux-devtools/remote": "^0.9.1", "@testing-library/dom": "9.3.4", "@testing-library/jest-dom": "^6.4.2", @@ -87,7 +87,7 @@ "css-loader": "6.8.1", "eslint": "8.49.0", "eslint-config-airbnb": "^19.0.4", - "eslint-config-prettier": "^8.3.0", + "eslint-config-prettier": "^9.1.0", "eslint-config-react-app": "^7.0.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-flowtype": "^8.0.3", @@ -99,7 +99,7 @@ "file-loader": "^6.2.0", "fs-extra": "11.1.1", "html-loader": "4.2.0", - "html-webpack-plugin": "^5.5.3", + "html-webpack-plugin": "^5.6.0", "husky": "8.0.2", "i18next-conv": "14.0.0", "jest": "29.3.1", @@ -187,9 +187,9 @@ } }, "node_modules/@apollo/server": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.9.3.tgz", - "integrity": "sha512-U56Sx/UmzR3Es344hQ/Ptf2EJrH+kV4ZPoLmgGjWoiwf2wYQ/pRSvkSXgjOvoyE34wSa8Gh7f92ljfLfY+6q1w==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.9.5.tgz", + "integrity": "sha512-eDBfArYbZaTm1AGa82M1aL7lOscVhnZsH85+OWmHMIR98qntzEjNpWpQPYDTru63Qxs4kHcY29NUx/kMGZfGEA==", "dev": true, "dependencies": { "@apollo/cache-control-types": "^1.0.3", @@ -2787,9 +2787,9 @@ "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" }, "node_modules/@emotion/react": { - "version": "11.11.3", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz", - "integrity": "sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==", + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", "dev": true, "dependencies": { "@babel/runtime": "^7.18.3", @@ -5027,83 +5027,84 @@ "dev": true }, "node_modules/@redux-devtools/cli": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@redux-devtools/cli/-/cli-3.0.1.tgz", - "integrity": "sha512-NARxbTvLQQkZ4if5zvQTkTyesKCsu9DpN9Uupz1Ib18lUEuuEYl3HKJFYQ/hScnxxMSin7KN8j8guHMVD16pqg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@redux-devtools/cli/-/cli-4.0.0.tgz", + "integrity": "sha512-MACJmhnNS8Q0P8ldYgH+p7//U6QdetQqnIS5KZRzd8sp4rYSegzTqsQuf4eGZ4PSzrVOoVezj9eH6ESZFm9/9Q==", "dev": true, "dependencies": { - "@apollo/server": "^4.9.1", - "@redux-devtools/app": "^3.0.0", - "@types/react": "^18.2.20", + "@apollo/server": "^4.9.5", + "@emotion/react": "^11.11.3", + "@redux-devtools/app": "^6.0.0", + "@types/react": "^18.2.46", "body-parser": "^1.20.2", "chalk": "^5.3.0", "cors": "^2.8.5", "cross-spawn": "^7.0.3", - "electron": "^25.6.0", + "electron": "^27.2.0", "express": "^4.18.2", "get-port": "^7.0.0", - "graphql": "^16.8.0", - "knex": "^2.5.1", + "graphql": "^16.8.1", + "knex": "^3.1.0", "lodash-es": "^4.17.21", "minimist": "^1.2.8", "morgan": "^1.10.0", - "open": "^9.1.0", + "open": "^10.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-is": "^18.2.0", "semver": "^7.5.4", - "socketcluster-server": "^17.4.1", + "socketcluster-server": "^19.0.1", "sqlite3": "^5.1.6", "styled-components": "^5.3.11", - "uuid": "^9.0.0" + "uuid": "^9.0.1" }, "bin": { "redux-devtools": "bin/redux-devtools.js" }, "engines": { - "node": "^16.13.0 || >= 18.12.0" + "node": ">= 18.12.0" } }, "node_modules/@redux-devtools/cli/node_modules/@babel/code-frame": { - "version": "8.0.0-alpha.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0-alpha.6.tgz", - "integrity": "sha512-w29LUiFqVMe+Ybp/+xtQKAp6rpxjHxtCW909I0OMHvFKzrGB0K+1yQGRzXhTqrd4f7ZRRgzkAxdr0EOEbHt3dA==", + "version": "8.0.0-alpha.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0-alpha.8.tgz", + "integrity": "sha512-PaXRlxddNNx+S+OZ/laLOqhiqaDgBQCvxR/Z0CHcpXgPRmtBNbRxvWhf91+YtST1O+O6j2K0F4bb5DvA3Hr4gQ==", "dev": true, "dependencies": { - "@babel/highlight": "^8.0.0-alpha.6", - "chalk": "^5.3.0" + "@babel/highlight": "^8.0.0-alpha.8", + "picocolors": "^1.0.0" }, "engines": { "node": "^16.20.0 || ^18.16.0 || >=20.0.0" } }, "node_modules/@redux-devtools/cli/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.0-alpha.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.0-alpha.6.tgz", - "integrity": "sha512-a0YnAx4TJCgds07M2v0A3WeyZUfls2YOgNHCb1akGKlz5KdmTCqf2N2hw86YhjY6oDqb6Omb8gFKz/3TOKRevQ==", + "version": "8.0.0-alpha.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.0-alpha.8.tgz", + "integrity": "sha512-7uCkkKAnoPa864zeoJ82mhXNVhRtvVbquSZ+i98qG4jaUu3yRe3ZKWV1K075FTthM4gNgRVMWTK/KjrSuX9w+Q==", "dev": true, "engines": { "node": "^16.20.0 || ^18.16.0 || >=20.0.0" } }, "node_modules/@redux-devtools/cli/node_modules/@babel/highlight": { - "version": "8.0.0-alpha.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-8.0.0-alpha.6.tgz", - "integrity": "sha512-YzJ5blZXak7Gk6ZgV9a4B9bAkGzl594ZxmBaFFZkstA5hE1iOBTlUW4tkGKz2uomL7FLLW4qHJF3QjQLHKi7iw==", + "version": "8.0.0-alpha.8", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-8.0.0-alpha.8.tgz", + "integrity": "sha512-1xw7Z/AMD4ZJxxktv1sXClheNGab1BAU11SND6AiwUyhIAOVrvQ9QiOBd8I/iMXdXtANCNyu0YLswn3TNlcRZw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^8.0.0-alpha.6", - "chalk": "^5.3.0", - "js-tokens": "^8.0.0" + "@babel/helper-validator-identifier": "^8.0.0-alpha.8", + "js-tokens": "^8.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": "^16.20.0 || ^18.16.0 || >=20.0.0" } }, "node_modules/@redux-devtools/cli/node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz", + "integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -5113,252 +5114,290 @@ } }, "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/app": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/app/-/app-3.0.0.tgz", - "integrity": "sha512-3mn+cj3KhMRSa0EPxlRD4EnfFFMBsM1nKJ+g/6zB4iRyk23befz4YzwFG3KXkwyVmZCooCu3DjHkQhUOx4xxQA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.22.10", - "@redux-devtools/chart-monitor": "^4.0.0", - "@redux-devtools/core": "^3.13.0", - "@redux-devtools/inspector-monitor": "^4.0.0", - "@redux-devtools/inspector-monitor-test-tab": "^2.0.0", - "@redux-devtools/inspector-monitor-trace-tab": "^2.0.0", - "@redux-devtools/log-monitor": "^4.0.2", - "@redux-devtools/rtk-query-monitor": "^3.1.1", - "@redux-devtools/slider-monitor": "^4.0.0", - "@redux-devtools/ui": "^1.3.0", - "@reduxjs/toolkit": "^1.9.5", - "@types/prop-types": "^15.7.5", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@redux-devtools/app/-/app-6.0.0.tgz", + "integrity": "sha512-jR6Nm/Pe/ImQ2XmJC1JIyVduQ/3J4bXQhtvoH/pw8WuVJrQvFzhfY5McS+LbHB6H+Vjfhq7mq3u1T2dcQ6II3g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.23.5", + "@redux-devtools/chart-monitor": "^5.0.0", + "@redux-devtools/core": "^4.0.0", + "@redux-devtools/inspector-monitor": "^6.0.0", + "@redux-devtools/inspector-monitor-test-tab": "^4.0.0", + "@redux-devtools/inspector-monitor-trace-tab": "^4.0.0", + "@redux-devtools/log-monitor": "^5.0.0", + "@redux-devtools/rtk-query-monitor": "^5.0.0", + "@redux-devtools/slider-monitor": "^5.0.0", + "@redux-devtools/ui": "^1.3.1", + "@reduxjs/toolkit": "^1.9.7", "d3-state-visualizer": "^2.0.0", "javascript-stringify": "^2.1.0", "jsan": "^3.1.14", "jsondiffpatch": "^0.5.0", "localforage": "^1.10.0", "lodash": "^4.17.21", - "prop-types": "^15.8.1", - "react-icons": "^4.10.1", + "react-icons": "^4.12.0", "react-is": "^18.2.0", - "react-redux": "^8.1.2", - "redux": "4.2.1", + "react-redux": "^8.1.3", + "redux": "^4.2.1", "redux-persist": "^6.0.0", "socketcluster-client": "^17.2.2" }, "peerDependencies": { + "@emotion/react": "^11.0.0", "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "@types/styled-components": "^5.1.26", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "@types/styled-components": "^5.1.34", + "react": "^16.8.4 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0", "styled-components": "^5.3.11" } }, - "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/chart-monitor": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/chart-monitor/-/chart-monitor-4.1.0.tgz", - "integrity": "sha512-fdW7DKEhCB9oumMTyhdrVGOgb/6lBioPveW3w0+u7MCZn2i32laZYzU9VT39sx+Fc2kwxzbYsLxFsU0tijGsXA==", + "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/app/node_modules/@redux-devtools/chart-monitor": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@redux-devtools/chart-monitor/-/chart-monitor-5.0.2.tgz", + "integrity": "sha512-5S70WvWufMa8ybwpFqxGqA65Mb8xV7e1gviKnGp6KGDn+EYo2bu+O+o5ugt/4WW4z4HgeXkNl3DawwjTUNc9aw==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.2", - "@types/redux-devtools-themes": "^1.0.3", - "d3-state-visualizer": "^2.0.0", + "@babel/runtime": "^7.24.1", + "d3-state-visualizer": "^3.0.0", "deepmerge": "^4.3.1", - "redux-devtools-themes": "^1.0.0" + "react-base16-styling": "^0.10.0" }, "peerDependencies": { - "@redux-devtools/core": "^3.0.0", - "@types/react": "^16.3.0 || ^17.0.0 || ^18.0.0", - "react": "^16.3.0 || ^17.0.0 || ^18.0.0", + "@redux-devtools/core": "^4.0.0", + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.4 || ^17.0.0 || ^18.0.0", "redux": "^3.4.0 || ^4.0.0 || ^5.0.0" } }, - "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/core": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/core/-/core-3.14.0.tgz", - "integrity": "sha512-OMPflPPCXR9L1rpfd7gwY31/EuqPyE9Of/5wZgDDzeisaENY5h/EfnAjnHRKr7NIx/yUIUX2DJs8NpmUOnANMg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.23.2", - "@redux-devtools/instrument": "^2.2.0", - "@types/prop-types": "^15.7.10", - "lodash": "^4.17.21", - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "react": "^0.14.9 || ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", - "react-redux": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", - "redux": "^3.5.2 || ^4.0.0 || ^5.0.0" - } - }, - "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/inspector-monitor": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/inspector-monitor/-/inspector-monitor-4.1.0.tgz", - "integrity": "sha512-ga0rBdb8LeB7mN+X+/Pb0rx+yTPOsSHFFVxgcWag7XsBu+dXtujlRsCM++6LX6GjJGNzZAUsG4ZI4Wt0dG2CQg==", + "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/app/node_modules/@redux-devtools/chart-monitor/node_modules/d3-state-visualizer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-state-visualizer/-/d3-state-visualizer-3.0.0.tgz", + "integrity": "sha512-69syDmi4b6sr9/UJUVpKTzFFx7jeI8AogEWv2qL/AWKky9b9NgqgMtSBbJNQdncq6epsOKP7a2MDun90taHtTQ==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.2", - "@dnd-kit/core": "^6.1.0", - "@dnd-kit/modifiers": "^7.0.0", - "@dnd-kit/sortable": "^8.0.0", - "@dnd-kit/utilities": "^3.2.2", - "@types/lodash": "^4.14.201", - "@types/prop-types": "^15.7.10", - "@types/redux-devtools-themes": "^1.0.3", - "dateformat": "^5.0.3", - "hex-rgba": "^1.0.2", - "immutable": "^4.3.4", - "javascript-stringify": "^2.1.0", - "jsondiffpatch": "^0.5.0", - "jss": "^10.10.0", - "jss-preset-default": "^10.10.0", - "lodash.debounce": "^4.0.8", - "prop-types": "^15.8.1", - "react-base16-styling": "^0.9.1", - "react-json-tree": "^0.18.0", - "redux-devtools-themes": "^1.0.0" - }, - "peerDependencies": { - "@redux-devtools/core": "^3.0.0", - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", - "redux": "^3.4.0 || ^4.0.0 || ^5.0.0" + "@types/d3": "^7.4.3", + "d3": "^7.9.0", + "d3tooltip": "^4.0.0", + "deepmerge": "^4.3.1", + "map2tree": "^4.0.0", + "ramda": "^0.29.1" } }, - "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/inspector-monitor-test-tab": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/inspector-monitor-test-tab/-/inspector-monitor-test-tab-2.1.0.tgz", - "integrity": "sha512-wQ8dnB1F7RZn4XFoKYVPUPt996CIGWG1/ndQwGkXPvHg+P32QHUHsrhR+QDpn6Z1dMamycDjqijS2KDkQA/MDg==", + "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/app/node_modules/@redux-devtools/inspector-monitor-test-tab": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@redux-devtools/inspector-monitor-test-tab/-/inspector-monitor-test-tab-4.0.0.tgz", + "integrity": "sha512-a5SuX6eyFvBAK9UcRW/n5K8nSISWR+UGW0+fZh+KP20naSgkvagrzC705NX8riCfJc8fr5csIYVYwmypMAZ2UA==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.2", + "@babel/runtime": "^7.23.5", "@redux-devtools/ui": "^1.3.1", - "@types/prop-types": "^15.7.10", "es6template": "^1.0.5", "javascript-stringify": "^2.1.0", "jsan": "^3.1.14", "object-path": "^0.11.8", - "prop-types": "^15.8.1", - "react-icons": "^4.11.0", + "react-icons": "^4.12.0", "simple-diff": "^1.7.2" }, "peerDependencies": { - "@redux-devtools/inspector-monitor": "^4.0.0", + "@emotion/react": "^11.0.0", + "@redux-devtools/inspector-monitor": "^6.0.0", "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "@types/styled-components": "^5.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "@types/styled-components": "^5.1.34", + "react": "^16.8.4 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0", "redux": "^3.4.0 || ^4.0.0 || ^5.0.0", - "styled-components": "^5.0.0" + "styled-components": "^5.3.11" } }, - "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/inspector-monitor-trace-tab": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/inspector-monitor-trace-tab/-/inspector-monitor-trace-tab-2.1.0.tgz", - "integrity": "sha512-Sac8iyYahyrq3k76kty1ebMVCp7Q1ABdBdzj8vJ5FSiaXOKU+HY1y4YS6Fb9ra7Bj5udIqzr/zdxprdPc7Yatg==", + "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/app/node_modules/@redux-devtools/inspector-monitor-trace-tab": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@redux-devtools/inspector-monitor-trace-tab/-/inspector-monitor-trace-tab-4.0.1.tgz", + "integrity": "sha512-7tu/JU9wmpp74sBlBirgikrjJsx3IoJBKzUj9lO9ox9U9cJ/MhSo9B8ZAmLDI8EJa3CzxCcNQnDxW/Wucy17DQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^8.0.0-alpha.4", - "@babel/runtime": "^7.23.2", - "@types/chrome": "^0.0.251", + "@babel/code-frame": "^8.0.0-alpha.7", + "@babel/runtime": "^7.24.1", + "@types/chrome": "^0.0.263", "anser": "^2.1.1", - "html-entities": "^2.4.0", + "html-entities": "^2.5.2", "path-browserify": "^1.0.1", - "redux-devtools-themes": "^1.0.0", + "react-base16-styling": "^0.10.0", "source-map": "^0.5.7" }, "peerDependencies": { - "@redux-devtools/inspector-monitor": "^4.0.0", + "@emotion/react": "^11.11.4", + "@redux-devtools/inspector-monitor": "^6.0.0", "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.4 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0", "redux": "^3.4.0 || ^4.0.0 || ^5.0.0" } }, - "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/log-monitor": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/log-monitor/-/log-monitor-4.1.0.tgz", - "integrity": "sha512-2danca7yfQnuyzYxUVsEontmcRfQLFZgU8M33690JfvHlUV1OYjkNhbwKkMEso5py8sTd2EgoC2PGySoyupbUg==", + "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/app/node_modules/@redux-devtools/log-monitor": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@redux-devtools/log-monitor/-/log-monitor-5.0.1.tgz", + "integrity": "sha512-NCuKr9hdruL01rbghKs/eD2lLcKhXmICXf2js2gTM9oDA2662+X9M9BahcJwjhkTUBxdsXbDQ5d/uGvxr530Vg==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.2", + "@babel/runtime": "^7.24.1", "@types/lodash.debounce": "^4.0.9", - "@types/prop-types": "^15.7.10", - "@types/redux-devtools-themes": "^1.0.3", "lodash.debounce": "^4.0.8", - "prop-types": "^15.8.1", - "react-json-tree": "^0.18.0", - "redux-devtools-themes": "^1.0.0" + "react-base16-styling": "^0.10.0", + "react-json-tree": "^0.19.0" }, "peerDependencies": { - "@redux-devtools/core": "^3.0.0", - "@types/react": "^16.3.0 || ^17.0.0 || ^18.0.0", - "react": "^16.3.0 || ^17.0.0 || ^18.0.0", + "@redux-devtools/core": "^4.0.0", + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.4 || ^17.0.0 || ^18.0.0", "redux": "^3.4.0 || ^4.0.0 || ^5.0.0" } }, - "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/rtk-query-monitor": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/rtk-query-monitor/-/rtk-query-monitor-3.2.0.tgz", - "integrity": "sha512-Td7oZUAO/TyxillkC2E33vYqfKEDFilJfLHLg8Na7KdfG3Y0JR5qmu0xypZDODeEsA5teHpkMnhI3O9ew/JqdQ==", + "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/app/node_modules/@redux-devtools/rtk-query-monitor": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@redux-devtools/rtk-query-monitor/-/rtk-query-monitor-5.0.1.tgz", + "integrity": "sha512-mqYkEop8lgyswW4HxEDnPBsdxU/h3Rm+/q0KCbeuVSZU7Ek/gEgSnKAi++JvsTMPekgA1Uie94/kztOrB6Kv/w==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.2", - "@redux-devtools/ui": "^1.3.1", - "@types/lodash": "^4.14.201", - "@types/prop-types": "^15.7.10", - "@types/redux-devtools-themes": "^1.0.3", + "@babel/runtime": "^7.24.1", + "@redux-devtools/ui": "^1.3.2", + "@types/lodash": "^4.17.0", "hex-rgba": "^1.0.2", - "immutable": "^4.3.4", - "jss": "^10.10.0", - "jss-preset-default": "^10.10.0", + "immutable": "^4.3.5", "lodash.debounce": "^4.0.8", - "prop-types": "^15.8.1", - "react-base16-styling": "^0.9.1", - "react-json-tree": "^0.18.0", - "redux-devtools-themes": "^1.0.0" + "react-base16-styling": "^0.10.0", + "react-json-tree": "^0.19.0" }, "peerDependencies": { - "@redux-devtools/core": "^3.0.0", + "@emotion/react": "^11.11.4", + "@redux-devtools/core": "^4.0.0", "@reduxjs/toolkit": "^1.0.0 || ^2.0.0", - "@types/react": "^16.3.0 || ^17.0.0 || ^18.0.0", - "@types/styled-components": "^5.0.0", - "react": "^16.3.0 || ^17.0.0 || ^18.0.0", + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "@types/styled-components": "^5.1.34", + "react": "^16.8.4 || ^17.0.0 || ^18.0.0", "redux": "^3.4.0 || ^4.0.0 || ^5.0.0", - "styled-components": "^5.0.0" + "styled-components": "^5.3.11" } }, - "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/slider-monitor": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/slider-monitor/-/slider-monitor-4.1.0.tgz", - "integrity": "sha512-p0jDgLcmuOQyJTlMAwjSZ8wxioqV802IXtm7BsxGq9021WsmyhEGRhqfu4XvXUt95sBHNsRsm9yE5XVTMmlAtw==", + "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/app/node_modules/@redux-devtools/slider-monitor": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@redux-devtools/slider-monitor/-/slider-monitor-5.0.1.tgz", + "integrity": "sha512-ud04CLxJNMpNfXKrfHtt4i7LbYCi3PVdcrQgU0X3qeEUWiXkvumZJIs0DPKSMk3Ukqy/G06SMNL4aqy6icjkLQ==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.2", - "@redux-devtools/ui": "^1.3.1", - "@types/prop-types": "^15.7.10", - "@types/redux-devtools-themes": "^1.0.3", - "prop-types": "^15.8.1", - "redux-devtools-themes": "^1.0.0" + "@babel/runtime": "^7.24.1", + "@redux-devtools/ui": "^1.3.2", + "react-base16-styling": "^0.10.0" + }, + "peerDependencies": { + "@redux-devtools/core": "^4.0.0", + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "@types/styled-components": "^5.1.34", + "react": "^16.8.4 || ^17.0.0 || ^18.0.0", + "redux": "^3.4.0 || ^4.0.0 || ^5.0.0", + "styled-components": "^5.3.11" + } + }, + "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/inspector-monitor": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@redux-devtools/inspector-monitor/-/inspector-monitor-6.0.1.tgz", + "integrity": "sha512-FGUOrV3hEKrelqGSWmExKbBfXTEtLQ1yj+gLuPY2yAnZwFwOKzxybL5tsHCvVZsm/hgkA3WGcpYHtZ6+pJSWBg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.24.1", + "@dnd-kit/core": "^6.1.0", + "@dnd-kit/modifiers": "^7.0.0", + "@dnd-kit/sortable": "^8.0.0", + "@dnd-kit/utilities": "^3.2.2", + "@types/lodash": "^4.17.0", + "dateformat": "^5.0.3", + "hex-rgba": "^1.0.2", + "immutable": "^4.3.5", + "javascript-stringify": "^2.1.0", + "jsondiffpatch": "^0.6.0", + "lodash.debounce": "^4.0.8", + "react-base16-styling": "^0.10.0", + "react-json-tree": "^0.19.0" + }, + "peerDependencies": { + "@emotion/react": "^11.11.4", + "@redux-devtools/core": "^4.0.0", + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.4 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0", + "redux": "^3.4.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/inspector-monitor/node_modules/jsondiffpatch": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz", + "integrity": "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==", + "dev": true, + "dependencies": { + "@types/diff-match-patch": "^1.0.36", + "chalk": "^5.3.0", + "diff-match-patch": "^1.0.5" + }, + "bin": { + "jsondiffpatch": "bin/jsondiffpatch.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/ui": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@redux-devtools/ui/-/ui-1.3.2.tgz", + "integrity": "sha512-/g9FSzvOSCkTXibd+h13kRE433/Zv4KVImQaanJsl8fc0MEfBSyFtGE1wjeIkwqs322EVBxw3fPWaQAC1gpLHg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.24.1", + "@rjsf/core": "^4.2.3", + "@types/codemirror": "^5.60.15", + "@types/json-schema": "^7.0.15", + "@types/simple-element-resize-detector": "^1.3.3", + "codemirror": "^5.65.16", + "color": "^4.2.3", + "react-base16-styling": "^0.10.0", + "react-icons": "^5.0.1", + "react-select": "^5.8.0", + "simple-element-resize-detector": "^1.3.0" }, "peerDependencies": { - "@redux-devtools/core": "^3.0.0", "@types/react": "^16.3.0 || ^17.0.0 || ^18.0.0", - "@types/styled-components": "^5.0.0", + "@types/styled-components": "^5.1.34", "react": "^16.3.0 || ^17.0.0 || ^18.0.0", - "redux": "^3.4.0 || ^4.0.0 || ^5.0.0", - "styled-components": "^5.0.0" + "styled-components": "^5.3.11" + } + }, + "node_modules/@redux-devtools/cli/node_modules/@redux-devtools/ui/node_modules/react-icons": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==", + "dev": true, + "peerDependencies": { + "react": "*" } }, "node_modules/@redux-devtools/cli/node_modules/@types/chrome": { - "version": "0.0.251", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.251.tgz", - "integrity": "sha512-UF+yr0LEKWWGsKxQ5A3XOSF5SNoU1ctW3pXcWJPpT8OOUTEspYeaLU8spDKe+6xalXeMTS0TBrX1g0b6qlWmkw==", + "version": "0.0.263", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.263.tgz", + "integrity": "sha512-As0vzv99ov3M6ZR7R6VzhMWFZXkPMrFrCEXXVrMN576Cm70fTkj7Df2CF+qEo170JepX50pd11cX6O4DSAtl2Q==", "dev": true, "dependencies": { "@types/filesystem": "*", "@types/har-format": "*" } }, + "node_modules/@redux-devtools/cli/node_modules/async-stream-emitter": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/async-stream-emitter/-/async-stream-emitter-4.1.0.tgz", + "integrity": "sha512-cfPZYjHkhCdHSR+eux71vOU8+8Xb23oLyxccAjwYHgOxDb3+qSDb2HV1Y0Hmu39vZlse2cm15CUShLiVYXHCmQ==", + "dev": true, + "dependencies": { + "stream-demux": "^8.1.0" + } + }, "node_modules/@redux-devtools/cli/node_modules/body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", @@ -5383,6 +5422,21 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/@redux-devtools/cli/node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@redux-devtools/cli/node_modules/chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", @@ -5404,6 +5458,16 @@ "node": ">= 0.6" } }, + "node_modules/@redux-devtools/cli/node_modules/d3tooltip": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/d3tooltip/-/d3tooltip-4.0.0.tgz", + "integrity": "sha512-jQRmF04fL9G+sROeN1/CZaHRqE31Pl7Qky6qIJ2MkecTwXZtgfDgmgrPZ0VS5Ewnj9sYWJ6CAl6fYI4PNn2Fpw==", + "dev": true, + "peerDependencies": { + "@types/d3": "^7.4.3", + "d3": "^7.9.0" + } + }, "node_modules/@redux-devtools/cli/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -5413,6 +5477,34 @@ "ms": "2.0.0" } }, + "node_modules/@redux-devtools/cli/node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@redux-devtools/cli/node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@redux-devtools/cli/node_modules/define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", @@ -5425,12 +5517,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@redux-devtools/cli/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@redux-devtools/cli/node_modules/js-tokens": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz", "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==", "dev": true }, + "node_modules/@redux-devtools/cli/node_modules/map2tree": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/map2tree/-/map2tree-4.0.0.tgz", + "integrity": "sha512-nzoeFAjW5yCwLFpV3A6EZS3kHD6H9y82S0KANc8tN2Mha+Ad2vIggDqxxYdI8lgghHzQL3yf/6tqlcoplRw8NA==", + "dev": true, + "dependencies": { + "lodash-es": "^4.17.21" + } + }, "node_modules/@redux-devtools/cli/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -5438,23 +5554,33 @@ "dev": true }, "node_modules/@redux-devtools/cli/node_modules/open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/open/-/open-10.0.2.tgz", + "integrity": "sha512-GnYLdE+E3K8NeSE23N0g67/9q9AXRph5oTUbz6IbIgElPigEnQ2aHuqRge3y0JUr67qoc84xME5kF03fDc3fcA==", "dev": true, "dependencies": { - "default-browser": "^4.0.0", + "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" + "is-wsl": "^3.1.0" }, "engines": { - "node": ">=14.16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@redux-devtools/cli/node_modules/ramda": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", + "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, "node_modules/@redux-devtools/cli/node_modules/raw-body": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", @@ -5471,9 +5597,9 @@ } }, "node_modules/@redux-devtools/cli/node_modules/react-redux": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.2.tgz", - "integrity": "sha512-xJKYI189VwfsFc4CJvHqHlDrzyFTY/3vZACbE+rr/zQ34Xx1wQfB4OTOSeOSNrF6BDVe8OOdxIrAnMGXA3ggfw==", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", "dev": true, "dependencies": { "@babel/runtime": "^7.12.1", @@ -5510,11 +5636,23 @@ } }, "node_modules/@redux-devtools/cli/node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "dev": true }, + "node_modules/@redux-devtools/cli/node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@redux-devtools/cli/node_modules/sc-formatter": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/sc-formatter/-/sc-formatter-4.0.0.tgz", @@ -5565,6 +5703,16 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/@redux-devtools/cli/node_modules/stream-demux": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/stream-demux/-/stream-demux-8.1.0.tgz", + "integrity": "sha512-20vtOmAj2EVzQZKZVmfyio16u/3QOKSvg+0ldgZeS+m2FNI1vKFoqggamagsPCXufdZ1Tk8VvAM/HV/YUmRbSg==", + "dev": true, + "dependencies": { + "consumable-stream": "^2.0.0", + "writable-consumable-stream": "^3.0.1" + } + }, "node_modules/@redux-devtools/cli/node_modules/styled-components": { "version": "5.3.11", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", @@ -5608,10 +5756,19 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/@redux-devtools/cli/node_modules/writable-consumable-stream": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/writable-consumable-stream/-/writable-consumable-stream-3.0.1.tgz", + "integrity": "sha512-rAOJTA/sMgXD/X6eMwbQJe49w+Fnkdx3iV5oUzdmiZ7Bwx03khqUnAKIpzp/hbI8q2EP5NfjXgIXN0MsipfHeg==", + "dev": true, + "dependencies": { + "consumable-stream": "^2.0.0" + } + }, "node_modules/@redux-devtools/cli/node_modules/ws": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.1.tgz", - "integrity": "sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "engines": { "node": ">=10.0.0" @@ -5724,6 +5881,15 @@ "node": ">=6.9.0" } }, + "node_modules/@redux-devtools/remote/node_modules/async-stream-emitter": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/async-stream-emitter/-/async-stream-emitter-4.1.0.tgz", + "integrity": "sha512-cfPZYjHkhCdHSR+eux71vOU8+8Xb23oLyxccAjwYHgOxDb3+qSDb2HV1Y0Hmu39vZlse2cm15CUShLiVYXHCmQ==", + "dev": true, + "dependencies": { + "stream-demux": "^8.1.0" + } + }, "node_modules/@redux-devtools/remote/node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", @@ -5756,6 +5922,25 @@ "ws": "^8.9.0" } }, + "node_modules/@redux-devtools/remote/node_modules/stream-demux": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/stream-demux/-/stream-demux-8.1.0.tgz", + "integrity": "sha512-20vtOmAj2EVzQZKZVmfyio16u/3QOKSvg+0ldgZeS+m2FNI1vKFoqggamagsPCXufdZ1Tk8VvAM/HV/YUmRbSg==", + "dev": true, + "dependencies": { + "consumable-stream": "^2.0.0", + "writable-consumable-stream": "^3.0.1" + } + }, + "node_modules/@redux-devtools/remote/node_modules/writable-consumable-stream": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/writable-consumable-stream/-/writable-consumable-stream-3.0.1.tgz", + "integrity": "sha512-rAOJTA/sMgXD/X6eMwbQJe49w+Fnkdx3iV5oUzdmiZ7Bwx03khqUnAKIpzp/hbI8q2EP5NfjXgIXN0MsipfHeg==", + "dev": true, + "dependencies": { + "consumable-stream": "^2.0.0" + } + }, "node_modules/@redux-devtools/remote/node_modules/ws": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", @@ -5808,54 +5993,6 @@ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "dev": true }, - "node_modules/@redux-devtools/ui": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@redux-devtools/ui/-/ui-1.3.1.tgz", - "integrity": "sha512-qtfau1zSmv6nSUrnesuxaos1V7r89HQV2Hq/thcRcyHd0Y0OYgpQlnwEK3wkK1esLU2BhEUUb85DC8IdJ3l6Sg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.23.2", - "@rjsf/core": "^4.2.3", - "@types/base16": "^1.0.5", - "@types/codemirror": "^5.60.13", - "@types/json-schema": "^7.0.15", - "@types/prop-types": "^15.7.10", - "@types/redux-devtools-themes": "^1.0.3", - "@types/simple-element-resize-detector": "^1.3.3", - "base16": "^1.0.0", - "codemirror": "^5.65.15", - "color": "^4.2.3", - "prop-types": "^15.8.1", - "react-icons": "^4.11.0", - "react-select": "^5.8.0", - "redux-devtools-themes": "^1.0.0", - "simple-element-resize-detector": "^1.3.0" - }, - "peerDependencies": { - "@types/react": "^16.3.0 || ^17.0.0 || ^18.0.0", - "@types/styled-components": "^5.0.0", - "react": "^16.3.0 || ^17.0.0 || ^18.0.0", - "styled-components": "^5.0.0" - } - }, - "node_modules/@redux-devtools/ui/node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@redux-devtools/ui/node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true - }, "node_modules/@redux-devtools/utils": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@redux-devtools/utils/-/utils-3.0.0.tgz", @@ -6568,12 +6705,6 @@ "@babel/types": "^7.3.0" } }, - "node_modules/@types/base16": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/base16/-/base16-1.0.5.tgz", - "integrity": "sha512-OzOWrTluG9cwqidEzC/Q6FAmIPcnZfm8BFRlIx0+UIUqnuAmi5OS88O0RpT3Yz6qdmqObvUhasrbNsCofE4W9A==", - "dev": true - }, "node_modules/@types/big.js": { "version": "6.1.6", "resolved": "https://registry.npmjs.org/@types/big.js/-/big.js-6.1.6.tgz", @@ -6658,9 +6789,9 @@ } }, "node_modules/@types/d3": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz", - "integrity": "sha512-jIfNVK0ZlxcuRDKtRS/SypEyOQ6UHaFQBKv032X45VvxSJ6Yi5G9behy9h6tNTHTDGh5Vq+KbmBjUWLgY4meCA==", + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", "dev": true, "dependencies": { "@types/d3-array": "*", @@ -6696,45 +6827,45 @@ } }, "node_modules/@types/d3-array": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.7.tgz", - "integrity": "sha512-4/Q0FckQ8TBjsB0VdGFemJOG8BLXUB2KKlL0VmZ+eOYeOnTb/wDRQqYWpBmQ6IlvWkXwkYiot+n9Px2aTJ7zGQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", "dev": true }, "node_modules/@types/d3-axis": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.3.tgz", - "integrity": "sha512-SE3x/pLO/+GIHH17mvs1uUVPkZ3bHquGzvZpPAh4yadRy71J93MJBpgK/xY8l9gT28yTN1g9v3HfGSFeBMmwZw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", "dev": true, "dependencies": { "@types/d3-selection": "*" } }, "node_modules/@types/d3-brush": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.3.tgz", - "integrity": "sha512-MQ1/M/B5ifTScHSe5koNkhxn2mhUPqXjGuKjjVYckplAPjP9t2I2sZafb/YVHDwhoXWZoSav+Q726eIbN3qprA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", "dev": true, "dependencies": { "@types/d3-selection": "*" } }, "node_modules/@types/d3-chord": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.3.tgz", - "integrity": "sha512-keuSRwO02c7PBV3JMWuctIfdeJrVFI7RpzouehvBWL4/GGUB3PBNg/9ZKPZAgJphzmS2v2+7vr7BGDQw1CAulw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", "dev": true }, "node_modules/@types/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", "dev": true }, "node_modules/@types/d3-contour": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.3.tgz", - "integrity": "sha512-x7G/tdDZt4m09XZnG2SutbIuQqmkNYqR9uhDMdPlpJbcwepkEjEWG29euFcgVA1k6cn92CHdDL9Z+fOnxnbVQw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", "dev": true, "dependencies": { "@types/d3-array": "*", @@ -6742,174 +6873,180 @@ } }, "node_modules/@types/d3-delaunay": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz", - "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", "dev": true }, "node_modules/@types/d3-dispatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.3.tgz", - "integrity": "sha512-Df7KW3Re7G6cIpIhQtqHin8yUxUHYAqiE41ffopbmU5+FifYUNV7RVyTg8rQdkEagg83m14QtS8InvNb95Zqug==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==", "dev": true }, "node_modules/@types/d3-drag": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.3.tgz", - "integrity": "sha512-82AuQMpBQjuXeIX4tjCYfWjpm3g7aGCfx6dFlxX2JlRaiME/QWcHzBsINl7gbHCODA2anPYlL31/Trj/UnjK9A==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", "dev": true, "dependencies": { "@types/d3-selection": "*" } }, "node_modules/@types/d3-dsv": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.2.tgz", - "integrity": "sha512-DooW5AOkj4AGmseVvbwHvwM/Ltu0Ks0WrhG6r5FG9riHT5oUUTHz6xHsHqJSVU8ZmPkOqlUEY2obS5C9oCIi2g==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", "dev": true }, "node_modules/@types/d3-ease": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", - "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", "dev": true }, "node_modules/@types/d3-fetch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.3.tgz", - "integrity": "sha512-/EsDKRiQkby3Z/8/AiZq8bsuLDo/tYHnNIZkUpSeEHWV7fHUl6QFBjvMPbhkKGk9jZutzfOkGygCV7eR/MkcXA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", "dev": true, "dependencies": { "@types/d3-dsv": "*" } }, "node_modules/@types/d3-force": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.5.tgz", - "integrity": "sha512-EGG+IWx93ESSXBwfh/5uPuR9Hp8M6o6qEGU7bBQslxCvrdUBQZha/EFpu/VMdLU4B0y4Oe4h175nSm7p9uqFug==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.9.tgz", + "integrity": "sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==", "dev": true }, "node_modules/@types/d3-format": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz", - "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", "dev": true }, "node_modules/@types/d3-geo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.0.4.tgz", - "integrity": "sha512-kmUK8rVVIBPKJ1/v36bk2aSgwRj2N/ZkjDT+FkMT5pgedZoPlyhaG62J+9EgNIgUXE6IIL0b7bkLxCzhE6U4VQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", "dev": true, "dependencies": { "@types/geojson": "*" } }, "node_modules/@types/d3-hierarchy": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.3.tgz", - "integrity": "sha512-GpSK308Xj+HeLvogfEc7QsCOcIxkDwLhFYnOoohosEzOqv7/agxwvJER1v/kTC+CY1nfazR0F7gnHo7GE41/fw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.6.tgz", + "integrity": "sha512-qlmD/8aMk5xGorUvTUWHCiumvgaUXYldYjNVOWtYoTYY/L+WwIEAmJxUmTgr9LoGNG0PPAOmqMDJVDPc7DOpPw==", "dev": true }, "node_modules/@types/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", "dev": true, "dependencies": { "@types/d3-color": "*" } }, "node_modules/@types/d3-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", - "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.2.tgz", + "integrity": "sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA==", "dev": true }, "node_modules/@types/d3-polygon": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.0.tgz", - "integrity": "sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", "dev": true }, "node_modules/@types/d3-quadtree": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.2.tgz", - "integrity": "sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", "dev": true }, "node_modules/@types/d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", "dev": true }, "node_modules/@types/d3-scale": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.4.tgz", - "integrity": "sha512-eq1ZeTj0yr72L8MQk6N6heP603ubnywSDRfNpi5enouR112HzGLS6RIvExCzZTraFF4HdzNpJMwA/zGiMoHUUw==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", "dev": true, "dependencies": { "@types/d3-time": "*" } }, "node_modules/@types/d3-scale-chromatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", - "integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==", "dev": true }, "node_modules/@types/d3-selection": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.6.tgz", - "integrity": "sha512-2ACr96USZVjXR9KMD9IWi1Epo4rSDKnUtYn6q2SPhYxykvXTw9vR77lkFNruXVg4i1tzQtBxeDMx0oNvJWbF1w==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz", + "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==", "dev": true }, "node_modules/@types/d3-shape": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.2.tgz", - "integrity": "sha512-NN4CXr3qeOUNyK5WasVUV8NCSAx/CRVcwcb0BuuS1PiTqwIm6ABi1SyasLZ/vsVCFDArF+W4QiGzSry1eKYQ7w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", "dev": true, "dependencies": { "@types/d3-path": "*" } }, "node_modules/@types/d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==", "dev": true }, "node_modules/@types/d3-time-format": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.0.tgz", - "integrity": "sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", "dev": true }, "node_modules/@types/d3-timer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", - "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", "dev": true }, "node_modules/@types/d3-transition": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.4.tgz", - "integrity": "sha512-512a4uCOjUzsebydItSXsHrPeQblCVk8IKjqCUmrlvBWkkVh3donTTxmURDo1YPwIVDh5YVwCAO6gR4sgimCPQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz", + "integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==", "dev": true, "dependencies": { "@types/d3-selection": "*" } }, "node_modules/@types/d3-zoom": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.4.tgz", - "integrity": "sha512-cqkuY1ah9ZQre2POqjSLcM8g40UVya/qwEUrNYP2/rCVljbmqKCVcv+ebvwhlI5azIbSEL7m+os6n+WlYA43aA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", "dev": true, "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, + "node_modules/@types/diff-match-patch": { + "version": "1.0.36", + "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", + "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", + "dev": true + }, "node_modules/@types/elliptic": { "version": "6.4.14", "resolved": "https://registry.npmjs.org/@types/elliptic/-/elliptic-6.4.14.tgz", @@ -6994,9 +7131,9 @@ "dev": true }, "node_modules/@types/geojson": { - "version": "7946.0.10", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", - "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==", + "version": "7946.0.13", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.13.tgz", + "integrity": "sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==", "dev": true }, "node_modules/@types/get-params": { @@ -7151,9 +7288,9 @@ } }, "node_modules/@types/lodash": { - "version": "4.14.202", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", - "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==" + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==" }, "node_modules/@types/lodash.debounce": { "version": "4.0.9", @@ -7205,27 +7342,13 @@ } }, "node_modules/@types/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.10.tgz", + "integrity": "sha512-PPpPK6F9ALFTn59Ka3BaL+qGuipRfxNE8qVgkp0bVixeiR2c2/L+IVOiBdu9JhhT22sWnQEp6YyHGI2b2+CMcA==", "dev": true, "dependencies": { "@types/node": "*", - "form-data": "^3.0.0" - } - }, - "node_modules/@types/node-fetch/node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" + "form-data": "^4.0.0" } }, "node_modules/@types/parse-json": { @@ -7285,19 +7408,10 @@ "@types/react": "*" } }, - "node_modules/@types/redux-devtools-themes": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/redux-devtools-themes/-/redux-devtools-themes-1.0.3.tgz", - "integrity": "sha512-KqiQ2+6VTb1Yn02+ZNQsB0XcoJTu/W4AhnIUaeTnkVFie5YWMoeSpW4IOFYJ2/HJ+wi1kLw+PHDgjZ3t+M6IRw==", - "dev": true, - "dependencies": { - "@types/base16": "*" - } - }, "node_modules/@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", "dev": true, "dependencies": { "@types/node": "*" @@ -7365,9 +7479,9 @@ "dev": true }, "node_modules/@types/styled-components": { - "version": "5.1.26", - "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.26.tgz", - "integrity": "sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==", + "version": "5.1.34", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.34.tgz", + "integrity": "sha512-mmiVvwpYklFIv9E8qfxuPyIt/OuyIrn6gMOAMOFUO3WJfSrSE+sGUoa4PiZj77Ut7bKZpaa6o1fBKS/4TOEvnA==", "dev": true, "dependencies": { "@types/hoist-non-react-statics": "*", @@ -8629,13 +8743,13 @@ } }, "node_modules/ag-auth": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ag-auth/-/ag-auth-2.0.0.tgz", - "integrity": "sha512-gk5BgWzijPUaCMDYFtuggIlUDWGMlMSOdF/tdQa5sM7Nh0KowohFh3P3sx06gegufKWzWF97dwP/7hLg3CkqWg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ag-auth/-/ag-auth-2.0.1.tgz", + "integrity": "sha512-PpIBdQiTIzp+ELJ8pPHVvxxCIUGXONPSecFgh34zyr/Z3vpqPmT/S21u6lzt1jGPulOTic4Wm9FpVGzC/BYHSQ==", "dev": true, "dependencies": { "jsonwebtoken": "^9.0.0", - "sc-errors": "^2.0.0" + "sc-errors": "^2.0.2" } }, "node_modules/ag-channel": { @@ -8648,23 +8762,23 @@ } }, "node_modules/ag-request": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ag-request/-/ag-request-1.0.0.tgz", - "integrity": "sha512-2f7I0cQLMVyGAqjSewVMEFuAsJsIY6egdE16UHS636r+8c6Oevrv0j6SrOIXyRN6yuNT4PBuhiKmrhHbh9OvEg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ag-request/-/ag-request-1.0.1.tgz", + "integrity": "sha512-3F4pDpLy9mxOXop7LoWE78J5g2jmiEJ0gJfzcECOsf/NaCfyeNmOdNLDVM5dS4Hvbi9T+HENL4DmXq5XSotPaA==", "dev": true, "dependencies": { - "sc-errors": "^2.0.0" + "sc-errors": "^2.0.2" } }, "node_modules/ag-simple-broker": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ag-simple-broker/-/ag-simple-broker-5.0.0.tgz", - "integrity": "sha512-LMwfBdoNm+8ac/RkgW6z1mjIvy2cgEqWa9cJUD5sc5uiJkIn/kXhVRlPdfa/MJtxPivo9DRhKb9DlSp2gCv+Cg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ag-simple-broker/-/ag-simple-broker-6.0.1.tgz", + "integrity": "sha512-pDlHotEoC9uV2Uk8DrR570QXMiUd9QYwJZXWDlBJZEbYTHzMJLEJDJStxmn7Kp4eT7SIGoPFuzELYZyMYNZ2Kw==", "dev": true, "dependencies": { "ag-channel": "^5.0.0", - "async-stream-emitter": "^4.0.0", - "stream-demux": "^8.0.0" + "async-stream-emitter": "^7.0.1", + "stream-demux": "^10.0.1" } }, "node_modules/agent-base": { @@ -9144,12 +9258,12 @@ } }, "node_modules/async-stream-emitter": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/async-stream-emitter/-/async-stream-emitter-4.1.0.tgz", - "integrity": "sha512-cfPZYjHkhCdHSR+eux71vOU8+8Xb23oLyxccAjwYHgOxDb3+qSDb2HV1Y0Hmu39vZlse2cm15CUShLiVYXHCmQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/async-stream-emitter/-/async-stream-emitter-7.0.1.tgz", + "integrity": "sha512-1bgA3iZ80rCBX2LocvsyZPy0QB3/xM+CsXBze2HDHLmshOqx2JlAANGq23djaJ48e9fpcKzTzS1QM0hAKKI0UQ==", "dev": true, "dependencies": { - "stream-demux": "^8.1.0" + "stream-demux": "^10.0.1" } }, "node_modules/asynciterator.prototype": { @@ -9533,12 +9647,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/base16": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", - "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==", - "dev": true - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -9879,12 +9987,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -10922,12 +11030,15 @@ } }, "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, "dependencies": { "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/clone-stats": { @@ -11764,16 +11875,6 @@ "postcss-value-parser": "^4.0.2" } }, - "node_modules/css-vendor": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", - "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.8.3", - "is-in-browser": "^1.0.2" - } - }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -11828,14 +11929,14 @@ "dev": true }, "node_modules/csstype": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", - "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/d3": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", - "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", "dev": true, "dependencies": { "d3-array": "3", @@ -12270,18 +12371,36 @@ } }, "node_modules/d3tooltip": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3tooltip/-/d3tooltip-3.0.0.tgz", - "integrity": "sha512-bQSxCU0D99KEvpHZjB5oiXToc98FxIHOAAOJRncZMsU3iUU18a2X//FgPnuI0K7wVjfKfgr4dXw5sEvTXSyu6Q==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3tooltip/-/d3tooltip-3.0.1.tgz", + "integrity": "sha512-y1CqKBZE/PUVvXzPeJrAxRh5pj6hzz8/oFdEpgGI0TbhFVhQ1xBv0wCFEz6BzZ94t+5Y+n1x+Q6rGMDtlzy7rA==", "dev": true, "dependencies": { - "@babel/runtime": "^7.20.6" + "@babel/runtime": "^7.23.2" }, "peerDependencies": { - "@types/d3": "^7.4.0", - "d3": "^7.8.0" + "@types/d3": "^7.0.0", + "d3": "^7.0.0" + } + }, + "node_modules/d3tooltip/node_modules/@babel/runtime": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.7.tgz", + "integrity": "sha512-w06OXVOFso7LcbzMiDGt+3X7Rh7Ho8MmgPoWU3rarH+8upf+wSU/grlGbWzQyr3DkdN6ZeuMFjpdwW0Q+HxobA==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" } }, + "node_modules/d3tooltip/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -13079,9 +13198,9 @@ "dev": true }, "node_modules/electron": { - "version": "25.9.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-25.9.0.tgz", - "integrity": "sha512-wgscxf2ORHL/8mAQfy7l9rVDG//wrG9RUQndG508kCCMHRq9deFyZ4psOMzySheBRSfGMcFoRFYSlkAeZr8cFg==", + "version": "27.2.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-27.2.0.tgz", + "integrity": "sha512-no/iMICVLI/5G0IqgKFbB89HDN88DWwKeRO+dPfJPkpJISdEX8Cx/sMEOFuuRa4VNInNe5CKCqRWExK5z3AdcQ==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -13102,9 +13221,9 @@ "integrity": "sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==" }, "node_modules/electron/node_modules/@types/node": { - "version": "18.18.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.9.tgz", - "integrity": "sha512-0f5klcuImLnG4Qreu9hPj/rEfFq6YRc5n2mAjSsH+ec/mJL+3voBH0+8T7o8RpFjH7ovc+TRsL/c7OYIQsPTfQ==", + "version": "18.19.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.4.tgz", + "integrity": "sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -13697,9 +13816,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -14805,9 +14924,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -15879,9 +15998,9 @@ "dev": true }, "node_modules/graphql": { - "version": "16.8.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.0.tgz", - "integrity": "sha512-0oKGaR+y3qcS5mCu1vb7KG+a89vjn06C7Ihq/dDl3jA+A8B3TKomvi3CiEcVLJQGalbu8F52LxkOym7U5sSfbg==", + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", + "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" @@ -16197,9 +16316,9 @@ } }, "node_modules/html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", "dev": true, "funding": [ { @@ -16331,9 +16450,9 @@ } }, "node_modules/html-webpack-plugin": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", - "integrity": "sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz", + "integrity": "sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==", "dev": true, "dependencies": { "@types/html-minifier-terser": "^6.0.0", @@ -16350,7 +16469,16 @@ "url": "https://opencollective.com/html-webpack-plugin" }, "peerDependencies": { + "@rspack/core": "0.x || 1.x", "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, "node_modules/htmlparser2": { @@ -16551,12 +16679,6 @@ "url": "https://github.com/sponsors/typicode" } }, - "node_modules/hyphenate-style-name": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", - "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", - "dev": true - }, "node_modules/i18next": { "version": "23.11.0", "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.11.0.tgz", @@ -16844,9 +16966,9 @@ } }, "node_modules/immutable": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", - "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, "node_modules/import-fresh": { @@ -17260,12 +17382,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-in-browser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", - "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==", - "dev": true - }, "node_modules/is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -20114,172 +20230,6 @@ "node": ">=0.6.0" } }, - "node_modules/jss": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", - "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "csstype": "^3.0.2", - "is-in-browser": "^1.1.3", - "tiny-warning": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/jss" - } - }, - "node_modules/jss-plugin-camel-case": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", - "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "hyphenate-style-name": "^1.0.3", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-compose": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-compose/-/jss-plugin-compose-10.10.0.tgz", - "integrity": "sha512-F5kgtWpI2XfZ3Z8eP78tZEYFdgTIbpA/TMuX3a8vwrNolYtN1N4qJR/Ob0LAsqIwCMLojtxN7c7Oo/+Vz6THow==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "node_modules/jss-plugin-default-unit": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", - "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-expand": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-expand/-/jss-plugin-expand-10.10.0.tgz", - "integrity": "sha512-ymT62W2OyDxBxr7A6JR87vVX9vTq2ep5jZLIdUSusfBIEENLdkkc0lL/Xaq8W9s3opUq7R0sZQpzRWELrfVYzA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-extend": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-extend/-/jss-plugin-extend-10.10.0.tgz", - "integrity": "sha512-sKYrcMfr4xxigmIwqTjxNcHwXJIfvhvjTNxF+Tbc1NmNdyspGW47Ey6sGH8BcQ4FFQhLXctpWCQSpDwdNmXSwg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "node_modules/jss-plugin-global": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", - "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-nested": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", - "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "node_modules/jss-plugin-props-sort": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", - "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "node_modules/jss-plugin-rule-value-function": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", - "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "node_modules/jss-plugin-rule-value-observable": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-observable/-/jss-plugin-rule-value-observable-10.10.0.tgz", - "integrity": "sha512-ZLMaYrR3QE+vD7nl3oNXuj79VZl9Kp8/u6A1IbTPDcuOu8b56cFdWRZNZ0vNr8jHewooEeq2doy8Oxtymr2ZPA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "symbol-observable": "^1.2.0" - } - }, - "node_modules/jss-plugin-template": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-template/-/jss-plugin-template-10.10.0.tgz", - "integrity": "sha512-ocXZBIOJOA+jISPdsgkTs8wwpK6UbsvtZK5JI7VUggTD6LWKbtoxUzadd2TpfF+lEtlhUmMsCkTRNkITdPKa6w==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "node_modules/jss-plugin-vendor-prefixer": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", - "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "css-vendor": "^2.0.8", - "jss": "10.10.0" - } - }, - "node_modules/jss-preset-default": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-preset-default/-/jss-preset-default-10.10.0.tgz", - "integrity": "sha512-GL175Wt2FGhjE+f+Y3aWh+JioL06/QWFgZp53CbNNq6ZkVU0TDplD8Bxm9KnkotAYn3FlplNqoW5CjyLXcoJ7Q==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "jss-plugin-camel-case": "10.10.0", - "jss-plugin-compose": "10.10.0", - "jss-plugin-default-unit": "10.10.0", - "jss-plugin-expand": "10.10.0", - "jss-plugin-extend": "10.10.0", - "jss-plugin-global": "10.10.0", - "jss-plugin-nested": "10.10.0", - "jss-plugin-props-sort": "10.10.0", - "jss-plugin-rule-value-function": "10.10.0", - "jss-plugin-rule-value-observable": "10.10.0", - "jss-plugin-template": "10.10.0", - "jss-plugin-vendor-prefixer": "10.10.0" - } - }, "node_modules/jsx-ast-utils": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", @@ -20398,9 +20348,9 @@ } }, "node_modules/knex": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/knex/-/knex-2.5.1.tgz", - "integrity": "sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz", + "integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==", "dev": true, "dependencies": { "colorette": "2.0.19", @@ -20412,7 +20362,7 @@ "getopts": "2.3.0", "interpret": "^2.2.0", "lodash": "^4.17.21", - "pg-connection-string": "2.6.1", + "pg-connection-string": "2.6.2", "rechoir": "^0.8.0", "resolve-from": "^5.0.0", "tarn": "^3.0.2", @@ -20422,7 +20372,7 @@ "knex": "bin/cli.js" }, "engines": { - "node": ">=12" + "node": ">=16" }, "peerDependenciesMeta": { "better-sqlite3": { @@ -20975,12 +20925,6 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, - "node_modules/lodash.curry": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", - "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==", - "dev": true - }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -23298,9 +23242,9 @@ "dev": true }, "node_modules/pg-connection-string": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", - "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==", "dev": true }, "node_modules/picocolors": { @@ -24084,9 +24028,9 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -24095,28 +24039,15 @@ } }, "node_modules/react-base16-styling": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.9.1.tgz", - "integrity": "sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.16.7", - "@types/base16": "^1.0.2", - "@types/lodash": "^4.14.178", - "base16": "^1.0.0", - "color": "^3.2.1", - "csstype": "^3.0.10", - "lodash.curry": "^4.1.1" - } - }, - "node_modules/react-base16-styling/node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.10.0.tgz", + "integrity": "sha512-H1k2eFB6M45OaiRru3PBXkuCcn2qNmx+gzLb4a9IPMR7tMH8oBRXU5jGbPDYG1Hz+82d88ED0vjR8BmqU3pQdg==", "dev": true, "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" + "@types/lodash": "^4.17.0", + "color": "^4.2.3", + "csstype": "^3.1.3", + "lodash-es": "^4.17.21" } }, "node_modules/react-dom": { @@ -24205,9 +24136,9 @@ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/react-icons": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz", - "integrity": "sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz", + "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==", "dev": true, "peerDependencies": { "react": "*" @@ -24253,14 +24184,13 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/react-json-tree": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.18.0.tgz", - "integrity": "sha512-Qe6HKSXrr++n9Y31nkRJ3XvQMATISpqigH1vEKhLwB56+nk5thTP0ITThpjxY6ZG/ubpVq/aEHIcyLP/OPHxeA==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.19.0.tgz", + "integrity": "sha512-PqT1WRVcWP+RROsZPQfNEKIC1iM/ZMfY4g5jN6oDnXp5593PPRAYgoHcgYCDjflAHQMtxl8XGdlTwIBdEGUXvw==", "dev": true, "dependencies": { - "@babel/runtime": "^7.20.6", - "@types/lodash": "^4.14.191", - "react-base16-styling": "^0.9.1" + "@types/lodash": "^4.17.0", + "react-base16-styling": "^0.10.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", @@ -24493,15 +24423,6 @@ "@babel/runtime": "^7.9.2" } }, - "node_modules/redux-devtools-themes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redux-devtools-themes/-/redux-devtools-themes-1.0.0.tgz", - "integrity": "sha512-hBWqdZX+dioMWnTjf8+uSm0q1wCdYO4kU5gERzHcMMbu0Qg7JDR42TnJ6GHJ6r7k/tIpsCSygc9U0ehAtR24TQ==", - "dev": true, - "dependencies": { - "base16": "^1.0.0" - } - }, "node_modules/redux-persist": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", @@ -25250,9 +25171,9 @@ } }, "node_modules/sc-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/sc-errors/-/sc-errors-2.0.1.tgz", - "integrity": "sha512-JoVhq3Ud+3Ujv2SIG7W0XtjRHsrNgl6iXuHHsh0s+Kdt5NwI6N2EGAZD4iteitdDv68ENBkpjtSvN597/wxPSQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sc-errors/-/sc-errors-2.0.3.tgz", + "integrity": "sha512-HNpClBWpo7zxLBnhH0U/FbC19Gl3OJlVyPxo9Q2eomfdWgYfd84uhqe0LRgybc+nSpcYjtF08+/dKPLugLMMeQ==", "dev": true }, "node_modules/schema-utils": { @@ -25815,21 +25736,21 @@ } }, "node_modules/socketcluster-server": { - "version": "17.4.1", - "resolved": "https://registry.npmjs.org/socketcluster-server/-/socketcluster-server-17.4.1.tgz", - "integrity": "sha512-ElKD9U7EncoWNGYOL+G6UAxuYmui1fnawpyhZIpFG/A/lDNGwHsQLNIpJXh1SB4BpoQLn2q4ewwnUK3Mse5mfA==", + "version": "19.0.1", + "resolved": "https://registry.npmjs.org/socketcluster-server/-/socketcluster-server-19.0.1.tgz", + "integrity": "sha512-IfMZxTkzvqOUExqiVxkxE2DjN/tap6WpbplatptsHKa58wfN6SdfcYCYeR3hlUBOx+cG09/hPxlN+R5tYZF0Mg==", "dev": true, "dependencies": { "ag-auth": "^2.0.0", - "ag-request": "^1.0.0", - "ag-simple-broker": "^5.0.0", - "async-stream-emitter": "^4.0.0", + "ag-request": "^1.0.1", + "ag-simple-broker": "^6.0.0", + "async-stream-emitter": "^7.0.1", "base64id": "^2.0.0", "clone-deep": "^4.0.1", - "sc-errors": "^2.0.1", + "sc-errors": "^2.0.3", "sc-formatter": "^4.0.0", - "stream-demux": "^8.0.0", - "writable-consumable-stream": "^2.0.0", + "stream-demux": "^10.0.1", + "writable-consumable-stream": "^4.1.0", "ws": "^8.9.0" } }, @@ -25840,9 +25761,9 @@ "dev": true }, "node_modules/socketcluster-server/node_modules/ws": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.1.tgz", - "integrity": "sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "engines": { "node": ">=10.0.0" @@ -26216,23 +26137,20 @@ } }, "node_modules/stream-demux": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/stream-demux/-/stream-demux-8.1.0.tgz", - "integrity": "sha512-20vtOmAj2EVzQZKZVmfyio16u/3QOKSvg+0ldgZeS+m2FNI1vKFoqggamagsPCXufdZ1Tk8VvAM/HV/YUmRbSg==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/stream-demux/-/stream-demux-10.0.1.tgz", + "integrity": "sha512-QjTYLJWpZxZ6uL5R1JzgOzjvao8zDx78ec+uOjHNeVc/9TuasYLldoVrYARZeT1xI1hFYuiKf13IM8b4wamhHg==", "dev": true, "dependencies": { - "consumable-stream": "^2.0.0", - "writable-consumable-stream": "^3.0.1" + "consumable-stream": "^3.0.0", + "writable-consumable-stream": "^4.1.0" } }, - "node_modules/stream-demux/node_modules/writable-consumable-stream": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/writable-consumable-stream/-/writable-consumable-stream-3.0.1.tgz", - "integrity": "sha512-rAOJTA/sMgXD/X6eMwbQJe49w+Fnkdx3iV5oUzdmiZ7Bwx03khqUnAKIpzp/hbI8q2EP5NfjXgIXN0MsipfHeg==", - "dev": true, - "dependencies": { - "consumable-stream": "^2.0.0" - } + "node_modules/stream-demux/node_modules/consumable-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/consumable-stream/-/consumable-stream-3.0.0.tgz", + "integrity": "sha512-CnnsJ9OG9ouxAjt3pc63/DaerezRo/WudqU71pc5epaIUi7NHu2T4v+3f0nKbbCY7icS/TfQ1Satr9rwZ7Jwsg==", + "dev": true }, "node_modules/stream-shift": { "version": "1.0.1", @@ -26648,15 +26566,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -26928,12 +26837,6 @@ "node": ">=8" } }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", - "dev": true - }, "node_modules/titleize": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", @@ -29304,14 +29207,20 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "node_modules/writable-consumable-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/writable-consumable-stream/-/writable-consumable-stream-2.0.0.tgz", - "integrity": "sha512-SITambzxtPTFU/wR82h+zOKGBiEv5V8gC1mt8xvoE1/168ApEa8H+6s2UToYJo3VLL7sNYTaApKuPD+pZHMGJQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/writable-consumable-stream/-/writable-consumable-stream-4.1.0.tgz", + "integrity": "sha512-4cjCPd4Ayfbix0qqPCzMbnPPZKRh/cKeNCj05unybP3/sRkRAOxh7rSwbhxs3YB6G4/Z2p/2FRBEIQcTeB4jyw==", "dev": true, "dependencies": { - "consumable-stream": "^2.0.0" + "consumable-stream": "^3.0.0" } }, + "node_modules/writable-consumable-stream/node_modules/consumable-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/consumable-stream/-/consumable-stream-3.0.0.tgz", + "integrity": "sha512-CnnsJ9OG9ouxAjt3pc63/DaerezRo/WudqU71pc5epaIUi7NHu2T4v+3f0nKbbCY7icS/TfQ1Satr9rwZ7Jwsg==", + "dev": true + }, "node_modules/write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -29627,9 +29536,9 @@ } }, "@apollo/server": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.9.3.tgz", - "integrity": "sha512-U56Sx/UmzR3Es344hQ/Ptf2EJrH+kV4ZPoLmgGjWoiwf2wYQ/pRSvkSXgjOvoyE34wSa8Gh7f92ljfLfY+6q1w==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.9.5.tgz", + "integrity": "sha512-eDBfArYbZaTm1AGa82M1aL7lOscVhnZsH85+OWmHMIR98qntzEjNpWpQPYDTru63Qxs4kHcY29NUx/kMGZfGEA==", "dev": true, "requires": { "@apollo/cache-control-types": "^1.0.3", @@ -31465,9 +31374,9 @@ "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" }, "@emotion/react": { - "version": "11.11.3", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz", - "integrity": "sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==", + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", "dev": true, "requires": { "@babel/runtime": "^7.18.3", @@ -33073,254 +32982,287 @@ "dev": true }, "@redux-devtools/cli": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@redux-devtools/cli/-/cli-3.0.1.tgz", - "integrity": "sha512-NARxbTvLQQkZ4if5zvQTkTyesKCsu9DpN9Uupz1Ib18lUEuuEYl3HKJFYQ/hScnxxMSin7KN8j8guHMVD16pqg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@redux-devtools/cli/-/cli-4.0.0.tgz", + "integrity": "sha512-MACJmhnNS8Q0P8ldYgH+p7//U6QdetQqnIS5KZRzd8sp4rYSegzTqsQuf4eGZ4PSzrVOoVezj9eH6ESZFm9/9Q==", "dev": true, "requires": { - "@apollo/server": "^4.9.1", - "@redux-devtools/app": "^3.0.0", - "@types/react": "^18.2.20", + "@apollo/server": "^4.9.5", + "@emotion/react": "^11.11.3", + "@redux-devtools/app": "^6.0.0", + "@types/react": "^18.2.46", "body-parser": "^1.20.2", "chalk": "^5.3.0", "cors": "^2.8.5", "cross-spawn": "^7.0.3", - "electron": "^25.6.0", + "electron": "^27.2.0", "express": "^4.18.2", "get-port": "^7.0.0", - "graphql": "^16.8.0", - "knex": "^2.5.1", + "graphql": "^16.8.1", + "knex": "^3.1.0", "lodash-es": "^4.17.21", "minimist": "^1.2.8", "morgan": "^1.10.0", - "open": "^9.1.0", + "open": "^10.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-is": "^18.2.0", "semver": "^7.5.4", - "socketcluster-server": "^17.4.1", + "socketcluster-server": "^19.0.1", "sqlite3": "^5.1.6", "styled-components": "^5.3.11", - "uuid": "^9.0.0" + "uuid": "^9.0.1" }, "dependencies": { "@babel/code-frame": { - "version": "8.0.0-alpha.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0-alpha.6.tgz", - "integrity": "sha512-w29LUiFqVMe+Ybp/+xtQKAp6rpxjHxtCW909I0OMHvFKzrGB0K+1yQGRzXhTqrd4f7ZRRgzkAxdr0EOEbHt3dA==", + "version": "8.0.0-alpha.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0-alpha.8.tgz", + "integrity": "sha512-PaXRlxddNNx+S+OZ/laLOqhiqaDgBQCvxR/Z0CHcpXgPRmtBNbRxvWhf91+YtST1O+O6j2K0F4bb5DvA3Hr4gQ==", "dev": true, "requires": { - "@babel/highlight": "^8.0.0-alpha.6", - "chalk": "^5.3.0" + "@babel/highlight": "^8.0.0-alpha.8", + "picocolors": "^1.0.0" } }, "@babel/helper-validator-identifier": { - "version": "8.0.0-alpha.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.0-alpha.6.tgz", - "integrity": "sha512-a0YnAx4TJCgds07M2v0A3WeyZUfls2YOgNHCb1akGKlz5KdmTCqf2N2hw86YhjY6oDqb6Omb8gFKz/3TOKRevQ==", + "version": "8.0.0-alpha.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.0-alpha.8.tgz", + "integrity": "sha512-7uCkkKAnoPa864zeoJ82mhXNVhRtvVbquSZ+i98qG4jaUu3yRe3ZKWV1K075FTthM4gNgRVMWTK/KjrSuX9w+Q==", "dev": true }, "@babel/highlight": { - "version": "8.0.0-alpha.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-8.0.0-alpha.6.tgz", - "integrity": "sha512-YzJ5blZXak7Gk6ZgV9a4B9bAkGzl594ZxmBaFFZkstA5hE1iOBTlUW4tkGKz2uomL7FLLW4qHJF3QjQLHKi7iw==", + "version": "8.0.0-alpha.8", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-8.0.0-alpha.8.tgz", + "integrity": "sha512-1xw7Z/AMD4ZJxxktv1sXClheNGab1BAU11SND6AiwUyhIAOVrvQ9QiOBd8I/iMXdXtANCNyu0YLswn3TNlcRZw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^8.0.0-alpha.6", - "chalk": "^5.3.0", - "js-tokens": "^8.0.0" + "@babel/helper-validator-identifier": "^8.0.0-alpha.8", + "js-tokens": "^8.0.0", + "picocolors": "^1.0.0" } }, "@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz", + "integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==", "dev": true, "requires": { "regenerator-runtime": "^0.14.0" } }, "@redux-devtools/app": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/app/-/app-3.0.0.tgz", - "integrity": "sha512-3mn+cj3KhMRSa0EPxlRD4EnfFFMBsM1nKJ+g/6zB4iRyk23befz4YzwFG3KXkwyVmZCooCu3DjHkQhUOx4xxQA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.22.10", - "@redux-devtools/chart-monitor": "^4.0.0", - "@redux-devtools/core": "^3.13.0", - "@redux-devtools/inspector-monitor": "^4.0.0", - "@redux-devtools/inspector-monitor-test-tab": "^2.0.0", - "@redux-devtools/inspector-monitor-trace-tab": "^2.0.0", - "@redux-devtools/log-monitor": "^4.0.2", - "@redux-devtools/rtk-query-monitor": "^3.1.1", - "@redux-devtools/slider-monitor": "^4.0.0", - "@redux-devtools/ui": "^1.3.0", - "@reduxjs/toolkit": "^1.9.5", - "@types/prop-types": "^15.7.5", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@redux-devtools/app/-/app-6.0.0.tgz", + "integrity": "sha512-jR6Nm/Pe/ImQ2XmJC1JIyVduQ/3J4bXQhtvoH/pw8WuVJrQvFzhfY5McS+LbHB6H+Vjfhq7mq3u1T2dcQ6II3g==", + "dev": true, + "requires": { + "@babel/runtime": "^7.23.5", + "@redux-devtools/chart-monitor": "^5.0.0", + "@redux-devtools/core": "^4.0.0", + "@redux-devtools/inspector-monitor": "^6.0.0", + "@redux-devtools/inspector-monitor-test-tab": "^4.0.0", + "@redux-devtools/inspector-monitor-trace-tab": "^4.0.0", + "@redux-devtools/log-monitor": "^5.0.0", + "@redux-devtools/rtk-query-monitor": "^5.0.0", + "@redux-devtools/slider-monitor": "^5.0.0", + "@redux-devtools/ui": "^1.3.1", + "@reduxjs/toolkit": "^1.9.7", "d3-state-visualizer": "^2.0.0", "javascript-stringify": "^2.1.0", "jsan": "^3.1.14", "jsondiffpatch": "^0.5.0", "localforage": "^1.10.0", "lodash": "^4.17.21", - "prop-types": "^15.8.1", - "react-icons": "^4.10.1", + "react-icons": "^4.12.0", "react-is": "^18.2.0", - "react-redux": "^8.1.2", - "redux": "4.2.1", + "react-redux": "^8.1.3", + "redux": "^4.2.1", "redux-persist": "^6.0.0", "socketcluster-client": "^17.2.2" - } - }, - "@redux-devtools/chart-monitor": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/chart-monitor/-/chart-monitor-4.1.0.tgz", - "integrity": "sha512-fdW7DKEhCB9oumMTyhdrVGOgb/6lBioPveW3w0+u7MCZn2i32laZYzU9VT39sx+Fc2kwxzbYsLxFsU0tijGsXA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.23.2", - "@types/redux-devtools-themes": "^1.0.3", - "d3-state-visualizer": "^2.0.0", - "deepmerge": "^4.3.1", - "redux-devtools-themes": "^1.0.0" - } - }, - "@redux-devtools/core": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/core/-/core-3.14.0.tgz", - "integrity": "sha512-OMPflPPCXR9L1rpfd7gwY31/EuqPyE9Of/5wZgDDzeisaENY5h/EfnAjnHRKr7NIx/yUIUX2DJs8NpmUOnANMg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.23.2", - "@redux-devtools/instrument": "^2.2.0", - "@types/prop-types": "^15.7.10", - "lodash": "^4.17.21", - "prop-types": "^15.8.1" + }, + "dependencies": { + "@redux-devtools/chart-monitor": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@redux-devtools/chart-monitor/-/chart-monitor-5.0.2.tgz", + "integrity": "sha512-5S70WvWufMa8ybwpFqxGqA65Mb8xV7e1gviKnGp6KGDn+EYo2bu+O+o5ugt/4WW4z4HgeXkNl3DawwjTUNc9aw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.24.1", + "d3-state-visualizer": "^3.0.0", + "deepmerge": "^4.3.1", + "react-base16-styling": "^0.10.0" + }, + "dependencies": { + "d3-state-visualizer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-state-visualizer/-/d3-state-visualizer-3.0.0.tgz", + "integrity": "sha512-69syDmi4b6sr9/UJUVpKTzFFx7jeI8AogEWv2qL/AWKky9b9NgqgMtSBbJNQdncq6epsOKP7a2MDun90taHtTQ==", + "dev": true, + "requires": { + "@types/d3": "^7.4.3", + "d3": "^7.9.0", + "d3tooltip": "^4.0.0", + "deepmerge": "^4.3.1", + "map2tree": "^4.0.0", + "ramda": "^0.29.1" + } + } + } + }, + "@redux-devtools/inspector-monitor-test-tab": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@redux-devtools/inspector-monitor-test-tab/-/inspector-monitor-test-tab-4.0.0.tgz", + "integrity": "sha512-a5SuX6eyFvBAK9UcRW/n5K8nSISWR+UGW0+fZh+KP20naSgkvagrzC705NX8riCfJc8fr5csIYVYwmypMAZ2UA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.23.5", + "@redux-devtools/ui": "^1.3.1", + "es6template": "^1.0.5", + "javascript-stringify": "^2.1.0", + "jsan": "^3.1.14", + "object-path": "^0.11.8", + "react-icons": "^4.12.0", + "simple-diff": "^1.7.2" + } + }, + "@redux-devtools/inspector-monitor-trace-tab": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@redux-devtools/inspector-monitor-trace-tab/-/inspector-monitor-trace-tab-4.0.1.tgz", + "integrity": "sha512-7tu/JU9wmpp74sBlBirgikrjJsx3IoJBKzUj9lO9ox9U9cJ/MhSo9B8ZAmLDI8EJa3CzxCcNQnDxW/Wucy17DQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^8.0.0-alpha.7", + "@babel/runtime": "^7.24.1", + "@types/chrome": "^0.0.263", + "anser": "^2.1.1", + "html-entities": "^2.5.2", + "path-browserify": "^1.0.1", + "react-base16-styling": "^0.10.0", + "source-map": "^0.5.7" + } + }, + "@redux-devtools/log-monitor": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@redux-devtools/log-monitor/-/log-monitor-5.0.1.tgz", + "integrity": "sha512-NCuKr9hdruL01rbghKs/eD2lLcKhXmICXf2js2gTM9oDA2662+X9M9BahcJwjhkTUBxdsXbDQ5d/uGvxr530Vg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.24.1", + "@types/lodash.debounce": "^4.0.9", + "lodash.debounce": "^4.0.8", + "react-base16-styling": "^0.10.0", + "react-json-tree": "^0.19.0" + } + }, + "@redux-devtools/rtk-query-monitor": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@redux-devtools/rtk-query-monitor/-/rtk-query-monitor-5.0.1.tgz", + "integrity": "sha512-mqYkEop8lgyswW4HxEDnPBsdxU/h3Rm+/q0KCbeuVSZU7Ek/gEgSnKAi++JvsTMPekgA1Uie94/kztOrB6Kv/w==", + "dev": true, + "requires": { + "@babel/runtime": "^7.24.1", + "@redux-devtools/ui": "^1.3.2", + "@types/lodash": "^4.17.0", + "hex-rgba": "^1.0.2", + "immutable": "^4.3.5", + "lodash.debounce": "^4.0.8", + "react-base16-styling": "^0.10.0", + "react-json-tree": "^0.19.0" + } + }, + "@redux-devtools/slider-monitor": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@redux-devtools/slider-monitor/-/slider-monitor-5.0.1.tgz", + "integrity": "sha512-ud04CLxJNMpNfXKrfHtt4i7LbYCi3PVdcrQgU0X3qeEUWiXkvumZJIs0DPKSMk3Ukqy/G06SMNL4aqy6icjkLQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.24.1", + "@redux-devtools/ui": "^1.3.2", + "react-base16-styling": "^0.10.0" + } + } } }, "@redux-devtools/inspector-monitor": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/inspector-monitor/-/inspector-monitor-4.1.0.tgz", - "integrity": "sha512-ga0rBdb8LeB7mN+X+/Pb0rx+yTPOsSHFFVxgcWag7XsBu+dXtujlRsCM++6LX6GjJGNzZAUsG4ZI4Wt0dG2CQg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@redux-devtools/inspector-monitor/-/inspector-monitor-6.0.1.tgz", + "integrity": "sha512-FGUOrV3hEKrelqGSWmExKbBfXTEtLQ1yj+gLuPY2yAnZwFwOKzxybL5tsHCvVZsm/hgkA3WGcpYHtZ6+pJSWBg==", "dev": true, "requires": { - "@babel/runtime": "^7.23.2", + "@babel/runtime": "^7.24.1", "@dnd-kit/core": "^6.1.0", "@dnd-kit/modifiers": "^7.0.0", "@dnd-kit/sortable": "^8.0.0", "@dnd-kit/utilities": "^3.2.2", - "@types/lodash": "^4.14.201", - "@types/prop-types": "^15.7.10", - "@types/redux-devtools-themes": "^1.0.3", + "@types/lodash": "^4.17.0", "dateformat": "^5.0.3", "hex-rgba": "^1.0.2", - "immutable": "^4.3.4", + "immutable": "^4.3.5", "javascript-stringify": "^2.1.0", - "jsondiffpatch": "^0.5.0", - "jss": "^10.10.0", - "jss-preset-default": "^10.10.0", + "jsondiffpatch": "^0.6.0", "lodash.debounce": "^4.0.8", - "prop-types": "^15.8.1", - "react-base16-styling": "^0.9.1", - "react-json-tree": "^0.18.0", - "redux-devtools-themes": "^1.0.0" - } - }, - "@redux-devtools/inspector-monitor-test-tab": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/inspector-monitor-test-tab/-/inspector-monitor-test-tab-2.1.0.tgz", - "integrity": "sha512-wQ8dnB1F7RZn4XFoKYVPUPt996CIGWG1/ndQwGkXPvHg+P32QHUHsrhR+QDpn6Z1dMamycDjqijS2KDkQA/MDg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.23.2", - "@redux-devtools/ui": "^1.3.1", - "@types/prop-types": "^15.7.10", - "es6template": "^1.0.5", - "javascript-stringify": "^2.1.0", - "jsan": "^3.1.14", - "object-path": "^0.11.8", - "prop-types": "^15.8.1", - "react-icons": "^4.11.0", - "simple-diff": "^1.7.2" - } - }, - "@redux-devtools/inspector-monitor-trace-tab": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/inspector-monitor-trace-tab/-/inspector-monitor-trace-tab-2.1.0.tgz", - "integrity": "sha512-Sac8iyYahyrq3k76kty1ebMVCp7Q1ABdBdzj8vJ5FSiaXOKU+HY1y4YS6Fb9ra7Bj5udIqzr/zdxprdPc7Yatg==", - "dev": true, - "requires": { - "@babel/code-frame": "^8.0.0-alpha.4", - "@babel/runtime": "^7.23.2", - "@types/chrome": "^0.0.251", - "anser": "^2.1.1", - "html-entities": "^2.4.0", - "path-browserify": "^1.0.1", - "redux-devtools-themes": "^1.0.0", - "source-map": "^0.5.7" + "react-base16-styling": "^0.10.0", + "react-json-tree": "^0.19.0" + }, + "dependencies": { + "jsondiffpatch": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz", + "integrity": "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==", + "dev": true, + "requires": { + "@types/diff-match-patch": "^1.0.36", + "chalk": "^5.3.0", + "diff-match-patch": "^1.0.5" + } + } } }, - "@redux-devtools/log-monitor": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/log-monitor/-/log-monitor-4.1.0.tgz", - "integrity": "sha512-2danca7yfQnuyzYxUVsEontmcRfQLFZgU8M33690JfvHlUV1OYjkNhbwKkMEso5py8sTd2EgoC2PGySoyupbUg==", + "@redux-devtools/ui": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@redux-devtools/ui/-/ui-1.3.2.tgz", + "integrity": "sha512-/g9FSzvOSCkTXibd+h13kRE433/Zv4KVImQaanJsl8fc0MEfBSyFtGE1wjeIkwqs322EVBxw3fPWaQAC1gpLHg==", "dev": true, "requires": { - "@babel/runtime": "^7.23.2", - "@types/lodash.debounce": "^4.0.9", - "@types/prop-types": "^15.7.10", - "@types/redux-devtools-themes": "^1.0.3", - "lodash.debounce": "^4.0.8", - "prop-types": "^15.8.1", - "react-json-tree": "^0.18.0", - "redux-devtools-themes": "^1.0.0" + "@babel/runtime": "^7.24.1", + "@rjsf/core": "^4.2.3", + "@types/codemirror": "^5.60.15", + "@types/json-schema": "^7.0.15", + "@types/simple-element-resize-detector": "^1.3.3", + "codemirror": "^5.65.16", + "color": "^4.2.3", + "react-base16-styling": "^0.10.0", + "react-icons": "^5.0.1", + "react-select": "^5.8.0", + "simple-element-resize-detector": "^1.3.0" + }, + "dependencies": { + "react-icons": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==", + "dev": true, + "requires": {} + } } }, - "@redux-devtools/rtk-query-monitor": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/rtk-query-monitor/-/rtk-query-monitor-3.2.0.tgz", - "integrity": "sha512-Td7oZUAO/TyxillkC2E33vYqfKEDFilJfLHLg8Na7KdfG3Y0JR5qmu0xypZDODeEsA5teHpkMnhI3O9ew/JqdQ==", + "@types/chrome": { + "version": "0.0.263", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.263.tgz", + "integrity": "sha512-As0vzv99ov3M6ZR7R6VzhMWFZXkPMrFrCEXXVrMN576Cm70fTkj7Df2CF+qEo170JepX50pd11cX6O4DSAtl2Q==", "dev": true, "requires": { - "@babel/runtime": "^7.23.2", - "@redux-devtools/ui": "^1.3.1", - "@types/lodash": "^4.14.201", - "@types/prop-types": "^15.7.10", - "@types/redux-devtools-themes": "^1.0.3", - "hex-rgba": "^1.0.2", - "immutable": "^4.3.4", - "jss": "^10.10.0", - "jss-preset-default": "^10.10.0", - "lodash.debounce": "^4.0.8", - "prop-types": "^15.8.1", - "react-base16-styling": "^0.9.1", - "react-json-tree": "^0.18.0", - "redux-devtools-themes": "^1.0.0" + "@types/filesystem": "*", + "@types/har-format": "*" } }, - "@redux-devtools/slider-monitor": { + "async-stream-emitter": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/slider-monitor/-/slider-monitor-4.1.0.tgz", - "integrity": "sha512-p0jDgLcmuOQyJTlMAwjSZ8wxioqV802IXtm7BsxGq9021WsmyhEGRhqfu4XvXUt95sBHNsRsm9yE5XVTMmlAtw==", + "resolved": "https://registry.npmjs.org/async-stream-emitter/-/async-stream-emitter-4.1.0.tgz", + "integrity": "sha512-cfPZYjHkhCdHSR+eux71vOU8+8Xb23oLyxccAjwYHgOxDb3+qSDb2HV1Y0Hmu39vZlse2cm15CUShLiVYXHCmQ==", "dev": true, "requires": { - "@babel/runtime": "^7.23.2", - "@redux-devtools/ui": "^1.3.1", - "@types/prop-types": "^15.7.10", - "@types/redux-devtools-themes": "^1.0.3", - "prop-types": "^15.8.1", - "redux-devtools-themes": "^1.0.0" - } - }, - "@types/chrome": { - "version": "0.0.251", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.251.tgz", - "integrity": "sha512-UF+yr0LEKWWGsKxQ5A3XOSF5SNoU1ctW3pXcWJPpT8OOUTEspYeaLU8spDKe+6xalXeMTS0TBrX1g0b6qlWmkw==", - "dev": true, - "requires": { - "@types/filesystem": "*", - "@types/har-format": "*" + "stream-demux": "^8.1.0" } }, "body-parser": { @@ -33343,6 +33285,15 @@ "unpipe": "1.0.0" } }, + "bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "requires": { + "run-applescript": "^7.0.0" + } + }, "chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", @@ -33355,6 +33306,13 @@ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true }, + "d3tooltip": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/d3tooltip/-/d3tooltip-4.0.0.tgz", + "integrity": "sha512-jQRmF04fL9G+sROeN1/CZaHRqE31Pl7Qky6qIJ2MkecTwXZtgfDgmgrPZ0VS5Ewnj9sYWJ6CAl6fYI4PNn2Fpw==", + "dev": true, + "requires": {} + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -33364,18 +33322,52 @@ "ms": "2.0.0" } }, + "default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "requires": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + } + }, + "default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true + }, "define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true }, + "is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "requires": { + "is-inside-container": "^1.0.0" + } + }, "js-tokens": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz", "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==", "dev": true }, + "map2tree": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/map2tree/-/map2tree-4.0.0.tgz", + "integrity": "sha512-nzoeFAjW5yCwLFpV3A6EZS3kHD6H9y82S0KANc8tN2Mha+Ad2vIggDqxxYdI8lgghHzQL3yf/6tqlcoplRw8NA==", + "dev": true, + "requires": { + "lodash-es": "^4.17.21" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -33383,17 +33375,23 @@ "dev": true }, "open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/open/-/open-10.0.2.tgz", + "integrity": "sha512-GnYLdE+E3K8NeSE23N0g67/9q9AXRph5oTUbz6IbIgElPigEnQ2aHuqRge3y0JUr67qoc84xME5kF03fDc3fcA==", "dev": true, "requires": { - "default-browser": "^4.0.0", + "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" + "is-wsl": "^3.1.0" } }, + "ramda": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", + "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==", + "dev": true + }, "raw-body": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", @@ -33407,9 +33405,9 @@ } }, "react-redux": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.2.tgz", - "integrity": "sha512-xJKYI189VwfsFc4CJvHqHlDrzyFTY/3vZACbE+rr/zQ34Xx1wQfB4OTOSeOSNrF6BDVe8OOdxIrAnMGXA3ggfw==", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", "dev": true, "requires": { "@babel/runtime": "^7.12.1", @@ -33421,9 +33419,15 @@ } }, "regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", "dev": true }, "sc-formatter": { @@ -33469,6 +33473,16 @@ } } }, + "stream-demux": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/stream-demux/-/stream-demux-8.1.0.tgz", + "integrity": "sha512-20vtOmAj2EVzQZKZVmfyio16u/3QOKSvg+0ldgZeS+m2FNI1vKFoqggamagsPCXufdZ1Tk8VvAM/HV/YUmRbSg==", + "dev": true, + "requires": { + "consumable-stream": "^2.0.0", + "writable-consumable-stream": "^3.0.1" + } + }, "styled-components": { "version": "5.3.11", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", @@ -33493,10 +33507,19 @@ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "dev": true }, + "writable-consumable-stream": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/writable-consumable-stream/-/writable-consumable-stream-3.0.1.tgz", + "integrity": "sha512-rAOJTA/sMgXD/X6eMwbQJe49w+Fnkdx3iV5oUzdmiZ7Bwx03khqUnAKIpzp/hbI8q2EP5NfjXgIXN0MsipfHeg==", + "dev": true, + "requires": { + "consumable-stream": "^2.0.0" + } + }, "ws": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.1.tgz", - "integrity": "sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "requires": {} } @@ -33581,6 +33604,15 @@ "regenerator-runtime": "^0.14.0" } }, + "async-stream-emitter": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/async-stream-emitter/-/async-stream-emitter-4.1.0.tgz", + "integrity": "sha512-cfPZYjHkhCdHSR+eux71vOU8+8Xb23oLyxccAjwYHgOxDb3+qSDb2HV1Y0Hmu39vZlse2cm15CUShLiVYXHCmQ==", + "dev": true, + "requires": { + "stream-demux": "^8.1.0" + } + }, "regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", @@ -33613,6 +33645,25 @@ "ws": "^8.9.0" } }, + "stream-demux": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/stream-demux/-/stream-demux-8.1.0.tgz", + "integrity": "sha512-20vtOmAj2EVzQZKZVmfyio16u/3QOKSvg+0ldgZeS+m2FNI1vKFoqggamagsPCXufdZ1Tk8VvAM/HV/YUmRbSg==", + "dev": true, + "requires": { + "consumable-stream": "^2.0.0", + "writable-consumable-stream": "^3.0.1" + } + }, + "writable-consumable-stream": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/writable-consumable-stream/-/writable-consumable-stream-3.0.1.tgz", + "integrity": "sha512-rAOJTA/sMgXD/X6eMwbQJe49w+Fnkdx3iV5oUzdmiZ7Bwx03khqUnAKIpzp/hbI8q2EP5NfjXgIXN0MsipfHeg==", + "dev": true, + "requires": { + "consumable-stream": "^2.0.0" + } + }, "ws": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", @@ -33649,47 +33700,6 @@ } } }, - "@redux-devtools/ui": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@redux-devtools/ui/-/ui-1.3.1.tgz", - "integrity": "sha512-qtfau1zSmv6nSUrnesuxaos1V7r89HQV2Hq/thcRcyHd0Y0OYgpQlnwEK3wkK1esLU2BhEUUb85DC8IdJ3l6Sg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.23.2", - "@rjsf/core": "^4.2.3", - "@types/base16": "^1.0.5", - "@types/codemirror": "^5.60.13", - "@types/json-schema": "^7.0.15", - "@types/prop-types": "^15.7.10", - "@types/redux-devtools-themes": "^1.0.3", - "@types/simple-element-resize-detector": "^1.3.3", - "base16": "^1.0.0", - "codemirror": "^5.65.15", - "color": "^4.2.3", - "prop-types": "^15.8.1", - "react-icons": "^4.11.0", - "react-select": "^5.8.0", - "redux-devtools-themes": "^1.0.0", - "simple-element-resize-detector": "^1.3.0" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.14.0" - } - }, - "regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true - } - } - }, "@redux-devtools/utils": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@redux-devtools/utils/-/utils-3.0.0.tgz", @@ -34239,12 +34249,6 @@ "@babel/types": "^7.3.0" } }, - "@types/base16": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/base16/-/base16-1.0.5.tgz", - "integrity": "sha512-OzOWrTluG9cwqidEzC/Q6FAmIPcnZfm8BFRlIx0+UIUqnuAmi5OS88O0RpT3Yz6qdmqObvUhasrbNsCofE4W9A==", - "dev": true - }, "@types/big.js": { "version": "6.1.6", "resolved": "https://registry.npmjs.org/@types/big.js/-/big.js-6.1.6.tgz", @@ -34329,9 +34333,9 @@ } }, "@types/d3": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz", - "integrity": "sha512-jIfNVK0ZlxcuRDKtRS/SypEyOQ6UHaFQBKv032X45VvxSJ6Yi5G9behy9h6tNTHTDGh5Vq+KbmBjUWLgY4meCA==", + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", "dev": true, "requires": { "@types/d3-array": "*", @@ -34367,45 +34371,45 @@ } }, "@types/d3-array": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.7.tgz", - "integrity": "sha512-4/Q0FckQ8TBjsB0VdGFemJOG8BLXUB2KKlL0VmZ+eOYeOnTb/wDRQqYWpBmQ6IlvWkXwkYiot+n9Px2aTJ7zGQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", "dev": true }, "@types/d3-axis": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.3.tgz", - "integrity": "sha512-SE3x/pLO/+GIHH17mvs1uUVPkZ3bHquGzvZpPAh4yadRy71J93MJBpgK/xY8l9gT28yTN1g9v3HfGSFeBMmwZw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", "dev": true, "requires": { "@types/d3-selection": "*" } }, "@types/d3-brush": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.3.tgz", - "integrity": "sha512-MQ1/M/B5ifTScHSe5koNkhxn2mhUPqXjGuKjjVYckplAPjP9t2I2sZafb/YVHDwhoXWZoSav+Q726eIbN3qprA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", "dev": true, "requires": { "@types/d3-selection": "*" } }, "@types/d3-chord": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.3.tgz", - "integrity": "sha512-keuSRwO02c7PBV3JMWuctIfdeJrVFI7RpzouehvBWL4/GGUB3PBNg/9ZKPZAgJphzmS2v2+7vr7BGDQw1CAulw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", "dev": true }, "@types/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", "dev": true }, "@types/d3-contour": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.3.tgz", - "integrity": "sha512-x7G/tdDZt4m09XZnG2SutbIuQqmkNYqR9uhDMdPlpJbcwepkEjEWG29euFcgVA1k6cn92CHdDL9Z+fOnxnbVQw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", "dev": true, "requires": { "@types/d3-array": "*", @@ -34413,174 +34417,180 @@ } }, "@types/d3-delaunay": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz", - "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", "dev": true }, "@types/d3-dispatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.3.tgz", - "integrity": "sha512-Df7KW3Re7G6cIpIhQtqHin8yUxUHYAqiE41ffopbmU5+FifYUNV7RVyTg8rQdkEagg83m14QtS8InvNb95Zqug==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==", "dev": true }, "@types/d3-drag": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.3.tgz", - "integrity": "sha512-82AuQMpBQjuXeIX4tjCYfWjpm3g7aGCfx6dFlxX2JlRaiME/QWcHzBsINl7gbHCODA2anPYlL31/Trj/UnjK9A==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", "dev": true, "requires": { "@types/d3-selection": "*" } }, "@types/d3-dsv": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.2.tgz", - "integrity": "sha512-DooW5AOkj4AGmseVvbwHvwM/Ltu0Ks0WrhG6r5FG9riHT5oUUTHz6xHsHqJSVU8ZmPkOqlUEY2obS5C9oCIi2g==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", "dev": true }, "@types/d3-ease": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", - "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", "dev": true }, "@types/d3-fetch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.3.tgz", - "integrity": "sha512-/EsDKRiQkby3Z/8/AiZq8bsuLDo/tYHnNIZkUpSeEHWV7fHUl6QFBjvMPbhkKGk9jZutzfOkGygCV7eR/MkcXA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", "dev": true, "requires": { "@types/d3-dsv": "*" } }, "@types/d3-force": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.5.tgz", - "integrity": "sha512-EGG+IWx93ESSXBwfh/5uPuR9Hp8M6o6qEGU7bBQslxCvrdUBQZha/EFpu/VMdLU4B0y4Oe4h175nSm7p9uqFug==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.9.tgz", + "integrity": "sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==", "dev": true }, "@types/d3-format": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz", - "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", "dev": true }, "@types/d3-geo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.0.4.tgz", - "integrity": "sha512-kmUK8rVVIBPKJ1/v36bk2aSgwRj2N/ZkjDT+FkMT5pgedZoPlyhaG62J+9EgNIgUXE6IIL0b7bkLxCzhE6U4VQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", "dev": true, "requires": { "@types/geojson": "*" } }, "@types/d3-hierarchy": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.3.tgz", - "integrity": "sha512-GpSK308Xj+HeLvogfEc7QsCOcIxkDwLhFYnOoohosEzOqv7/agxwvJER1v/kTC+CY1nfazR0F7gnHo7GE41/fw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.6.tgz", + "integrity": "sha512-qlmD/8aMk5xGorUvTUWHCiumvgaUXYldYjNVOWtYoTYY/L+WwIEAmJxUmTgr9LoGNG0PPAOmqMDJVDPc7DOpPw==", "dev": true }, "@types/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", "dev": true, "requires": { "@types/d3-color": "*" } }, "@types/d3-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", - "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.2.tgz", + "integrity": "sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA==", "dev": true }, "@types/d3-polygon": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.0.tgz", - "integrity": "sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", "dev": true }, "@types/d3-quadtree": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.2.tgz", - "integrity": "sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", "dev": true }, "@types/d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", "dev": true }, "@types/d3-scale": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.4.tgz", - "integrity": "sha512-eq1ZeTj0yr72L8MQk6N6heP603ubnywSDRfNpi5enouR112HzGLS6RIvExCzZTraFF4HdzNpJMwA/zGiMoHUUw==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", "dev": true, "requires": { "@types/d3-time": "*" } }, "@types/d3-scale-chromatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", - "integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==", "dev": true }, "@types/d3-selection": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.6.tgz", - "integrity": "sha512-2ACr96USZVjXR9KMD9IWi1Epo4rSDKnUtYn6q2SPhYxykvXTw9vR77lkFNruXVg4i1tzQtBxeDMx0oNvJWbF1w==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz", + "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==", "dev": true }, "@types/d3-shape": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.2.tgz", - "integrity": "sha512-NN4CXr3qeOUNyK5WasVUV8NCSAx/CRVcwcb0BuuS1PiTqwIm6ABi1SyasLZ/vsVCFDArF+W4QiGzSry1eKYQ7w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", "dev": true, "requires": { "@types/d3-path": "*" } }, "@types/d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==", "dev": true }, "@types/d3-time-format": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.0.tgz", - "integrity": "sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", "dev": true }, "@types/d3-timer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", - "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", "dev": true }, "@types/d3-transition": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.4.tgz", - "integrity": "sha512-512a4uCOjUzsebydItSXsHrPeQblCVk8IKjqCUmrlvBWkkVh3donTTxmURDo1YPwIVDh5YVwCAO6gR4sgimCPQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz", + "integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==", "dev": true, "requires": { "@types/d3-selection": "*" } }, "@types/d3-zoom": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.4.tgz", - "integrity": "sha512-cqkuY1ah9ZQre2POqjSLcM8g40UVya/qwEUrNYP2/rCVljbmqKCVcv+ebvwhlI5azIbSEL7m+os6n+WlYA43aA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", "dev": true, "requires": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, + "@types/diff-match-patch": { + "version": "1.0.36", + "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", + "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", + "dev": true + }, "@types/elliptic": { "version": "6.4.14", "resolved": "https://registry.npmjs.org/@types/elliptic/-/elliptic-6.4.14.tgz", @@ -34664,9 +34674,9 @@ "dev": true }, "@types/geojson": { - "version": "7946.0.10", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", - "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==", + "version": "7946.0.13", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.13.tgz", + "integrity": "sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==", "dev": true }, "@types/get-params": { @@ -34814,9 +34824,9 @@ } }, "@types/lodash": { - "version": "4.14.202", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", - "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==" + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==" }, "@types/lodash.debounce": { "version": "4.0.9", @@ -34868,26 +34878,13 @@ } }, "@types/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.10.tgz", + "integrity": "sha512-PPpPK6F9ALFTn59Ka3BaL+qGuipRfxNE8qVgkp0bVixeiR2c2/L+IVOiBdu9JhhT22sWnQEp6YyHGI2b2+CMcA==", "dev": true, "requires": { "@types/node": "*", - "form-data": "^3.0.0" - }, - "dependencies": { - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } + "form-data": "^4.0.0" } }, "@types/parse-json": { @@ -34947,19 +34944,10 @@ "@types/react": "*" } }, - "@types/redux-devtools-themes": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/redux-devtools-themes/-/redux-devtools-themes-1.0.3.tgz", - "integrity": "sha512-KqiQ2+6VTb1Yn02+ZNQsB0XcoJTu/W4AhnIUaeTnkVFie5YWMoeSpW4IOFYJ2/HJ+wi1kLw+PHDgjZ3t+M6IRw==", - "dev": true, - "requires": { - "@types/base16": "*" - } - }, "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", "dev": true, "requires": { "@types/node": "*" @@ -35027,9 +35015,9 @@ "dev": true }, "@types/styled-components": { - "version": "5.1.26", - "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.26.tgz", - "integrity": "sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==", + "version": "5.1.34", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.34.tgz", + "integrity": "sha512-mmiVvwpYklFIv9E8qfxuPyIt/OuyIrn6gMOAMOFUO3WJfSrSE+sGUoa4PiZj77Ut7bKZpaa6o1fBKS/4TOEvnA==", "dev": true, "requires": { "@types/hoist-non-react-statics": "*", @@ -35969,13 +35957,13 @@ "dev": true }, "ag-auth": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ag-auth/-/ag-auth-2.0.0.tgz", - "integrity": "sha512-gk5BgWzijPUaCMDYFtuggIlUDWGMlMSOdF/tdQa5sM7Nh0KowohFh3P3sx06gegufKWzWF97dwP/7hLg3CkqWg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ag-auth/-/ag-auth-2.0.1.tgz", + "integrity": "sha512-PpIBdQiTIzp+ELJ8pPHVvxxCIUGXONPSecFgh34zyr/Z3vpqPmT/S21u6lzt1jGPulOTic4Wm9FpVGzC/BYHSQ==", "dev": true, "requires": { "jsonwebtoken": "^9.0.0", - "sc-errors": "^2.0.0" + "sc-errors": "^2.0.2" } }, "ag-channel": { @@ -35988,23 +35976,23 @@ } }, "ag-request": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ag-request/-/ag-request-1.0.0.tgz", - "integrity": "sha512-2f7I0cQLMVyGAqjSewVMEFuAsJsIY6egdE16UHS636r+8c6Oevrv0j6SrOIXyRN6yuNT4PBuhiKmrhHbh9OvEg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ag-request/-/ag-request-1.0.1.tgz", + "integrity": "sha512-3F4pDpLy9mxOXop7LoWE78J5g2jmiEJ0gJfzcECOsf/NaCfyeNmOdNLDVM5dS4Hvbi9T+HENL4DmXq5XSotPaA==", "dev": true, "requires": { - "sc-errors": "^2.0.0" + "sc-errors": "^2.0.2" } }, "ag-simple-broker": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ag-simple-broker/-/ag-simple-broker-5.0.0.tgz", - "integrity": "sha512-LMwfBdoNm+8ac/RkgW6z1mjIvy2cgEqWa9cJUD5sc5uiJkIn/kXhVRlPdfa/MJtxPivo9DRhKb9DlSp2gCv+Cg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ag-simple-broker/-/ag-simple-broker-6.0.1.tgz", + "integrity": "sha512-pDlHotEoC9uV2Uk8DrR570QXMiUd9QYwJZXWDlBJZEbYTHzMJLEJDJStxmn7Kp4eT7SIGoPFuzELYZyMYNZ2Kw==", "dev": true, "requires": { "ag-channel": "^5.0.0", - "async-stream-emitter": "^4.0.0", - "stream-demux": "^8.0.0" + "async-stream-emitter": "^7.0.1", + "stream-demux": "^10.0.1" } }, "agent-base": { @@ -36379,12 +36367,12 @@ } }, "async-stream-emitter": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/async-stream-emitter/-/async-stream-emitter-4.1.0.tgz", - "integrity": "sha512-cfPZYjHkhCdHSR+eux71vOU8+8Xb23oLyxccAjwYHgOxDb3+qSDb2HV1Y0Hmu39vZlse2cm15CUShLiVYXHCmQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/async-stream-emitter/-/async-stream-emitter-7.0.1.tgz", + "integrity": "sha512-1bgA3iZ80rCBX2LocvsyZPy0QB3/xM+CsXBze2HDHLmshOqx2JlAANGq23djaJ48e9fpcKzTzS1QM0hAKKI0UQ==", "dev": true, "requires": { - "stream-demux": "^8.1.0" + "stream-demux": "^10.0.1" } }, "asynciterator.prototype": { @@ -36680,12 +36668,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "base16": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", - "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==", - "dev": true - }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -36946,12 +36928,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "broadcast-channel": { @@ -37704,9 +37686,9 @@ } }, "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, "requires": { "mimic-response": "^1.0.0" @@ -38358,16 +38340,6 @@ "postcss-value-parser": "^4.0.2" } }, - "css-vendor": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", - "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.3", - "is-in-browser": "^1.0.2" - } - }, "css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -38409,14 +38381,14 @@ } }, "csstype": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", - "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "d3": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", - "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", "dev": true, "requires": { "d3-array": "3", @@ -38740,12 +38712,29 @@ } }, "d3tooltip": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3tooltip/-/d3tooltip-3.0.0.tgz", - "integrity": "sha512-bQSxCU0D99KEvpHZjB5oiXToc98FxIHOAAOJRncZMsU3iUU18a2X//FgPnuI0K7wVjfKfgr4dXw5sEvTXSyu6Q==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3tooltip/-/d3tooltip-3.0.1.tgz", + "integrity": "sha512-y1CqKBZE/PUVvXzPeJrAxRh5pj6hzz8/oFdEpgGI0TbhFVhQ1xBv0wCFEz6BzZ94t+5Y+n1x+Q6rGMDtlzy7rA==", "dev": true, "requires": { - "@babel/runtime": "^7.20.6" + "@babel/runtime": "^7.23.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.7.tgz", + "integrity": "sha512-w06OXVOFso7LcbzMiDGt+3X7Rh7Ho8MmgPoWU3rarH+8upf+wSU/grlGbWzQyr3DkdN6ZeuMFjpdwW0Q+HxobA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.14.0" + } + }, + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + } } }, "damerau-levenshtein": { @@ -39345,9 +39334,9 @@ "dev": true }, "electron": { - "version": "25.9.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-25.9.0.tgz", - "integrity": "sha512-wgscxf2ORHL/8mAQfy7l9rVDG//wrG9RUQndG508kCCMHRq9deFyZ4psOMzySheBRSfGMcFoRFYSlkAeZr8cFg==", + "version": "27.2.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-27.2.0.tgz", + "integrity": "sha512-no/iMICVLI/5G0IqgKFbB89HDN88DWwKeRO+dPfJPkpJISdEX8Cx/sMEOFuuRa4VNInNe5CKCqRWExK5z3AdcQ==", "dev": true, "requires": { "@electron/get": "^2.0.0", @@ -39356,9 +39345,9 @@ }, "dependencies": { "@types/node": { - "version": "18.18.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.9.tgz", - "integrity": "sha512-0f5klcuImLnG4Qreu9hPj/rEfFq6YRc5n2mAjSsH+ec/mJL+3voBH0+8T7o8RpFjH7ovc+TRsL/c7OYIQsPTfQ==", + "version": "18.19.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.4.tgz", + "integrity": "sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A==", "dev": true, "requires": { "undici-types": "~5.26.4" @@ -39965,9 +39954,9 @@ } }, "eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "requires": {} }, @@ -40621,9 +40610,9 @@ } }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -41455,9 +41444,9 @@ "dev": true }, "graphql": { - "version": "16.8.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.0.tgz", - "integrity": "sha512-0oKGaR+y3qcS5mCu1vb7KG+a89vjn06C7Ihq/dDl3jA+A8B3TKomvi3CiEcVLJQGalbu8F52LxkOym7U5sSfbg==", + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", + "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", "dev": true }, "growly": { @@ -41728,9 +41717,9 @@ } }, "html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", "dev": true }, "html-escaper": { @@ -41817,9 +41806,9 @@ } }, "html-webpack-plugin": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", - "integrity": "sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz", + "integrity": "sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==", "dev": true, "requires": { "@types/html-minifier-terser": "^6.0.0", @@ -41974,12 +41963,6 @@ "integrity": "sha512-Tkv80jtvbnkK3mYWxPZePGFpQ/tT3HNSs/sasF9P2YfkMezDl3ON37YN6jUUI4eTg5LcyVynlb6r4eyvOmspvg==", "dev": true }, - "hyphenate-style-name": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", - "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", - "dev": true - }, "i18next": { "version": "23.11.0", "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.11.0.tgz", @@ -42177,9 +42160,9 @@ "dev": true }, "immutable": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", - "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, "import-fresh": { @@ -42467,12 +42450,6 @@ "is-extglob": "^2.1.1" } }, - "is-in-browser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", - "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==", - "dev": true - }, "is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -44576,168 +44553,6 @@ "verror": "1.10.0" } }, - "jss": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", - "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "csstype": "^3.0.2", - "is-in-browser": "^1.1.3", - "tiny-warning": "^1.0.2" - } - }, - "jss-plugin-camel-case": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", - "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "hyphenate-style-name": "^1.0.3", - "jss": "10.10.0" - } - }, - "jss-plugin-compose": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-compose/-/jss-plugin-compose-10.10.0.tgz", - "integrity": "sha512-F5kgtWpI2XfZ3Z8eP78tZEYFdgTIbpA/TMuX3a8vwrNolYtN1N4qJR/Ob0LAsqIwCMLojtxN7c7Oo/+Vz6THow==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "jss-plugin-default-unit": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", - "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "jss-plugin-expand": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-expand/-/jss-plugin-expand-10.10.0.tgz", - "integrity": "sha512-ymT62W2OyDxBxr7A6JR87vVX9vTq2ep5jZLIdUSusfBIEENLdkkc0lL/Xaq8W9s3opUq7R0sZQpzRWELrfVYzA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "jss-plugin-extend": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-extend/-/jss-plugin-extend-10.10.0.tgz", - "integrity": "sha512-sKYrcMfr4xxigmIwqTjxNcHwXJIfvhvjTNxF+Tbc1NmNdyspGW47Ey6sGH8BcQ4FFQhLXctpWCQSpDwdNmXSwg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "jss-plugin-global": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", - "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "jss-plugin-nested": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", - "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "jss-plugin-props-sort": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", - "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0" - } - }, - "jss-plugin-rule-value-function": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", - "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "jss-plugin-rule-value-observable": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-observable/-/jss-plugin-rule-value-observable-10.10.0.tgz", - "integrity": "sha512-ZLMaYrR3QE+vD7nl3oNXuj79VZl9Kp8/u6A1IbTPDcuOu8b56cFdWRZNZ0vNr8jHewooEeq2doy8Oxtymr2ZPA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "symbol-observable": "^1.2.0" - } - }, - "jss-plugin-template": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-template/-/jss-plugin-template-10.10.0.tgz", - "integrity": "sha512-ocXZBIOJOA+jISPdsgkTs8wwpK6UbsvtZK5JI7VUggTD6LWKbtoxUzadd2TpfF+lEtlhUmMsCkTRNkITdPKa6w==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "tiny-warning": "^1.0.2" - } - }, - "jss-plugin-vendor-prefixer": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", - "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "css-vendor": "^2.0.8", - "jss": "10.10.0" - } - }, - "jss-preset-default": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/jss-preset-default/-/jss-preset-default-10.10.0.tgz", - "integrity": "sha512-GL175Wt2FGhjE+f+Y3aWh+JioL06/QWFgZp53CbNNq6ZkVU0TDplD8Bxm9KnkotAYn3FlplNqoW5CjyLXcoJ7Q==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.10.0", - "jss-plugin-camel-case": "10.10.0", - "jss-plugin-compose": "10.10.0", - "jss-plugin-default-unit": "10.10.0", - "jss-plugin-expand": "10.10.0", - "jss-plugin-extend": "10.10.0", - "jss-plugin-global": "10.10.0", - "jss-plugin-nested": "10.10.0", - "jss-plugin-props-sort": "10.10.0", - "jss-plugin-rule-value-function": "10.10.0", - "jss-plugin-rule-value-observable": "10.10.0", - "jss-plugin-template": "10.10.0", - "jss-plugin-vendor-prefixer": "10.10.0" - } - }, "jsx-ast-utils": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", @@ -44849,9 +44664,9 @@ "dev": true }, "knex": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/knex/-/knex-2.5.1.tgz", - "integrity": "sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz", + "integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==", "dev": true, "requires": { "colorette": "2.0.19", @@ -44863,7 +44678,7 @@ "getopts": "2.3.0", "interpret": "^2.2.0", "lodash": "^4.17.21", - "pg-connection-string": "2.6.1", + "pg-connection-string": "2.6.2", "rechoir": "^0.8.0", "resolve-from": "^5.0.0", "tarn": "^3.0.2", @@ -45263,12 +45078,6 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, - "lodash.curry": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", - "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==", - "dev": true - }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -47027,9 +46836,9 @@ "dev": true }, "pg-connection-string": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", - "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==", "dev": true }, "picocolors": { @@ -47605,38 +47414,23 @@ } }, "react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "requires": { "loose-envify": "^1.1.0" } }, "react-base16-styling": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.9.1.tgz", - "integrity": "sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.10.0.tgz", + "integrity": "sha512-H1k2eFB6M45OaiRru3PBXkuCcn2qNmx+gzLb4a9IPMR7tMH8oBRXU5jGbPDYG1Hz+82d88ED0vjR8BmqU3pQdg==", "dev": true, "requires": { - "@babel/runtime": "^7.16.7", - "@types/base16": "^1.0.2", - "@types/lodash": "^4.14.178", - "base16": "^1.0.0", - "color": "^3.2.1", - "csstype": "^3.0.10", - "lodash.curry": "^4.1.1" - }, - "dependencies": { - "color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dev": true, - "requires": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - } + "@types/lodash": "^4.17.0", + "color": "^4.2.3", + "csstype": "^3.1.3", + "lodash-es": "^4.17.21" } }, "react-dom": { @@ -47700,9 +47494,9 @@ } }, "react-icons": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz", - "integrity": "sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz", + "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==", "dev": true, "requires": {} }, @@ -47735,14 +47529,13 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "react-json-tree": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.18.0.tgz", - "integrity": "sha512-Qe6HKSXrr++n9Y31nkRJ3XvQMATISpqigH1vEKhLwB56+nk5thTP0ITThpjxY6ZG/ubpVq/aEHIcyLP/OPHxeA==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.19.0.tgz", + "integrity": "sha512-PqT1WRVcWP+RROsZPQfNEKIC1iM/ZMfY4g5jN6oDnXp5593PPRAYgoHcgYCDjflAHQMtxl8XGdlTwIBdEGUXvw==", "dev": true, "requires": { - "@babel/runtime": "^7.20.6", - "@types/lodash": "^4.14.191", - "react-base16-styling": "^0.9.1" + "@types/lodash": "^4.17.0", + "react-base16-styling": "^0.10.0" } }, "react-loading-skeleton": { @@ -47892,15 +47685,6 @@ "@babel/runtime": "^7.9.2" } }, - "redux-devtools-themes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redux-devtools-themes/-/redux-devtools-themes-1.0.0.tgz", - "integrity": "sha512-hBWqdZX+dioMWnTjf8+uSm0q1wCdYO4kU5gERzHcMMbu0Qg7JDR42TnJ6GHJ6r7k/tIpsCSygc9U0ehAtR24TQ==", - "dev": true, - "requires": { - "base16": "^1.0.0" - } - }, "redux-persist": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", @@ -48486,9 +48270,9 @@ } }, "sc-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/sc-errors/-/sc-errors-2.0.1.tgz", - "integrity": "sha512-JoVhq3Ud+3Ujv2SIG7W0XtjRHsrNgl6iXuHHsh0s+Kdt5NwI6N2EGAZD4iteitdDv68ENBkpjtSvN597/wxPSQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sc-errors/-/sc-errors-2.0.3.tgz", + "integrity": "sha512-HNpClBWpo7zxLBnhH0U/FbC19Gl3OJlVyPxo9Q2eomfdWgYfd84uhqe0LRgybc+nSpcYjtF08+/dKPLugLMMeQ==", "dev": true }, "schema-utils": { @@ -48957,21 +48741,21 @@ "optional": true }, "socketcluster-server": { - "version": "17.4.1", - "resolved": "https://registry.npmjs.org/socketcluster-server/-/socketcluster-server-17.4.1.tgz", - "integrity": "sha512-ElKD9U7EncoWNGYOL+G6UAxuYmui1fnawpyhZIpFG/A/lDNGwHsQLNIpJXh1SB4BpoQLn2q4ewwnUK3Mse5mfA==", + "version": "19.0.1", + "resolved": "https://registry.npmjs.org/socketcluster-server/-/socketcluster-server-19.0.1.tgz", + "integrity": "sha512-IfMZxTkzvqOUExqiVxkxE2DjN/tap6WpbplatptsHKa58wfN6SdfcYCYeR3hlUBOx+cG09/hPxlN+R5tYZF0Mg==", "dev": true, "requires": { "ag-auth": "^2.0.0", - "ag-request": "^1.0.0", - "ag-simple-broker": "^5.0.0", - "async-stream-emitter": "^4.0.0", + "ag-request": "^1.0.1", + "ag-simple-broker": "^6.0.0", + "async-stream-emitter": "^7.0.1", "base64id": "^2.0.0", "clone-deep": "^4.0.1", - "sc-errors": "^2.0.1", + "sc-errors": "^2.0.3", "sc-formatter": "^4.0.0", - "stream-demux": "^8.0.0", - "writable-consumable-stream": "^2.0.0", + "stream-demux": "^10.0.1", + "writable-consumable-stream": "^4.1.0", "ws": "^8.9.0" }, "dependencies": { @@ -48982,9 +48766,9 @@ "dev": true }, "ws": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.1.tgz", - "integrity": "sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "requires": {} } @@ -49274,23 +49058,20 @@ } }, "stream-demux": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/stream-demux/-/stream-demux-8.1.0.tgz", - "integrity": "sha512-20vtOmAj2EVzQZKZVmfyio16u/3QOKSvg+0ldgZeS+m2FNI1vKFoqggamagsPCXufdZ1Tk8VvAM/HV/YUmRbSg==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/stream-demux/-/stream-demux-10.0.1.tgz", + "integrity": "sha512-QjTYLJWpZxZ6uL5R1JzgOzjvao8zDx78ec+uOjHNeVc/9TuasYLldoVrYARZeT1xI1hFYuiKf13IM8b4wamhHg==", "dev": true, "requires": { - "consumable-stream": "^2.0.0", - "writable-consumable-stream": "^3.0.1" + "consumable-stream": "^3.0.0", + "writable-consumable-stream": "^4.1.0" }, "dependencies": { - "writable-consumable-stream": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/writable-consumable-stream/-/writable-consumable-stream-3.0.1.tgz", - "integrity": "sha512-rAOJTA/sMgXD/X6eMwbQJe49w+Fnkdx3iV5oUzdmiZ7Bwx03khqUnAKIpzp/hbI8q2EP5NfjXgIXN0MsipfHeg==", - "dev": true, - "requires": { - "consumable-stream": "^2.0.0" - } + "consumable-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/consumable-stream/-/consumable-stream-3.0.0.tgz", + "integrity": "sha512-CnnsJ9OG9ouxAjt3pc63/DaerezRo/WudqU71pc5epaIUi7NHu2T4v+3f0nKbbCY7icS/TfQ1Satr9rwZ7Jwsg==", + "dev": true } } }, @@ -49589,12 +49370,6 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true - }, "symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -49816,12 +49591,6 @@ "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==", "dev": true }, - "tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", - "dev": true - }, "titleize": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", @@ -51563,12 +51332,20 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "writable-consumable-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/writable-consumable-stream/-/writable-consumable-stream-2.0.0.tgz", - "integrity": "sha512-SITambzxtPTFU/wR82h+zOKGBiEv5V8gC1mt8xvoE1/168ApEa8H+6s2UToYJo3VLL7sNYTaApKuPD+pZHMGJQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/writable-consumable-stream/-/writable-consumable-stream-4.1.0.tgz", + "integrity": "sha512-4cjCPd4Ayfbix0qqPCzMbnPPZKRh/cKeNCj05unybP3/sRkRAOxh7rSwbhxs3YB6G4/Z2p/2FRBEIQcTeB4jyw==", "dev": true, "requires": { - "consumable-stream": "^2.0.0" + "consumable-stream": "^3.0.0" + }, + "dependencies": { + "consumable-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/consumable-stream/-/consumable-stream-3.0.0.tgz", + "integrity": "sha512-CnnsJ9OG9ouxAjt3pc63/DaerezRo/WudqU71pc5epaIUi7NHu2T4v+3f0nKbbCY7icS/TfQ1Satr9rwZ7Jwsg==", + "dev": true + } } }, "write-file-atomic": { diff --git a/package.json b/package.json index 475187cac..25c5f45e0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "Casper Wallet", "description": "Securely manage your CSPR tokens and interact with dapps with the self-custody wallet for the Casper blockchain.", - "version": "1.10.0", + "version": "1.11.0", "author": "MAKE LLC", "scripts": { "devtools:redux": "redux-devtools --hostname=localhost", @@ -106,7 +106,7 @@ "@babel/preset-react": "7.18.6", "@babel/preset-typescript": "^7.23.3", "@playwright/test": "^1.39.0", - "@redux-devtools/cli": "^3.0.1", + "@redux-devtools/cli": "^4.0.0", "@redux-devtools/remote": "^0.9.1", "@testing-library/dom": "9.3.4", "@testing-library/jest-dom": "^6.4.2", @@ -131,7 +131,7 @@ "css-loader": "6.8.1", "eslint": "8.49.0", "eslint-config-airbnb": "^19.0.4", - "eslint-config-prettier": "^8.3.0", + "eslint-config-prettier": "^9.1.0", "eslint-config-react-app": "^7.0.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-flowtype": "^8.0.3", @@ -143,7 +143,7 @@ "file-loader": "^6.2.0", "fs-extra": "11.1.1", "html-loader": "4.2.0", - "html-webpack-plugin": "^5.5.3", + "html-webpack-plugin": "^5.6.0", "husky": "8.0.2", "i18next-conv": "14.0.0", "jest": "29.3.1", diff --git a/scripts/build_all.sh b/scripts/build_all.sh index 2e5e60028..80f71e514 100755 --- a/scripts/build_all.sh +++ b/scripts/build_all.sh @@ -1,3 +1,3 @@ HASH=$(git rev-parse --short HEAD) -npm run build:chrome && npm run build:firefox && cd ./build && zip -r casper-wallet#$HASH.zip ./* && npm run build:src +npm run build:chrome && npm run build:firefox && cd ./build && zip -r casper-wallet-1.11.0.rc5#$HASH.zip ./* && npm run build:src diff --git a/src/apps/import-account-with-file/app-router.tsx b/src/apps/import-account-with-file/app-router.tsx index 710f4c748..0d81c423f 100644 --- a/src/apps/import-account-with-file/app-router.tsx +++ b/src/apps/import-account-with-file/app-router.tsx @@ -4,11 +4,10 @@ import { HashRouter, Route, Routes } from 'react-router-dom'; import { useUserActivityTracker } from '@hooks/use-user-activity-tracker'; import '@libs/i18n/i18n'; -import { HeaderPopup, LayoutWindow } from '@libs/layout'; import { ImportAccountWithFilePage } from './pages/import-account-with-file'; -import { ImportAccountWithFileFailureContentPage } from './pages/import-account-with-file-failure'; -import { ImportAccountWithFileSuccessContentPage } from './pages/import-account-with-file-success'; +import { ImportAccountWithFileFailurePage } from './pages/import-account-with-file-failure'; +import { ImportAccountWithFileSuccessPage } from './pages/import-account-with-file-success'; import { ImportAccountWithFileUploadPage } from './pages/import-account-with-file-upload'; import { RouterPath } from './router'; @@ -28,21 +27,11 @@ export function AppRouter() { /> } - renderContent={() => } - /> - } + element={} /> } - renderContent={() => } - /> - } + element={} /> diff --git a/src/apps/import-account-with-file/pages/import-account-with-file-failure/content.tsx b/src/apps/import-account-with-file/pages/import-account-with-file-failure/content.tsx new file mode 100644 index 000000000..e046ec40f --- /dev/null +++ b/src/apps/import-account-with-file/pages/import-account-with-file-failure/content.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import { useTypedLocation } from '@import-account-with-file/router'; + +import { + ContentContainer, + IllustrationContainer, + ParagraphContainer, + SpacingSize +} from '@libs/layout'; +import { SvgIcon, Typography } from '@libs/ui/components'; + +export function ImportAccountWithFileFailureContentPage() { + const { t } = useTranslation(); + const location = useTypedLocation(); + const state = location.state; + + return ( + + + + + + + Something went wrong + + + + + {state?.importAccountStatusMessage + ? state.importAccountStatusMessage + : t( + ': We couldn’t import your account. Please confirm that you’re importing a file containing your secret key (not to be confused with your public key).' + )} + + + + ); +} diff --git a/src/apps/import-account-with-file/pages/import-account-with-file-failure/index.tsx b/src/apps/import-account-with-file/pages/import-account-with-file-failure/index.tsx index e69b48ffa..79831c7a1 100644 --- a/src/apps/import-account-with-file/pages/import-account-with-file-failure/index.tsx +++ b/src/apps/import-account-with-file/pages/import-account-with-file-failure/index.tsx @@ -1,60 +1,37 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { - RouterPath, - useTypedLocation, - useTypedNavigate -} from '@import-account-with-file/router'; +import { RouterPath, useTypedNavigate } from '@import-account-with-file/router'; import { closeCurrentWindow } from '@background/close-current-window'; import { - ContentContainer, - FooterButtonsAbsoluteContainer, - IllustrationContainer, - ParagraphContainer, - SpacingSize + FooterButtonsContainer, + HeaderPopup, + LayoutWindow } from '@libs/layout'; -import { Button, SvgIcon, Typography } from '@libs/ui/components'; +import { Button } from '@libs/ui/components'; + +import { ImportAccountWithFileFailureContentPage } from './content'; -export function ImportAccountWithFileFailureContentPage() { +export const ImportAccountWithFileFailurePage = () => { const { t } = useTranslation(); const navigate = useTypedNavigate(); - const location = useTypedLocation(); - const state = location.state; return ( - - - - - - - Something went wrong - - - - - {state?.importAccountStatusMessage - ? state.importAccountStatusMessage - : t( - ': We couldn’t import your account. Please confirm that you’re importing a file containing your secret key (not to be confused with your public key).' - )} - - - - - - - + } + renderContent={() => } + renderFooter={() => ( + + + + + )} + /> ); -} +}; diff --git a/src/apps/import-account-with-file/pages/import-account-with-file-success/content.tsx b/src/apps/import-account-with-file/pages/import-account-with-file-success/content.tsx new file mode 100644 index 000000000..0801d7f1e --- /dev/null +++ b/src/apps/import-account-with-file/pages/import-account-with-file-success/content.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import { + ContentContainer, + IllustrationContainer, + ParagraphContainer, + SpacingSize +} from '@libs/layout'; +import { SvgIcon, Typography } from '@libs/ui/components'; + +export function ImportAccountWithFileSuccessContentPage() { + const { t } = useTranslation(); + + return ( + + + + + + + Your account was successfully imported + + + + + + Imported accounts are distinguished by an ‘IMPORTED’ icon in the + account lists. + + + + + ); +} diff --git a/src/apps/import-account-with-file/pages/import-account-with-file-success/index.tsx b/src/apps/import-account-with-file/pages/import-account-with-file-success/index.tsx index 74ecc1f9e..dd7ad7576 100644 --- a/src/apps/import-account-with-file/pages/import-account-with-file-success/index.tsx +++ b/src/apps/import-account-with-file/pages/import-account-with-file-success/index.tsx @@ -4,44 +4,28 @@ import { Trans, useTranslation } from 'react-i18next'; import { closeCurrentWindow } from '@background/close-current-window'; import { - ContentContainer, - FooterButtonsAbsoluteContainer, - IllustrationContainer, - ParagraphContainer, - SpacingSize + FooterButtonsContainer, + HeaderPopup, + LayoutWindow } from '@libs/layout'; -import { Button, SvgIcon, Typography } from '@libs/ui/components'; +import { Button } from '@libs/ui/components'; -export function ImportAccountWithFileSuccessContentPage() { +import { ImportAccountWithFileSuccessContentPage } from './content'; + +export const ImportAccountWithFileSuccessPage = () => { const { t } = useTranslation(); return ( - - - - - - - Your account was successfully imported - - - - - - Imported accounts are distinguished by an ‘IMPORTED’ icon in the - account lists. - - - - - - - + } + renderContent={() => } + renderFooter={() => ( + + + + )} + /> ); -} +}; diff --git a/src/apps/onboarding/app-router.tsx b/src/apps/onboarding/app-router.tsx index e6d76fa3e..4438db7ae 100644 --- a/src/apps/onboarding/app-router.tsx +++ b/src/apps/onboarding/app-router.tsx @@ -16,6 +16,7 @@ import { CreateVaultPasswordPage } from '@onboarding/pages/create-vault-password import { OnboardingSuccessPage } from '@onboarding/pages/onboarding-success'; import { RecoverFromSecretPhrasePage } from '@onboarding/pages/recover-from-secret-phrase'; import { ResetWalletPage } from '@onboarding/pages/reset-wallet'; +import { SelectAccountsToRecoverPage } from '@onboarding/pages/select-accounts-to-recover'; import { UnlockWalletPage } from '@onboarding/pages/unlock-wallet'; import { WelcomePage } from '@onboarding/pages/welcome'; import { WriteDownSecretPhrasePage } from '@onboarding/pages/write-down-secret-phrase'; @@ -110,6 +111,10 @@ function AuthorizedUserRoutes({ path={RouterPath.RecoverFromSecretPhrase} element={} /> + } + /> - - Awesome, your secret phrase is confirmed! + + Step 6 + + + Awesome, your secret phrase is confirmed! + + diff --git a/src/apps/onboarding/pages/confirm-secret-phrase-success/index.tsx b/src/apps/onboarding/pages/confirm-secret-phrase-success/index.tsx index 747085eec..5e7731d42 100644 --- a/src/apps/onboarding/pages/confirm-secret-phrase-success/index.tsx +++ b/src/apps/onboarding/pages/confirm-secret-phrase-success/index.tsx @@ -30,7 +30,7 @@ export function ConfirmSecretPhraseSuccessPage() { )} renderContent={() => } renderFooter={() => ( - + diff --git a/src/apps/onboarding/pages/confirm-secret-phrase/content.tsx b/src/apps/onboarding/pages/confirm-secret-phrase/content.tsx index 1cb74f7be..0dbd19eb3 100644 --- a/src/apps/onboarding/pages/confirm-secret-phrase/content.tsx +++ b/src/apps/onboarding/pages/confirm-secret-phrase/content.tsx @@ -2,7 +2,12 @@ import React, { Dispatch, SetStateAction } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { SecretPhrase } from '@libs/crypto'; -import { TabPageContainer, TabTextContainer } from '@libs/layout'; +import { + SpacingSize, + TabPageContainer, + TabTextContainer, + VerticalSpaceContainer +} from '@libs/layout'; import { SecretPhraseWordsView, Typography, @@ -24,9 +29,14 @@ export function ConfirmSecretPhrasePageContent({ return ( - - Confirm your secret recovery phrase + + Step 5 + + + Confirm your secret recovery phrase + + diff --git a/src/apps/onboarding/pages/confirm-secret-phrase/index.tsx b/src/apps/onboarding/pages/confirm-secret-phrase/index.tsx index 23f26e9be..75a9a6896 100644 --- a/src/apps/onboarding/pages/confirm-secret-phrase/index.tsx +++ b/src/apps/onboarding/pages/confirm-secret-phrase/index.tsx @@ -80,7 +80,7 @@ export function ConfirmSecretPhrasePage({ /> )} renderFooter={() => ( - + diff --git a/src/apps/onboarding/pages/create-secret-phrase-confirmation/content.tsx b/src/apps/onboarding/pages/create-secret-phrase-confirmation/content.tsx index 82d2df0ad..6b23eb3d1 100644 --- a/src/apps/onboarding/pages/create-secret-phrase-confirmation/content.tsx +++ b/src/apps/onboarding/pages/create-secret-phrase-confirmation/content.tsx @@ -36,11 +36,16 @@ export function CreateSecretPhraseConfirmationPageContent() { return ( - - - Before we generate your secret recovery phrase, please remember - + + Step 3 + + + + Before we generate your secret recovery phrase, please remember + + + diff --git a/src/apps/onboarding/pages/create-secret-phrase-confirmation/index.tsx b/src/apps/onboarding/pages/create-secret-phrase-confirmation/index.tsx index cdff335b1..e41a14b93 100644 --- a/src/apps/onboarding/pages/create-secret-phrase-confirmation/index.tsx +++ b/src/apps/onboarding/pages/create-secret-phrase-confirmation/index.tsx @@ -35,6 +35,7 @@ export function CreateSecretPhraseConfirmationPage({ return ( ( @@ -43,7 +44,7 @@ export function CreateSecretPhraseConfirmationPage({ )} renderContent={() => } renderFooter={() => ( - + setIsChecked(currentValue => !currentValue)} diff --git a/src/apps/onboarding/pages/create-secret-phrase/content.tsx b/src/apps/onboarding/pages/create-secret-phrase/content.tsx index e42557b30..8705ead5c 100644 --- a/src/apps/onboarding/pages/create-secret-phrase/content.tsx +++ b/src/apps/onboarding/pages/create-secret-phrase/content.tsx @@ -1,7 +1,12 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { TabPageContainer, TabTextContainer } from '@libs/layout'; +import { + SpacingSize, + TabPageContainer, + TabTextContainer, + VerticalSpaceContainer +} from '@libs/layout'; import { Typography } from '@libs/ui/components'; export function CreateSecretPhraseContent() { @@ -9,9 +14,14 @@ export function CreateSecretPhraseContent() { return ( - - Create secret recovery phrase + + Step 2 + + + Create secret recovery phrase + + diff --git a/src/apps/onboarding/pages/create-vault-password/content.tsx b/src/apps/onboarding/pages/create-vault-password/content.tsx index 954536df2..a8edef859 100644 --- a/src/apps/onboarding/pages/create-vault-password/content.tsx +++ b/src/apps/onboarding/pages/create-vault-password/content.tsx @@ -1,7 +1,12 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { TabPageContainer, TabTextContainer } from '@libs/layout'; +import { + SpacingSize, + TabPageContainer, + TabTextContainer, + VerticalSpaceContainer +} from '@libs/layout'; import { Typography } from '@libs/ui/components'; import { minPasswordLength } from '@libs/ui/forms/form-validation-rules'; @@ -16,9 +21,14 @@ export function CreateVaultPasswordPageContent({ return ( - - Create password + + Step 1 + + + Create password + + diff --git a/src/apps/onboarding/pages/create-vault-password/index.tsx b/src/apps/onboarding/pages/create-vault-password/index.tsx index 79814129e..efbd80f11 100644 --- a/src/apps/onboarding/pages/create-vault-password/index.tsx +++ b/src/apps/onboarding/pages/create-vault-password/index.tsx @@ -110,7 +110,7 @@ export function CreateVaultPasswordPage({ )} renderFooter={() => ( - + setIsChecked(currentValue => !currentValue)} diff --git a/src/apps/onboarding/pages/onboarding-success/content.tsx b/src/apps/onboarding/pages/onboarding-success/content.tsx index d6343d8b1..8fd99934e 100644 --- a/src/apps/onboarding/pages/onboarding-success/content.tsx +++ b/src/apps/onboarding/pages/onboarding-success/content.tsx @@ -1,14 +1,9 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import styled from 'styled-components'; import { TabPageContainer, TabTextContainer } from '@libs/layout'; import { Link, SvgIcon, Typography } from '@libs/ui/components'; -const TipContainer = styled.div` - margin-top: 12px; -`; - export function OnboardingSuccessPageContent() { const { t } = useTranslation(); @@ -20,7 +15,7 @@ export function OnboardingSuccessPageContent() { height={120} /> - + Congrats! Your Casper Wallet is set up and ready to go @@ -36,7 +31,7 @@ export function OnboardingSuccessPageContent() { - + Tip: If this is your first time using a cryptocurrency web wallet we @@ -51,7 +46,7 @@ export function OnboardingSuccessPageContent() { . - + ); } diff --git a/src/apps/onboarding/pages/recover-from-secret-phrase/content.tsx b/src/apps/onboarding/pages/recover-from-secret-phrase/content.tsx index b5699ebdf..deb006237 100644 --- a/src/apps/onboarding/pages/recover-from-secret-phrase/content.tsx +++ b/src/apps/onboarding/pages/recover-from-secret-phrase/content.tsx @@ -4,8 +4,10 @@ import { Trans, useTranslation } from 'react-i18next'; import { InputsContainer, + SpacingSize, TabPageContainer, - TabTextContainer + TabTextContainer, + VerticalSpaceContainer } from '@libs/layout'; import { FormField, @@ -27,9 +29,14 @@ export function RecoverFromSecretPhrasePageContent({ const { t } = useTranslation(); return ( - - Please enter your secret recovery phrase + + Step 3 + + + Please enter your secret recovery phrase + + diff --git a/src/apps/onboarding/pages/recover-from-secret-phrase/index.tsx b/src/apps/onboarding/pages/recover-from-secret-phrase/index.tsx index 1f3b14917..962152a03 100644 --- a/src/apps/onboarding/pages/recover-from-secret-phrase/index.tsx +++ b/src/apps/onboarding/pages/recover-from-secret-phrase/index.tsx @@ -4,10 +4,6 @@ import { Trans, useTranslation } from 'react-i18next'; import { Stepper } from '@onboarding/components/stepper'; import { RouterPath } from '@onboarding/router'; import { useTypedNavigate } from '@onboarding/router/use-typed-navigate'; -import { closeActiveTab } from '@onboarding/utils/close-active-tab'; - -import { initVault } from '@background/redux/sagas/actions'; -import { dispatchToMainStore } from '@background/redux/utils'; import { validateSecretPhrase } from '@libs/crypto'; import { @@ -41,8 +37,11 @@ export function RecoverFromSecretPhrasePage() { if (!validateSecretPhrase(secretPhrase)) { throw Error('Invalid secret phrase.'); } - dispatchToMainStore(initVault({ secretPhrase })); - closeActiveTab(); + navigate(RouterPath.SelectAccountsToRecover, { + state: { + secretPhrase + } + }); } catch (err) { console.error(err); navigate( @@ -67,13 +66,14 @@ export function RecoverFromSecretPhrasePage() {
( navigate(RouterPath.CreateSecretPhrase)} /> - + )} renderContent={() => ( @@ -85,7 +85,7 @@ export function RecoverFromSecretPhrasePage() { renderFooter={() => ( )} diff --git a/src/apps/onboarding/pages/reset-wallet/content.tsx b/src/apps/onboarding/pages/reset-wallet/content.tsx index 5d0990d76..85c2b323d 100644 --- a/src/apps/onboarding/pages/reset-wallet/content.tsx +++ b/src/apps/onboarding/pages/reset-wallet/content.tsx @@ -11,7 +11,7 @@ export function ResetWalletPageContent() { return ( - + Are you sure you want to start again? diff --git a/src/apps/onboarding/pages/select-accounts-to-recover/content.tsx b/src/apps/onboarding/pages/select-accounts-to-recover/content.tsx new file mode 100644 index 000000000..d80b0b79b --- /dev/null +++ b/src/apps/onboarding/pages/select-accounts-to-recover/content.tsx @@ -0,0 +1,106 @@ +import { Player } from '@lottiefiles/react-lottie-player'; +import React, { SetStateAction } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import styled from 'styled-components'; + +import { useIsDarkMode } from '@hooks/use-is-dark-mode'; + +import spinnerDarkModeAnimation from '@libs/animations/spinner_dark_mode.json'; +import spinnerLightModeAnimation from '@libs/animations/spinner_light_mode.json'; +import { + CenteredFlexColumn, + SpacingSize, + TabPageContainer, + VerticalSpaceContainer +} from '@libs/layout'; +import { AccountListRows } from '@libs/types/account'; +import { + DynamicAccountsListWithSelect, + Tile, + Typography +} from '@libs/ui/components'; + +const AnimationContainer = styled(CenteredFlexColumn)` + padding: 106px 16px; +`; + +interface SelectAccountsToRecoverContentProps { + isLoading: boolean; + derivedAccountsWithBalance: AccountListRows[]; + isLoadingMore: boolean; + onLoadMore: () => void; + maxItemsToRender: number; + setSelectedAccounts: React.Dispatch>; + selectedAccounts: AccountListRows[]; + setIsButtonDisabled: React.Dispatch>; +} + +export const SelectAccountsToRecoverContent = ({ + isLoading, + isLoadingMore, + onLoadMore, + derivedAccountsWithBalance, + maxItemsToRender, + setSelectedAccounts, + selectedAccounts, + setIsButtonDisabled +}: SelectAccountsToRecoverContentProps) => { + const { t } = useTranslation(); + const isDarkMode = useIsDarkMode(); + + return ( + + + Step 4 + + + + Select accounts to recover + + + + {isLoading ? ( + + + + + + + Just a moment + + + Your accounts will be here shortly. + + + + + + ) : ( + + )} + + ); +}; diff --git a/src/apps/onboarding/pages/select-accounts-to-recover/index.tsx b/src/apps/onboarding/pages/select-accounts-to-recover/index.tsx new file mode 100644 index 000000000..ea1250ca7 --- /dev/null +++ b/src/apps/onboarding/pages/select-accounts-to-recover/index.tsx @@ -0,0 +1,182 @@ +import React, { useEffect, useState } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; + +import { isEqualCaseInsensitive } from '@src/utils'; + +import { Stepper } from '@onboarding/components/stepper'; +import { SelectAccountsToRecoverContent } from '@onboarding/pages/select-accounts-to-recover/content'; +import { getKeyPairList } from '@onboarding/pages/select-accounts-to-recover/utils'; +import { + RouterPath, + useTypedLocation, + useTypedNavigate +} from '@onboarding/router'; +import { closeActiveTab } from '@onboarding/utils/close-active-tab'; + +import { recoverVault } from '@background/redux/sagas/actions'; +import { selectApiConfigBasedOnActiveNetwork } from '@background/redux/settings/selectors'; +import { dispatchToMainStore } from '@background/redux/utils'; + +import { getAccountHashFromPublicKey } from '@libs/entities/Account'; +import { + HeaderSubmenuBarNavLink, + LayoutTab, + TabFooterContainer, + TabHeaderContainer +} from '@libs/layout'; +import { dispatchFetchAccountBalances } from '@libs/services/balance-service'; +import { Account, AccountListRows, KeyPair } from '@libs/types/account'; +import { Button } from '@libs/ui/components'; + +export const SelectAccountsToRecoverPage = () => { + const [derivedAccounts, setDerivedAccounts] = useState([]); + const [derivedAccountsWithBalance, setDerivedAccountsWithBalance] = useState< + AccountListRows[] + >([]); + const [selectedAccounts, setSelectedAccounts] = useState( + [] + ); + + const [isLoading, setIsLoading] = useState(true); + const [isLoadingMore, setIsLoadingMore] = useState(false); + const [maxItemsToRender, setMaxItemsToRender] = useState(5); + const [isButtonDisabled, setIsButtonDisabled] = useState(true); + + const navigate = useTypedNavigate(); + const { t } = useTranslation(); + const location = useTypedLocation(); + + const { casperWalletApiUrl } = useSelector( + selectApiConfigBasedOnActiveNetwork + ); + + useEffect(() => { + if (location.state?.secretPhrase) { + const keyPairs = getKeyPairList({ + size: 5, + offset: 0, + secretPhrase: location.state?.secretPhrase + }); + + setDerivedAccounts(prevState => [...prevState, ...keyPairs]); + } + }, [location.state?.secretPhrase, setDerivedAccounts]); + + useEffect(() => { + if (!derivedAccounts.length) return; + + const hashes = derivedAccounts.reduce( + (previousValue, currentValue, currentIndex) => { + const hash = getAccountHashFromPublicKey(currentValue.publicKey); + + return derivedAccounts.length === currentIndex + 1 + ? previousValue + `${hash}` + : previousValue + `${hash},`; + }, + '' + ); + + dispatchFetchAccountBalances(hashes) + .then(({ payload }) => { + if ('data' in payload) { + const derivedAccountsWithBalance: AccountListRows[] = + derivedAccounts.map((account, index) => { + const accountWithBalance = payload.data.find(ac => + isEqualCaseInsensitive(ac.public_key, account.publicKey) + ); + + return { + ...account, + id: account.publicKey, + hidden: false, + derivationIndex: index, + name: '', + balance: { + liquidMotes: `${accountWithBalance?.balance ?? '0'}` + } + }; + }); + + setDerivedAccountsWithBalance(derivedAccountsWithBalance); + } + }) + .finally(() => { + setIsLoading(false); + setIsLoadingMore(false); + }); + }, [derivedAccounts, casperWalletApiUrl]); + + const onLoadMore = () => { + try { + setIsLoadingMore(true); + const keyPairs = getKeyPairList({ + size: 5, + offset: derivedAccounts.length, + secretPhrase: location.state?.secretPhrase + }); + + setDerivedAccounts(prevState => [...prevState, ...keyPairs]); + setMaxItemsToRender(prevState => prevState + 5); + } catch (e) { + setIsLoadingMore(false); + } + }; + + const onSubmit = () => { + if (!location.state?.secretPhrase) return; + + const accounts: Account[] = selectedAccounts.map(account => ({ + name: account.name, + publicKey: account.publicKey, + secretKey: account.secretKey, + hidden: account.hidden, + derivationIndex: account.derivationIndex + })); + + dispatchToMainStore( + recoverVault({ + secretPhrase: location.state.secretPhrase, + accounts + }) + ).finally(closeActiveTab); + }; + + return ( + ( + + navigate(RouterPath.CreateSecretPhrase)} + /> + + + )} + renderContent={() => ( + + )} + renderFooter={() => ( + + + + )} + /> + ); +}; diff --git a/src/apps/onboarding/pages/select-accounts-to-recover/utils.ts b/src/apps/onboarding/pages/select-accounts-to-recover/utils.ts new file mode 100644 index 000000000..24d3b39cf --- /dev/null +++ b/src/apps/onboarding/pages/select-accounts-to-recover/utils.ts @@ -0,0 +1,24 @@ +import { deriveKeyPair } from '@libs/crypto'; +import { KeyPair } from '@libs/types/account'; + +export interface KeyPairOptions { + size: number; + offset: number; + secretPhrase: string[]; +} + +export const getKeyPairList = ({ + size, + offset, + secretPhrase +}: KeyPairOptions) => { + const keyPairs: KeyPair[] = []; + + for (let i = 0; i < size; i++) { + const keyPair = deriveKeyPair(secretPhrase, offset + i); + + keyPairs.push(keyPair); + } + + return keyPairs; +}; diff --git a/src/apps/onboarding/pages/unlock-wallet/content.tsx b/src/apps/onboarding/pages/unlock-wallet/content.tsx index f83f72280..3fdc59f77 100644 --- a/src/apps/onboarding/pages/unlock-wallet/content.tsx +++ b/src/apps/onboarding/pages/unlock-wallet/content.tsx @@ -19,15 +19,14 @@ import { import { UnlockWalletFormValues } from '@libs/ui/forms/unlock-wallet'; // Design of this page is temporary. Should be changed after it will be done in Figma -const TabPageContainer = styled.div` - padding-top: 24px; -`; +const TabPageContainer = styled.div``; const IllustrationContainer = styled(TabPageContainerBase)``; // It's need for good reading const GrayBackgroundContainer = styled(TabPageContainerBase)` background-color: ${({ theme }) => theme.color.backgroundSecondary}; + padding-bottom: 28px; `; interface LoginPageContentProps { diff --git a/src/apps/onboarding/pages/unlock-wallet/index.tsx b/src/apps/onboarding/pages/unlock-wallet/index.tsx index 2e1e6bdce..c33c9b27f 100644 --- a/src/apps/onboarding/pages/unlock-wallet/index.tsx +++ b/src/apps/onboarding/pages/unlock-wallet/index.tsx @@ -59,6 +59,7 @@ export function UnlockWalletPage({ saveIsLoggedIn }: UnlockWalletPageProps) { return ( ( - - + + Ready to create your new wallet? - + diff --git a/src/apps/onboarding/pages/write-down-secret-phrase/content.tsx b/src/apps/onboarding/pages/write-down-secret-phrase/content.tsx index 9099e306d..cfbfa61d0 100644 --- a/src/apps/onboarding/pages/write-down-secret-phrase/content.tsx +++ b/src/apps/onboarding/pages/write-down-secret-phrase/content.tsx @@ -2,7 +2,12 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { SecretPhrase } from '@libs/crypto'; -import { TabPageContainer, TabTextContainer } from '@libs/layout'; +import { + SpacingSize, + TabPageContainer, + TabTextContainer, + VerticalSpaceContainer +} from '@libs/layout'; import { CopySecretPhraseBar, SecretPhraseWordsView, @@ -20,9 +25,14 @@ export function WriteDownSecretPhrasePageContent({ return ( - - Write down your secret recovery phrase + + Step 4 + + + Write down your secret recovery phrase + + diff --git a/src/apps/onboarding/pages/write-down-secret-phrase/index.tsx b/src/apps/onboarding/pages/write-down-secret-phrase/index.tsx index 8ae391daf..ce716f559 100644 --- a/src/apps/onboarding/pages/write-down-secret-phrase/index.tsx +++ b/src/apps/onboarding/pages/write-down-secret-phrase/index.tsx @@ -35,6 +35,7 @@ export function WriteDownSecretPhrasePage({ return ( ( @@ -43,7 +44,7 @@ export function WriteDownSecretPhrasePage({ )} renderContent={() => } renderFooter={() => ( - + setIsChecked(currentValue => !currentValue)} diff --git a/src/apps/onboarding/router/paths.ts b/src/apps/onboarding/router/paths.ts index 217457804..727522a96 100644 --- a/src/apps/onboarding/router/paths.ts +++ b/src/apps/onboarding/router/paths.ts @@ -9,5 +9,6 @@ export enum RouterPath { ConfirmSecretPhrase = '/confirm-secret-phrase', ConfirmSecretPhraseSuccess = '/confirm-secret-phrase-success', OnboardingSuccess = '/onboarding-success', - ResetWallet = '/reset-wallet' + ResetWallet = '/reset-wallet', + SelectAccountsToRecover = '/select-accounts-to-recover' } diff --git a/src/apps/onboarding/router/types.ts b/src/apps/onboarding/router/types.ts index 5518388f2..67a077b5e 100644 --- a/src/apps/onboarding/router/types.ts +++ b/src/apps/onboarding/router/types.ts @@ -1,3 +1,5 @@ import { ErrorLocationState } from '@libs/layout'; -export interface LocationState extends ErrorLocationState {} +export interface LocationState extends ErrorLocationState { + secretPhrase?: string[]; +} diff --git a/src/apps/popup/app-router.tsx b/src/apps/popup/app-router.tsx index 8cd0a67d0..7e67ea351 100644 --- a/src/apps/popup/app-router.tsx +++ b/src/apps/popup/app-router.tsx @@ -22,11 +22,11 @@ import { ImportAccountFromLedgerPage } from '@popup/pages/import-account-from-le import { ImportAccountFromTorusPage } from '@popup/pages/import-account-from-torus'; import { NavigationMenuPageContent } from '@popup/pages/navigation-menu'; import { NftDetailsPage } from '@popup/pages/nft-details'; -import { NoConnectedAccountPageContent } from '@popup/pages/no-connected-account'; +import { NoConnectedAccountPage } from '@popup/pages/no-connected-account'; import { RateAppPage } from '@popup/pages/rate-app'; import { ReceivePage } from '@popup/pages/receive'; -import { RemoveAccountPageContent } from '@popup/pages/remove-account'; -import { RenameAccountPageContent } from '@popup/pages/rename-account'; +import { RemoveAccountPage } from '@popup/pages/remove-account'; +import { RenameAccountPage } from '@popup/pages/rename-account'; import { SignWithLedgerInNewWindowPage } from '@popup/pages/sign-with-ledger-in-new-window'; import { StakesPage } from '@popup/pages/stakes'; import { TimeoutPageContent } from '@popup/pages/timeout'; @@ -134,52 +134,11 @@ function AppRoutes() { /> } /> - ( - ( - - )} - /> - )} - renderContent={() => } - /> - } - /> - ( - ( - - )} - /> - )} - renderContent={() => } - /> - } - /> + } /> + } /> ( - - )} - renderContent={() => } - /> - } + element={} /> ( - + + + + } + placeholder={t('Search')} + {...currencySearchRegister('currencySearch')} + /> + - + )} placement="fullBottom" children={() => ( diff --git a/src/apps/popup/pages/buy-cspr/country.tsx b/src/apps/popup/pages/buy-cspr/country.tsx index f02d848c7..7bf564501 100644 --- a/src/apps/popup/pages/buy-cspr/country.tsx +++ b/src/apps/popup/pages/buy-cspr/country.tsx @@ -7,21 +7,24 @@ import { selectAccountBalance } from '@background/redux/account-info/selectors'; import { ContentContainer, + InputsContainer, ParagraphContainer, SpacingSize } from '@libs/layout'; import { ResponseCountryPropsWithId } from '@libs/services/buy-cspr-service/types'; import { ActiveAccountPlate, + Input, List, Modal, + ModalSwitcher, + SvgIcon, Typography } from '@libs/ui/components'; import { motesToCSPR } from '@libs/ui/utils'; import { CountryRow } from './components/country-row'; import { ListRow } from './components/list-row'; -import { Switcher } from './components/switcher'; import { sortCountries } from './utils'; interface CountryProps { @@ -82,12 +85,14 @@ export const Country = ({ ( - + + + } + placeholder={t('Search')} + {...register('countryNameSearch')} + /> + - + )} placement="fullBottom" children={() => ( diff --git a/src/apps/popup/pages/change-password/index.tsx b/src/apps/popup/pages/change-password/index.tsx index 9b70db059..d36279738 100644 --- a/src/apps/popup/pages/change-password/index.tsx +++ b/src/apps/popup/pages/change-password/index.tsx @@ -1,12 +1,17 @@ -import React from 'react'; +import React, { useCallback, useState } from 'react'; import { useWatch } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; import { ChangePasswordPageContent } from '@popup/pages/change-password/content'; +import { PasswordProtectionPage } from '@popup/pages/password-protection-page'; import { RouterPath, useTypedNavigate } from '@popup/router'; -import { changePassword } from '@background/redux/sagas/actions'; +import { keysUpdated } from '@background/redux/keys/actions'; +import { encryptionKeyHashCreated } from '@background/redux/session/actions'; import { dispatchToMainStore } from '@background/redux/utils'; +import { vaultCipherCreated } from '@background/redux/vault-cipher/actions'; +import { selectVault } from '@background/redux/vault/selectors'; import { FooterButtonsContainer, @@ -21,10 +26,25 @@ import { } from '@libs/ui/forms/create-password'; import { calculateSubmitButtonDisabled } from '@libs/ui/forms/get-submit-button-state-from-validation'; +interface CreatePasswordWorkerMessageEvent extends MessageEvent { + data: { + passwordHash: string; + passwordSaltHash: string; + newEncryptionKeyHash: string; + keyDerivationSaltHash: string; + newVaultCipher: string; + }; +} + export const ChangePasswordPage = () => { + const [isPasswordConfirmed, setIsPasswordConfirmed] = + useState(false); + const { t } = useTranslation(); const navigate = useTypedNavigate(); + const vault = useSelector(selectVault); + const { register, handleSubmit, @@ -37,15 +57,65 @@ export const ChangePasswordPage = () => { name: 'password' }); + const setPasswordConfirmed = useCallback(() => { + setIsPasswordConfirmed(true); + }, []); + const isSubmitButtonDisabled = calculateSubmitButtonDisabled({ isDirty }); const onSubmit = (data: CreatePasswordFormValues) => { - dispatchToMainStore(changePassword({ password: data.password })); + const worker = new Worker( + new URL('@background/workers/create-password-worker.ts', import.meta.url) + ); + + worker.postMessage({ + password: data.password, + vault + }); + + worker.onmessage = (event: CreatePasswordWorkerMessageEvent) => { + const { + passwordHash, + passwordSaltHash, + newEncryptionKeyHash, + keyDerivationSaltHash, + newVaultCipher + } = event.data; + + dispatchToMainStore( + keysUpdated({ + passwordHash, + passwordSaltHash, + keyDerivationSaltHash + }) + ); + + dispatchToMainStore( + encryptionKeyHashCreated({ encryptionKeyHash: newEncryptionKeyHash }) + ); + + dispatchToMainStore( + vaultCipherCreated({ + vaultCipher: newVaultCipher + }) + ); + }; + + worker.onerror = error => { + console.error(error); + }; + navigate(RouterPath.Home); }; + if (!isPasswordConfirmed) { + return ( + + ); + } + return ( { const newName = name.trim(); diff --git a/src/apps/popup/pages/home/components/more-buttons-modal/modal-buttons.tsx b/src/apps/popup/pages/home/components/more-buttons-modal/modal-buttons.tsx index e7e7fed51..dec88451e 100644 --- a/src/apps/popup/pages/home/components/more-buttons-modal/modal-buttons.tsx +++ b/src/apps/popup/pages/home/components/more-buttons-modal/modal-buttons.tsx @@ -49,19 +49,7 @@ export const ModalButtons = () => { )} - navigate( - casperToken?.id - ? RouterPath.Transfer.replace( - ':tokenContractPackageHash', - casperToken.id - ).replace( - ':tokenContractHash', - casperToken.contractHash || 'null' - ) - : RouterPath.TransferNoParams - ) - } + onClick={() => navigate(RouterPath.Transfer)} > - - + ( + + )} + renderContent={() => } + renderFooter={() => ( + + + + )} + /> ); -} +}; diff --git a/src/apps/popup/pages/password-protection-page/index.tsx b/src/apps/popup/pages/password-protection-page/index.tsx index beaa380a1..e6d2769e9 100644 --- a/src/apps/popup/pages/password-protection-page/index.tsx +++ b/src/apps/popup/pages/password-protection-page/index.tsx @@ -1,12 +1,25 @@ -import React from 'react'; +import * as Yup from 'yup'; +import React, { useState } from 'react'; +import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; +import { AnyObject } from 'yup/es/types'; + +import { + ERROR_DISPLAYED_BEFORE_ATTEMPT_IS_DECREMENTED, + LOGIN_RETRY_ATTEMPTS_LIMIT +} from '@src/constants'; +import { getErrorMessageForIncorrectPassword } from '@src/utils'; import { selectPasswordHash, selectPasswordSaltHash } from '@background/redux/keys/selectors'; -import { loginRetryCountReseted } from '@background/redux/login-retry-count/actions'; +import { + loginRetryCountIncremented, + loginRetryCountReseted +} from '@background/redux/login-retry-count/actions'; +import { selectLoginRetryCount } from '@background/redux/login-retry-count/selectors'; import { dispatchToMainStore } from '@background/redux/utils'; import { @@ -17,24 +30,40 @@ import { UnlockProtectedPageContent } from '@libs/layout'; import { Button } from '@libs/ui/components'; -import { calculateSubmitButtonDisabled } from '@libs/ui/forms/get-submit-button-state-from-validation'; -import { useUnlockWalletForm } from '@libs/ui/forms/unlock-wallet'; interface BackupSecretPhrasePasswordPageType { - setPasswordConfirmed: () => void; + setPasswordConfirmed?: () => void; onClick?: (password: string) => Promise; - loading?: boolean; + isLoading?: boolean; +} + +interface VerifyPasswordMessageEvent extends MessageEvent { + data: { + isPasswordCorrect: Yup.StringSchema< + string | undefined, + AnyObject, + string | undefined + >; + }; } export const PasswordProtectionPage = ({ setPasswordConfirmed, onClick, - loading = false + isLoading = false }: BackupSecretPhrasePasswordPageType) => { + const [isSubmitting, setIsSubmitting] = useState(false); + const { t } = useTranslation(); const passwordHash = useSelector(selectPasswordHash); const passwordSaltHash = useSelector(selectPasswordSaltHash); + const loginRetryCount = useSelector(selectLoginRetryCount); + + const attemptsLeft = + LOGIN_RETRY_ATTEMPTS_LIMIT - + loginRetryCount - + ERROR_DISPLAYED_BEFORE_ATTEMPT_IS_DECREMENTED; if (passwordHash == null || passwordSaltHash == null) { throw Error("Password doesn't exist"); @@ -43,26 +72,62 @@ export const PasswordProtectionPage = ({ const { register, handleSubmit, - formState: { errors, isDirty }, - getValues - } = useUnlockWalletForm(passwordHash, passwordSaltHash); - - const isSubmitButtonDisabled = calculateSubmitButtonDisabled({ - isDirty + formState: { errors }, + getValues, + setError + } = useForm({ + defaultValues: { + password: '' + } }); const onSubmit = () => { - if (onClick) { - const { password } = getValues(); - - onClick(password).then(() => { - setPasswordConfirmed(); - dispatchToMainStore(loginRetryCountReseted()); - }); - } else { - setPasswordConfirmed(); - dispatchToMainStore(loginRetryCountReseted()); - } + setIsSubmitting(true); + + const { password } = getValues(); + + const worker = new Worker( + new URL('@background/workers/verify-password-worker.ts', import.meta.url) + ); + + worker.postMessage({ + passwordHash, + passwordSaltHash, + password + }); + + worker.onmessage = (event: VerifyPasswordMessageEvent) => { + const { isPasswordCorrect } = event.data; + + if (!isPasswordCorrect) { + dispatchToMainStore(loginRetryCountIncremented()); + const errorMessage = getErrorMessageForIncorrectPassword(attemptsLeft); + + setError('password', { + message: t(errorMessage) + }); + setIsSubmitting(false); + } else { + if (onClick) { + onClick(password).then(() => { + if (setPasswordConfirmed) { + setPasswordConfirmed(); + } + dispatchToMainStore(loginRetryCountReseted()); + }); + } else { + if (setPasswordConfirmed) { + setPasswordConfirmed(); + } + dispatchToMainStore(loginRetryCountReseted()); + } + } + }; + + worker.onerror = error => { + console.error(error); + setIsSubmitting(false); + }; }; return ( @@ -84,8 +149,8 @@ export const PasswordProtectionPage = ({ )} renderFooter={() => ( - )} diff --git a/src/apps/popup/pages/remove-account/content.tsx b/src/apps/popup/pages/remove-account/content.tsx new file mode 100644 index 000000000..52ee0af2c --- /dev/null +++ b/src/apps/popup/pages/remove-account/content.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import { + ContentContainer, + IllustrationContainer, + ParagraphContainer, + SpacingSize +} from '@libs/layout'; +import { SvgIcon, Typography } from '@libs/ui/components'; + +export function RemoveAccountPageContent() { + const { t } = useTranslation(); + + return ( + + + + + + + Remove account? + + + + + + Are you sure you want to remove this account? The action can’t be + undone. + + + + + ); +} diff --git a/src/apps/popup/pages/remove-account/index.tsx b/src/apps/popup/pages/remove-account/index.tsx index 7bff66478..6bdd9a902 100644 --- a/src/apps/popup/pages/remove-account/index.tsx +++ b/src/apps/popup/pages/remove-account/index.tsx @@ -8,18 +8,19 @@ import { dispatchToMainStore } from '@background/redux/utils'; import { accountRemoved } from '@background/redux/vault/actions'; import { - ContentContainer, - FooterButtonsAbsoluteContainer, - IllustrationContainer, - ParagraphContainer, - SpacingSize + FooterButtonsContainer, + HeaderPopup, + HeaderSubmenuBarNavLink, + PopupLayout } from '@libs/layout'; -import { Button, SvgIcon, Typography } from '@libs/ui/components'; +import { Button } from '@libs/ui/components'; -export function RemoveAccountPageContent() { +import { RemoveAccountPageContent } from './content'; + +export const RemoveAccountPage = () => { + const { t } = useTranslation(); const navigate = useTypedNavigate(); const { accountName } = useParams(); - const { t } = useTranslation(); const handleRemoveAccount = useCallback(() => { if (!accountName) { @@ -37,35 +38,28 @@ export function RemoveAccountPageContent() { } return ( - - - ( + ( + + )} /> - - - - Remove account? - - - - - - Are you sure you want to remove this account? The action can’t be - undone. - - - - - - - - + )} + renderContent={() => } + renderFooter={() => ( + + + + + )} + /> ); -} +}; diff --git a/src/apps/popup/pages/rename-account/content.tsx b/src/apps/popup/pages/rename-account/content.tsx new file mode 100644 index 000000000..8dc119fcb --- /dev/null +++ b/src/apps/popup/pages/rename-account/content.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { FieldErrors, UseFormRegister } from 'react-hook-form'; +import { Trans, useTranslation } from 'react-i18next'; + +import { + ContentContainer, + IllustrationContainer, + InputsContainer, + ParagraphContainer, + SpacingSize +} from '@libs/layout'; +import { Input, SvgIcon, Typography } from '@libs/ui/components'; +import { RenameAccountFormValues } from '@libs/ui/forms/rename-account'; + +interface RenameAccountPageContentProps { + register: UseFormRegister; + errors: FieldErrors; +} + +export function RenameAccountPageContent({ + register, + errors +}: RenameAccountPageContentProps) { + const { t } = useTranslation(); + + return ( + + + + + + + Rename account + + + + + + + ); +} diff --git a/src/apps/popup/pages/rename-account/index.tsx b/src/apps/popup/pages/rename-account/index.tsx index d21d73351..00cbd8b2f 100644 --- a/src/apps/popup/pages/rename-account/index.tsx +++ b/src/apps/popup/pages/rename-account/index.tsx @@ -10,20 +10,20 @@ import { accountRenamed } from '@background/redux/vault/actions'; import { selectVaultAccountsNames } from '@background/redux/vault/selectors'; import { - ContentContainer, - FooterButtonsAbsoluteContainer, - IllustrationContainer, - InputsContainer, - ParagraphContainer, - SpacingSize + FooterButtonsContainer, + HeaderPopup, + HeaderSubmenuBarNavLink, + PopupLayout } from '@libs/layout'; -import { Button, Input, SvgIcon, Typography } from '@libs/ui/components'; +import { Button } from '@libs/ui/components'; import { RenameAccountFormValues, useRenameAccount } from '@libs/ui/forms/rename-account'; -export function RenameAccountPageContent() { +import { RenameAccountPageContent } from './content'; + +export const RenameAccountPage = () => { const navigate = useTypedNavigate(); const { accountName } = useParams(); const { t } = useTranslation(); @@ -48,30 +48,24 @@ export function RenameAccountPageContent() { } return ( - - - ( + ( + + )} /> - - - - Rename account - - - - - - - + )} + renderContent={() => ( + + )} + renderFooter={() => ( + @@ -82,8 +76,8 @@ export function RenameAccountPageContent() { > Cancel - - - + + )} + /> ); -} +}; diff --git a/src/apps/popup/pages/stakes/amount-step.tsx b/src/apps/popup/pages/stakes/amount-step.tsx index afe66ba9f..217884dc0 100644 --- a/src/apps/popup/pages/stakes/amount-step.tsx +++ b/src/apps/popup/pages/stakes/amount-step.tsx @@ -36,7 +36,7 @@ const StakeMaxButton = styled(AlignedFlexRow)<{ disabled?: boolean }>` interface AmountStepProps { amountForm: UseFormReturn; - stakesType: AuctionManagerEntryPoint; + stakeType: AuctionManagerEntryPoint; stakeAmountMotes: string; amountStepText: string; amountStepMaxAmountValue: string | null; @@ -44,7 +44,7 @@ interface AmountStepProps { export const AmountStep = ({ amountForm, - stakesType, + stakeType, stakeAmountMotes, amountStepText, amountStepMaxAmountValue @@ -58,7 +58,7 @@ export const AmountStep = ({ const csprBalance = useSelector(selectAccountBalance); useEffect(() => { - switch (stakesType) { + switch (stakeType) { case AuctionManagerEntryPoint.delegate: { const maxAmountMotes: string = csprBalance.liquidMotes == null @@ -86,7 +86,7 @@ export const AmountStep = ({ setMaxAmountMotes(stakeAmountMotes); } } - }, [csprBalance.liquidMotes, stakeAmountMotes, stakesType]); + }, [csprBalance.liquidMotes, stakeAmountMotes, stakeType]); const { register, @@ -112,7 +112,7 @@ export const AmountStep = ({ { const { t } = useTranslation(); const currencyRate = useSelector(selectAccountCurrencyRate); - const transferFeeMotes = getAuctionManagerDeployCost(stakesType); + const transferFeeMotes = getAuctionManagerDeployCost(stakeType); const transferCostInCSPR = formatNumber(motesToCSPR(transferFeeMotes), { precision: { max: 5 } @@ -102,7 +98,7 @@ export const ConfirmStep = ({ } delegatorsNumber={validator?.delegators_number} validatorLabel={ - stakesType === AuctionManagerEntryPoint.redelegate + stakeType === AuctionManagerEntryPoint.redelegate ? t('From validator') : t('To validator') } diff --git a/src/apps/popup/pages/stakes/content.tsx b/src/apps/popup/pages/stakes/content.tsx index 5492eb9ef..e69de29bb 100644 --- a/src/apps/popup/pages/stakes/content.tsx +++ b/src/apps/popup/pages/stakes/content.tsx @@ -1,164 +0,0 @@ -import React from 'react'; -import { UseFormReturn } from 'react-hook-form'; -import { Trans, useTranslation } from 'react-i18next'; - -import { AuctionManagerEntryPoint, StakeSteps } from '@src/constants'; - -import { AmountStep } from '@popup/pages/stakes/amount-step'; -import { ConfirmStep } from '@popup/pages/stakes/confirm-step'; -import { RedelegateValidatorDropdownInput } from '@popup/pages/stakes/redelegate-validator-dropdown-input'; -import { Step } from '@popup/pages/stakes/step'; -import { useStakeActionTexts } from '@popup/pages/stakes/utils'; -import { ValidatorDropdownInput } from '@popup/pages/stakes/validator-dropdown-input'; - -import { - AlignedFlexRow, - ParagraphContainer, - SpacingSize, - VerticalSpaceContainer -} from '@libs/layout'; -import { ILedgerEvent } from '@libs/services/ledger'; -import { ValidatorResultWithId } from '@libs/services/validators-service/types'; -import { - LedgerEventView, - TransferSuccessScreen, - Typography -} from '@libs/ui/components'; -import { - StakeAmountFormValues, - StakeNewValidatorFormValues, - StakeValidatorFormValues -} from '@libs/ui/forms/stakes-form'; - -interface DelegateStakePageContentProps { - stakeStep: StakeSteps; - validatorForm: UseFormReturn; - amountForm: UseFormReturn; - newValidatorForm: UseFormReturn; - inputAmountCSPR: string; - validator: ValidatorResultWithId | null; - setValidator: React.Dispatch< - React.SetStateAction - >; - newValidator: ValidatorResultWithId | null; - setNewValidator: React.Dispatch< - React.SetStateAction - >; - stakesType: AuctionManagerEntryPoint; - stakeAmountMotes: string; - setStakeAmount: React.Dispatch>; - validatorList: ValidatorResultWithId[] | null; - undelegateValidatorList: ValidatorResultWithId[] | null; - loading: boolean; - LedgerEventStatus: ILedgerEvent; -} - -export const StakesPageContent = ({ - stakeStep, - validatorForm, - amountForm, - newValidatorForm, - inputAmountCSPR, - validator, - setValidator, - newValidator, - setNewValidator, - stakesType, - stakeAmountMotes, - setStakeAmount, - validatorList, - undelegateValidatorList, - loading, - LedgerEventStatus -}: DelegateStakePageContentProps) => { - const { t } = useTranslation(); - - const { - validatorStepHeaderText, - newValidatorStepHeaderText, - amountStepHeaderText, - confirmStepHeaderText, - successStepHeaderText, - confirmStepText, - amountStepText, - amountStepMaxAmountValue - } = useStakeActionTexts(stakesType, stakeAmountMotes); - - const getContent = { - [StakeSteps.Validator]: ( - - - - ), - [StakeSteps.Amount]: ( - - - - ), - [StakeSteps.NewValidator]: ( - - - - - Amount: - - {`${inputAmountCSPR} CSPR`} - - - - - ), - [StakeSteps.Confirm]: ( - - - - ), - [StakeSteps.ConfirmWithLedger]: ( - - ), - [StakeSteps.Success]: ( - - {stakesType === AuctionManagerEntryPoint.redelegate ? ( - - - - I usually takes around{' '} - 14 to 16 hours for - this operation to complete. - - - - ) : null} - - ) - }; - - return getContent[stakeStep]; -}; diff --git a/src/apps/popup/pages/stakes/index.tsx b/src/apps/popup/pages/stakes/index.tsx index 698153f33..42c97f58b 100644 --- a/src/apps/popup/pages/stakes/index.tsx +++ b/src/apps/popup/pages/stakes/index.tsx @@ -1,7 +1,8 @@ import { DeployUtil } from 'casper-js-sdk'; -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; +import styled from 'styled-components'; import { AuctionManagerEntryPoint, @@ -10,10 +11,18 @@ import { } from '@src/constants'; import { fetchAndDispatchExtendedDeployInfo } from '@src/utils'; -import { StakesPageContent } from '@popup/pages/stakes/content'; +import { AmountStep } from '@popup/pages/stakes/amount-step'; +import { ConfirmStep } from '@popup/pages/stakes/confirm-step'; import { NoDelegations } from '@popup/pages/stakes/no-delegations'; -import { useConfirmationButtonText } from '@popup/pages/stakes/utils'; -import { RouterPath, useTypedLocation, useTypedNavigate } from '@popup/router'; +import { RedelegateValidatorDropdownInput } from '@popup/pages/stakes/redelegate-validator-dropdown-input'; +import { Step } from '@popup/pages/stakes/step'; +import { + useConfirmationButtonText, + useStakeActionTexts, + useStakeType +} from '@popup/pages/stakes/utils'; +import { ValidatorDropdownInput } from '@popup/pages/stakes/validator-dropdown-input'; +import { RouterPath, useTypedNavigate } from '@popup/router'; import { selectAccountBalance } from '@background/redux/account-info/selectors'; import { ledgerDeployChanged } from '@background/redux/ledger/actions'; @@ -29,17 +38,21 @@ import { } from '@background/redux/vault/selectors'; import { useLedger } from '@hooks/use-ledger'; +import { useSubmitButton } from '@hooks/use-submit-button'; import { createAsymmetricKey } from '@libs/crypto/create-asymmetric-key'; import { AlignedFlexRow, + CenteredFlexRow, ErrorPath, FooterButtonsContainer, HeaderPopup, HeaderSubmenuBarNavLink, + ParagraphContainer, PopupLayout, SpaceBetweenFlexRow, SpacingSize, + VerticalSpaceContainer, createErrorLocationState } from '@libs/layout'; import { @@ -47,15 +60,13 @@ import { sendSignDeploy, signDeploy } from '@libs/services/deployer-service'; -import { - dispatchFetchAuctionValidatorsRequest, - dispatchFetchValidatorsDetailsDataRequest -} from '@libs/services/validators-service'; import { ValidatorResultWithId } from '@libs/services/validators-service/types'; import { Button, HomePageTabsId, + LedgerEventView, SvgIcon, + TransferSuccessScreen, Typography, renderLedgerFooter } from '@libs/ui/components'; @@ -63,134 +74,69 @@ import { calculateSubmitButtonDisabled } from '@libs/ui/forms/get-submit-button- import { useStakesForm } from '@libs/ui/forms/stakes-form'; import { CSPRtoMotes, formatNumber, motesToCSPR } from '@libs/ui/utils'; +const ScrollContainer = styled(VerticalSpaceContainer)<{ + isHidden: boolean; +}>` + opacity: ${({ isHidden }) => (isHidden ? '0' : '1')}; + height: ${({ isHidden }) => (isHidden ? '0' : '24px')}; + visibility: ${({ isHidden }) => (isHidden ? 'hidden' : 'visible')}; + transition: + opacity 0.2s ease-in-out, + height 0.5s ease-in-out; +`; + +const ConfirmButtonContainer = styled(FooterButtonsContainer)<{ + isHidden: boolean; +}>` + gap: ${({ isHidden }) => (isHidden ? '0' : '16px')}; + transition: gap 0.5s ease-in-out; +`; + export const StakesPage = () => { const [stakeStep, setStakeStep] = useState(StakeSteps.Validator); const [validatorPublicKey, setValidatorPublicKey] = useState(''); const [newValidatorPublicKey, setNewValidatorPublicKey] = useState(''); const [inputAmountCSPR, setInputAmountCSPR] = useState(''); - const [isSubmitButtonDisable, setIsSubmitButtonDisable] = useState(true); const [validator, setValidator] = useState( null ); const [newValidator, setNewValidator] = useState(null); - const [stakesType, setStakesType] = useState( - AuctionManagerEntryPoint.delegate - ); const [stakeAmountMotes, setStakeAmountMotes] = useState(''); - const [validatorList, setValidatorList] = useState< - ValidatorResultWithId[] | null - >(null); - const [undelegateValidatorList, setUndelegateValidatorList] = useState< - ValidatorResultWithId[] | null - >(null); - const [loading, setLoading] = useState(true); const activeAccount = useSelector(selectVaultActiveAccount); const isActiveAccountFromLedger = useSelector( selectIsActiveAccountFromLedger ); - const { - networkName, - nodeUrl, - auctionManagerContractHash, - casperClarityApiUrl - } = useSelector(selectApiConfigBasedOnActiveNetwork); + const { networkName, nodeUrl, auctionManagerContractHash } = useSelector( + selectApiConfigBasedOnActiveNetwork + ); const csprBalance = useSelector(selectAccountBalance); const ratedInStore = useSelector(selectRatedInStore); const askForReviewAfter = useSelector(selectAskForReviewAfter); const { t } = useTranslation(); const navigate = useTypedNavigate(); - const { pathname } = useTypedLocation(); - - useEffect(() => { - // checking pathname to know what type of stake it is - if (pathname.split('/')[1] === AuctionManagerEntryPoint.delegate) { - setStakesType(AuctionManagerEntryPoint.delegate); - - dispatchFetchAuctionValidatorsRequest() - .then(({ payload }) => { - if ('data' in payload) { - const { data } = payload; - - const validatorListWithId = data.map(validator => ({ - ...validator, - id: validator.public_key - })); - - setValidatorList(validatorListWithId); - } - }) - .finally(() => { - setLoading(false); - }); - } else if (pathname.split('/')[1] === AuctionManagerEntryPoint.undelegate) { - setStakesType(AuctionManagerEntryPoint.undelegate); - - if (activeAccount) { - dispatchFetchValidatorsDetailsDataRequest(activeAccount.publicKey) - .then(({ payload }) => { - if ('data' in payload) { - const { data } = payload; - - const validatorListWithId = data.map(delegator => ({ - ...delegator.validator, - id: delegator.validator_public_key, - user_stake: delegator.stake - })); - - setUndelegateValidatorList(validatorListWithId); - } - }) - .finally(() => { - setLoading(false); - }); - } - } else if (pathname.split('/')[1] === AuctionManagerEntryPoint.redelegate) { - setStakesType(AuctionManagerEntryPoint.redelegate); - - if (activeAccount) { - Promise.all([ - dispatchFetchAuctionValidatorsRequest(), - dispatchFetchValidatorsDetailsDataRequest(activeAccount.publicKey) - ]) - .then(([allValidatorsResp, undelegateValidatorResp]) => { - if ('data' in allValidatorsResp.payload) { - const { data } = allValidatorsResp.payload; - - const validatorListWithId = data.map(validator => ({ - ...validator, - id: validator.public_key - })); - - setValidatorList(validatorListWithId); - } - if ('data' in undelegateValidatorResp.payload) { - const { data } = undelegateValidatorResp.payload; - const validatorListWithId = data.map(delegator => ({ - ...delegator.validator, - id: delegator.validator_public_key, - user_stake: delegator.stake - })); + const { stakeType, validatorList, undelegateValidatorList, loading } = + useStakeType(); - setUndelegateValidatorList(validatorListWithId); - } - }) - .finally(() => { - setLoading(false); - }); - } - } - }, [activeAccount, pathname, casperClarityApiUrl]); + const hasDelegationToSelectedValidator = undelegateValidatorList?.some( + accountDelegation => accountDelegation.public_key === validator?.public_key + ); + const hasDelegationToSelectedNewValidator = undelegateValidatorList?.some( + accountDelegation => + accountDelegation.public_key === newValidator?.public_key + ); const { amountForm, validatorForm, newValidatorForm } = useStakesForm( csprBalance.liquidMotes, - stakesType, + stakeType, stakeAmountMotes, validator?.delegators_number, - newValidator?.delegators_number + newValidator?.delegators_number, + hasDelegationToSelectedValidator, + hasDelegationToSelectedNewValidator ); const { formState: amountFormState, getValues: getValuesAmountForm } = amountForm; @@ -201,46 +147,15 @@ export const StakesPage = () => { getValues: getValuesNewValidatorForm } = newValidatorForm; - // event listener for enable/disable submit button - useEffect(() => { - if (stakeStep !== StakeSteps.Confirm) return; - - const layoutContentContainer = document.querySelector('#ms-container'); - - // if the content is not scrollable, we can enable the submit button - if ( - layoutContentContainer && - layoutContentContainer.clientHeight === - layoutContentContainer.scrollHeight && - isSubmitButtonDisable - ) { - setIsSubmitButtonDisable(false); - } - - const handleScroll = () => { - if (layoutContentContainer && isSubmitButtonDisable) { - const bottom = - Math.ceil( - layoutContentContainer.clientHeight + - layoutContentContainer.scrollTop - ) >= layoutContentContainer.scrollHeight; - - if (bottom) { - setIsSubmitButtonDisable(false); - } - } - }; - - // add event listener to the scrollable container - layoutContentContainer?.addEventListener('scroll', handleScroll); - - // remove event listener on cleanup - return () => { - layoutContentContainer?.removeEventListener('scroll', handleScroll); - }; - }, [isSubmitButtonDisable, stakeStep]); + const { + isSubmitButtonDisable, + setIsSubmitButtonDisable, + isAdditionalTextVisible + } = useSubmitButton(stakeStep === StakeSteps.Confirm); const submitStake = async () => { + setIsSubmitButtonDisable(true); + if (activeAccount) { const motesAmount = CSPRtoMotes(inputAmountCSPR); @@ -250,7 +165,7 @@ export const StakesPage = () => { ); const deploy = await makeAuctionManagerDeploy( - stakesType, + stakeType, activeAccount.publicKey, validatorPublicKey, newValidatorPublicKey || null, @@ -313,7 +228,7 @@ export const StakesPage = () => { const motesAmount = CSPRtoMotes(inputAmountCSPR); const deploy = await makeAuctionManagerDeploy( - stakesType, + stakeType, activeAccount.publicKey, validatorPublicKey, newValidatorPublicKey || null, @@ -334,70 +249,262 @@ export const StakesPage = () => { beforeLedgerActionCb }); - const getButtonProps = () => { - const isValidatorFormButtonDisabled = calculateSubmitButtonDisabled({ - isValid: validatorFormState.isValid - }); - const isAmountFormButtonDisabled = calculateSubmitButtonDisabled({ - isValid: amountFormState.isValid - }); - const isNewValidatorFormButtonDisabled = calculateSubmitButtonDisabled({ - isValid: newValidatorFormState.isValid - }); - - switch (stakeStep) { - case StakeSteps.Validator: { - return { - disabled: isValidatorFormButtonDisabled, - onClick: () => { + const isValidatorFormButtonDisabled = calculateSubmitButtonDisabled({ + isValid: validatorFormState.isValid + }); + const isAmountFormButtonDisabled = calculateSubmitButtonDisabled({ + isValid: amountFormState.isValid + }); + const isNewValidatorFormButtonDisabled = calculateSubmitButtonDisabled({ + isValid: newValidatorFormState.isValid + }); + + const { + validatorStepHeaderText, + newValidatorStepHeaderText, + amountStepHeaderText, + confirmStepHeaderText, + successStepHeaderText, + confirmStepText, + amountStepText, + amountStepMaxAmountValue + } = useStakeActionTexts(stakeType, stakeAmountMotes); + + const confirmButtonText = useConfirmationButtonText(stakeType); + + const content = { + [StakeSteps.Validator]: ( + + + + ), + [StakeSteps.Amount]: ( + + + + ), + [StakeSteps.NewValidator]: ( + + + + + Amount: + + {`${inputAmountCSPR} CSPR`} + + + + + ), + [StakeSteps.Confirm]: ( + + + + ), + [StakeSteps.ConfirmWithLedger]: ( + + ), + [StakeSteps.Success]: ( + + {stakeType === AuctionManagerEntryPoint.redelegate ? ( + + + + I usually takes around{' '} + 14 to 16 hours for + this operation to complete. + + + + ) : null} + + ) + }; + + const headerButtons = { + [StakeSteps.Validator]: ( + + ), + [StakeSteps.Amount]: ( + setStakeStep(StakeSteps.Validator)} + /> + ), + [StakeSteps.NewValidator]: ( + setStakeStep(StakeSteps.Amount)} + /> + ), + [StakeSteps.Confirm]: ( + + stakeType === AuctionManagerEntryPoint.redelegate + ? setStakeStep(StakeSteps.NewValidator) + : setStakeStep(StakeSteps.Amount) + } + /> + ), + [StakeSteps.ConfirmWithLedger]: ( + setStakeStep(StakeSteps.Confirm)} + /> + ) + }; + + const ledgerFooterButton = renderLedgerFooter({ + onConnect: makeSubmitLedgerAction, + event: ledgerEventStatusToRender, + onErrorCtaPressed: () => setStakeStep(StakeSteps.Confirm) + }); + + const footerButtons = { + [StakeSteps.Validator]: ( + + + + ), + [StakeSteps.Amount]: ( + + + + Transaction fee + + + {formatNumber(motesToCSPR(STAKE_COST_MOTES), { + precision: { max: 5 } + })}{' '} + CSPR + + + + + ), + [StakeSteps.NewValidator]: ( + + + + ), + [StakeSteps.Confirm]: ( + + + + + Scroll down to check all details + + + + + + ), + [StakeSteps.ConfirmWithLedger]: ledgerFooterButton ? ( + ledgerFooterButton() + ) : ( + <> + ), + [StakeSteps.Success]: ( + + + + ) }; - const confirmButtonText = useConfirmationButtonText(stakesType); - if ( - (stakesType === AuctionManagerEntryPoint.undelegate || - stakesType === AuctionManagerEntryPoint.redelegate) && + (stakeType === AuctionManagerEntryPoint.undelegate || + stakeType === AuctionManagerEntryPoint.redelegate) && (csprBalance.delegatedMotes == null || csprBalance.delegatedMotes === '0') ) { return ( @@ -494,50 +559,6 @@ export const StakesPage = () => { ); } - const renderFooter = () => { - if (stakeStep === StakeSteps.ConfirmWithLedger) { - return renderLedgerFooter({ - onConnect: makeSubmitLedgerAction, - event: ledgerEventStatusToRender, - onErrorCtaPressed: () => setStakeStep(StakeSteps.Confirm) - }); - } - - return () => ( - - {stakeStep === StakeSteps.Amount ? ( - - - Transaction fee - - - {formatNumber(motesToCSPR(STAKE_COST_MOTES), { - precision: { max: 5 } - })}{' '} - CSPR - - - ) : null} - - - ); - }; - return ( ( @@ -545,30 +566,15 @@ export const StakesPage = () => { withNetworkSwitcher withMenu withConnectionStatus - renderSubmenuBarItems={getBackButton[stakeStep]} - /> - )} - renderContent={() => ( - headerButtons[stakeStep] + } /> )} - renderFooter={renderFooter()} + renderContent={() => content[stakeStep]} + renderFooter={() => footerButtons[stakeStep]} /> ); }; diff --git a/src/apps/popup/pages/stakes/redelegate-validator-dropdown-input.tsx b/src/apps/popup/pages/stakes/redelegate-validator-dropdown-input.tsx index 9750d797a..3cba5a4c4 100644 --- a/src/apps/popup/pages/stakes/redelegate-validator-dropdown-input.tsx +++ b/src/apps/popup/pages/stakes/redelegate-validator-dropdown-input.tsx @@ -107,7 +107,7 @@ export const RedelegateValidatorDropdownInput = ({ validator?.account_info?.info?.owner?.branding?.logo?.png_256 || validator?.account_info?.info?.owner?.branding?.logo?.png_1024 } - totalStake={validator.user_stake} + totalStake={validator.total_stake} delegatorsNumber={validator?.delegators_number} validatorLabel={t('To validator')} error={errors?.newValidatorPublicKey} @@ -179,7 +179,7 @@ export const RedelegateValidatorDropdownInput = ({ fee={validator.fee} name={validator?.account_info?.info?.owner?.name} logo={logo} - totalStake={validator.user_stake} + totalStake={validator.total_stake} delegatorsNumber={validator?.delegators_number} handleClick={() => { setValue('newValidatorPublicKey', validator.public_key); diff --git a/src/apps/popup/pages/stakes/utils.ts b/src/apps/popup/pages/stakes/utils.ts index 3fec838e9..c7662d42b 100644 --- a/src/apps/popup/pages/stakes/utils.ts +++ b/src/apps/popup/pages/stakes/utils.ts @@ -1,9 +1,19 @@ import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; import { AuctionManagerEntryPoint } from '@src/constants'; -import { ValidatorResultWithId } from '@libs/services/validators-service'; +import { useTypedLocation } from '@popup/router'; + +import { selectApiConfigBasedOnActiveNetwork } from '@background/redux/settings/selectors'; +import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; + +import { + ValidatorResultWithId, + dispatchFetchAuctionValidatorsRequest, + dispatchFetchValidatorsDetailsDataRequest +} from '@libs/services/validators-service'; import { formatNumber, motesToCSPR } from '@libs/ui/utils'; type StakeTexts = { @@ -81,11 +91,11 @@ export const useFilteredValidators = ( }; export const useStakeActionTexts = ( - stakesType: AuctionManagerEntryPoint, + stakeType: AuctionManagerEntryPoint, stakeAmountMotes?: string ) => { const [state, setState] = useState({ - ...stakeActionsTextMap[stakesType], + ...stakeActionsTextMap[stakeType], amountStepMaxAmountValue: null }); @@ -95,19 +105,19 @@ export const useStakeActionTexts = ( formatNumber(motesToCSPR(stakeAmountMotes), { precision: { max: 4 } }); setState({ - ...stakeActionsTextMap[stakesType], + ...stakeActionsTextMap[stakeType], amountStepMaxAmountValue: - stakesType !== AuctionManagerEntryPoint.delegate + stakeType !== AuctionManagerEntryPoint.delegate ? `${formattedAmountCSPR} CSPR` : null }); - }, [stakeAmountMotes, stakesType]); + }, [stakeAmountMotes, stakeType]); return state; }; export const useConfirmationButtonText = ( - stakesType: AuctionManagerEntryPoint + stakeType: AuctionManagerEntryPoint ) => { const { t } = useTranslation(); @@ -118,5 +128,98 @@ export const useConfirmationButtonText = ( default: t('Confirm') }; - return buttonTexts[stakesType] || buttonTexts.default; + return buttonTexts[stakeType] || buttonTexts.default; +}; + +export const useStakeType = () => { + const [stakeType, setStakeType] = useState( + AuctionManagerEntryPoint.delegate + ); + const [validatorList, setValidatorList] = useState< + ValidatorResultWithId[] | null + >(null); + const [undelegateValidatorList, setUndelegateValidatorList] = useState< + ValidatorResultWithId[] | null + >(null); + const [loading, setLoading] = useState(true); + + const { pathname } = useTypedLocation(); + + const activeAccount = useSelector(selectVaultActiveAccount); + const { casperClarityApiUrl } = useSelector( + selectApiConfigBasedOnActiveNetwork + ); + + useEffect(() => { + const name = pathname.split('/')[1]; + // checking pathname to know what type of stake it is + if ( + name === AuctionManagerEntryPoint.delegate || + name === AuctionManagerEntryPoint.redelegate + ) { + setStakeType(name); + + if (activeAccount) { + Promise.all([ + dispatchFetchAuctionValidatorsRequest(), + dispatchFetchValidatorsDetailsDataRequest(activeAccount.publicKey) + ]) + .then(([allValidatorsResp, undelegateValidatorResp]) => { + if ('data' in allValidatorsResp.payload) { + const { data } = allValidatorsResp.payload; + + const validatorListWithId = data.map(validator => ({ + ...validator, + id: validator.public_key + })); + + setValidatorList(validatorListWithId); + } + if ('data' in undelegateValidatorResp.payload) { + const { data } = undelegateValidatorResp.payload; + + const validatorListWithId = data.map(delegator => ({ + ...delegator.validator, + id: delegator.validator_public_key, + user_stake: delegator.stake + })); + + setUndelegateValidatorList(validatorListWithId); + } + }) + .finally(() => { + setLoading(false); + }); + } + } else if (name === AuctionManagerEntryPoint.undelegate) { + setStakeType(name); + + if (activeAccount) { + dispatchFetchValidatorsDetailsDataRequest(activeAccount.publicKey) + .then(({ payload }) => { + if ('data' in payload) { + const { data } = payload; + + const validatorListWithId = data.map(delegator => ({ + ...delegator.validator, + id: delegator.validator_public_key, + user_stake: delegator.stake + })); + + setUndelegateValidatorList(validatorListWithId); + } + }) + .finally(() => { + setLoading(false); + }); + } + } + }, [activeAccount, pathname, casperClarityApiUrl]); + + return { + stakeType, + validatorList, + undelegateValidatorList, + loading + }; }; diff --git a/src/apps/popup/pages/stakes/validator-dropdown-input.tsx b/src/apps/popup/pages/stakes/validator-dropdown-input.tsx index d2ab05703..8b28643b0 100644 --- a/src/apps/popup/pages/stakes/validator-dropdown-input.tsx +++ b/src/apps/popup/pages/stakes/validator-dropdown-input.tsx @@ -33,8 +33,7 @@ interface ValidatorDropdownInputProps { React.SetStateAction >; setStakeAmount: React.Dispatch>; - stakesType: AuctionManagerEntryPoint; - undelegateValidatorList: ValidatorResultWithId[] | null; + stakeType: AuctionManagerEntryPoint; loading: boolean; } @@ -44,8 +43,7 @@ export const ValidatorDropdownInput = ({ validator, setValidator, setStakeAmount, - stakesType, - undelegateValidatorList, + stakeType, loading }: ValidatorDropdownInputProps) => { const [isOpenValidatorPublicKeysList, setIsOpenValidatorPublicKeysList] = @@ -103,11 +101,11 @@ export const ValidatorDropdownInput = ({ const filteredValidatorsList = useFilteredValidators( inputValue, - undelegateValidatorList || validatorList + validatorList ); useEffect(() => { - switch (stakesType) { + switch (stakeType) { case AuctionManagerEntryPoint.delegate: { setLabel('To validator'); break; @@ -118,7 +116,7 @@ export const ValidatorDropdownInput = ({ break; } } - }, [stakesType]); + }, [stakeType]); return showValidatorPlate && validator ? ( @@ -133,7 +131,7 @@ export const ValidatorDropdownInput = ({ } // TODO: remove user_stake after we merge recipient and amount steps for undelegation totalStake={ - stakesType === AuctionManagerEntryPoint.delegate + stakeType === AuctionManagerEntryPoint.delegate ? validator.total_stake : validator.user_stake } @@ -211,7 +209,7 @@ export const ValidatorDropdownInput = ({ logo={logo} // TODO: remove user_stake after we merge recipient and amount steps for undelegation totalStake={ - stakesType === AuctionManagerEntryPoint.delegate + stakeType === AuctionManagerEntryPoint.delegate ? validator.total_stake : validator.user_stake } diff --git a/src/apps/popup/pages/token-details/token.tsx b/src/apps/popup/pages/token-details/token.tsx index 081d80b7c..4d02143c4 100644 --- a/src/apps/popup/pages/token-details/token.tsx +++ b/src/apps/popup/pages/token-details/token.tsx @@ -133,18 +133,7 @@ export const Token = ({ erc20Tokens }: TokenProps) => { - navigate( - tokenData?.id - ? RouterPath.Transfer.replace( - ':tokenContractPackageHash', - tokenData.id - ).replace( - ':tokenContractHash', - tokenData.contractHash || 'null' - ) - : RouterPath.TransferNoParams, - { state: { tokenData } } - ) + navigate(RouterPath.Transfer, { state: { tokenData } }) } > + + ), + [TransferNFTSteps.Recipient]: ( + + + + ), + [TransferNFTSteps.Confirm]: ( + + + + ), + [TransferNFTSteps.ConfirmWithLedger]: ledgerFooterButton ? ( + ledgerFooterButton() + ) : ( + <> + ), + [TransferNFTSteps.Success]: ( - {showSuccessScreen ? ( - <> - - - {!isRecipientPublicKeyInContact && ( - - )} - - ) : ( - + + {!isRecipientPublicKeyInContact && ( + )} - ); + ) }; return ( @@ -357,34 +455,14 @@ export const TransferNftPage = () => { withMenu withConnectionStatus renderSubmenuBarItems={ - showSuccessScreen + transferNFTStep === TransferNFTSteps.Success ? undefined - : showLedgerConfirm - ? () => ( - setShowLedgerConfirm(false)} - /> - ) - : () => + : () => headerButtons[transferNFTStep] } /> )} - renderContent={() => - showSuccessScreen ? ( - - ) : showLedgerConfirm ? ( - - ) : ( - - ) - } - renderFooter={renderFooter()} + renderContent={() => content[transferNFTStep]} + renderFooter={() => footerButtons[transferNFTStep]} /> ); }; diff --git a/src/apps/popup/pages/transfer-nft/recipient-step.tsx b/src/apps/popup/pages/transfer-nft/recipient-step.tsx new file mode 100644 index 000000000..432758855 --- /dev/null +++ b/src/apps/popup/pages/transfer-nft/recipient-step.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { UseFormReturn } from 'react-hook-form'; +import { Trans, useTranslation } from 'react-i18next'; + +import { + ContentContainer, + ParagraphContainer, + SpacingSize +} from '@libs/layout'; +import { Typography } from '@libs/ui/components'; +import { RecipientTabs } from '@libs/ui/components/recipient-tabs/recipient-tabs'; +import { TransferNftRecipientFormValues } from '@libs/ui/forms/transfer-nft'; + +interface RecipientStepProps { + recipientForm: UseFormReturn; + setRecipientName: React.Dispatch>; + recipientName: string; +} + +export const RecipientStep = ({ + recipientForm, + setRecipientName, + recipientName +}: RecipientStepProps) => { + const { t } = useTranslation(); + + return ( + + + + Select recipient + + + + + + ); +}; diff --git a/src/apps/popup/pages/transfer-nft/review-step.tsx b/src/apps/popup/pages/transfer-nft/review-step.tsx new file mode 100644 index 000000000..aa22224c0 --- /dev/null +++ b/src/apps/popup/pages/transfer-nft/review-step.tsx @@ -0,0 +1,96 @@ +import React from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import { Trans, useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; + +import { selectAccountCurrencyRate } from '@background/redux/account-info/selectors'; + +import { + ContentContainer, + ParagraphContainer, + SpacingSize, + VerticalSpaceContainer +} from '@libs/layout'; +import { NFTTokenResult } from '@libs/services/nft-service'; +import { Input, Typography } from '@libs/ui/components'; +import { TransferNftAmountFormValues } from '@libs/ui/forms/transfer-nft'; +import { formatFiatAmount, handleNumericInput } from '@libs/ui/utils'; + +import { NFTPlate } from './components/nft-plate'; +import { NFTData } from './utils'; + +interface ReviewStepProps { + nftToken: NFTTokenResult | undefined; + haveReverseOwnerLookUp: boolean; + amountForm: UseFormReturn; + nftData?: NFTData; +} + +export const ReviewStep = ({ + nftToken, + haveReverseOwnerLookUp, + amountForm, + nftData +}: ReviewStepProps) => { + const { t } = useTranslation(); + + const currencyRate = useSelector(selectAccountCurrencyRate); + + const { + register, + formState: { errors }, + control + } = amountForm; + + const paymentAmount = useWatch({ + control, + name: 'paymentAmount' + }); + + const paymentFiatAmount = formatFiatAmount( + paymentAmount || '0', + currencyRate + ); + + return ( + + + + Review details + + + + + + + + {haveReverseOwnerLookUp && ( + + + + Sorry, but we don’t support the reverse look-up modality + + + + )} + + + + + + ); +}; diff --git a/src/apps/popup/pages/transfer-nft/utils.ts b/src/apps/popup/pages/transfer-nft/utils.ts index 7f62a4845..cc094c015 100644 --- a/src/apps/popup/pages/transfer-nft/utils.ts +++ b/src/apps/popup/pages/transfer-nft/utils.ts @@ -85,3 +85,16 @@ export const getRuntimeArgs = ( throw new Error('Unknown token standard.'); } }; + +export enum TransferNFTSteps { + Review = 'review', + Recipient = 'recipient', + Confirm = 'confirm', + ConfirmWithLedger = 'confirm with ledger', + Success = 'success' +} + +export interface NFTData { + contentType: string; + url?: string; +} diff --git a/src/apps/popup/pages/transfer/amount-step.tsx b/src/apps/popup/pages/transfer/amount-step.tsx index 9ed16698d..71c4333b1 100644 --- a/src/apps/popup/pages/transfer/amount-step.tsx +++ b/src/apps/popup/pages/transfer/amount-step.tsx @@ -1,6 +1,6 @@ import Big from 'big.js'; import React, { useEffect, useState } from 'react'; -import { UseFormReturn, useWatch } from 'react-hook-form'; +import { useWatch } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; @@ -10,6 +10,10 @@ import { selectAccountBalance, selectAccountCurrencyRate } from '@background/redux/account-info/selectors'; +import { selectApiConfigBasedOnActiveNetwork } from '@background/redux/settings/selectors'; +import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; + +import { TokenType } from '@hooks/use-casper-token'; import { ContentContainer, @@ -17,22 +21,36 @@ import { SpacingSize, VerticalSpaceContainer } from '@libs/layout'; -import { Checkbox, Error, Input, Typography } from '@libs/ui/components'; -import { TransferAmountFormValues } from '@libs/ui/forms/transfer'; +import { Error, Input, Typography } from '@libs/ui/components'; +import { TransactionFeePlate } from '@libs/ui/components/transaction-fee-plate/transaction-fee-plate'; +import { calculateSubmitButtonDisabled } from '@libs/ui/forms/get-submit-button-state-from-validation'; +import { useTransferAmountForm } from '@libs/ui/forms/transfer'; import { + divideErc20Balance, formatFiatAmount, handleNumericInput, motesToCSPR } from '@libs/ui/utils'; interface AmountStepProps { - amountForm: UseFormReturn; - symbol: string | null; - isCSPR: boolean; + isErc20Transfer: boolean; + paymentAmount: string; + selectedToken: TokenType | null | undefined; + setAmount: (value: React.SetStateAction) => void; + setPaymentAmount: (value: React.SetStateAction) => void; + setTransferIdMemo: (value: React.SetStateAction) => void; + setIsAmountFormButtonDisabled: React.Dispatch>; } -export const AmountStep = ({ amountForm, symbol, isCSPR }: AmountStepProps) => { - const [isChecked, setIsChecked] = useState(false); +export const AmountStep = ({ + isErc20Transfer, + paymentAmount, + selectedToken, + setAmount, + setPaymentAmount, + setTransferIdMemo, + setIsAmountFormButtonDisabled +}: AmountStepProps) => { const [disabled, setDisabled] = useState(false); const [maxAmountMotes, setMaxAmountMotes] = useState('0'); @@ -40,6 +58,24 @@ export const AmountStep = ({ amountForm, symbol, isCSPR }: AmountStepProps) => { const currencyRate = useSelector(selectAccountCurrencyRate); const csprBalance = useSelector(selectAccountBalance); + const activeAccount = useSelector(selectVaultActiveAccount); + const { networkName } = useSelector(selectApiConfigBasedOnActiveNetwork); + + const erc20Balance = + (selectedToken?.balance && + divideErc20Balance( + selectedToken.balance, + selectedToken?.decimals || null + )) || + null; + + const amountForm = useTransferAmountForm( + erc20Balance, + isErc20Transfer, + csprBalance.liquidMotes, + paymentAmount, + selectedToken?.decimals + ); useEffect(() => { const maxAmountMotes: string = @@ -48,32 +84,45 @@ export const AmountStep = ({ amountForm, symbol, isCSPR }: AmountStepProps) => { : Big(csprBalance.liquidMotes).sub(TRANSFER_COST_MOTES).toFixed(); const hasEnoughBalance = Big(maxAmountMotes).gte(TRANSFER_MIN_AMOUNT_MOTES); - const isMaxAmountEqualMinAmount = Big(maxAmountMotes).eq( - TRANSFER_MIN_AMOUNT_MOTES - ); - setIsChecked(isMaxAmountEqualMinAmount); setMaxAmountMotes(maxAmountMotes); setDisabled(!hasEnoughBalance); }, [csprBalance.liquidMotes]); - const { - register, - formState: { errors }, - control, - setValue, - trigger - } = amountForm; + useEffect(() => { + if (!isErc20Transfer) { + setAmount(motesToCSPR(TRANSFER_MIN_AMOUNT_MOTES)); + } + }, [isErc20Transfer, setAmount]); + + const { register, formState, control, trigger, setValue } = amountForm; + + const { errors } = formState; + + useEffect(() => { + if (formState.touchedFields.amount) { + trigger(); + } + }, [ + networkName, + activeAccount?.publicKey, + trigger, + formState.touchedFields.amount, + selectedToken?.amount, + csprBalance.liquidMotes, + paymentAmount + ]); const { onChange: onChangeTransferIdMemo } = register('transferIdMemo'); const { onChange: onChangeCSPRAmount } = register('amount'); + const { onChange: onChangePaymentAmount } = register('paymentAmount'); const amount = useWatch({ control, name: 'amount' }); - const paymentAmount = useWatch({ + const paymentAmountFieldValue = useWatch({ control, name: 'paymentAmount' }); @@ -81,13 +130,20 @@ export const AmountStep = ({ amountForm, symbol, isCSPR }: AmountStepProps) => { const amountLabel = t('Amount'); const transferIdLabel = t('Transfer ID (memo)'); const paymentAmoutLabel = t('Set custom transaction fee'); - const fiatAmount = isCSPR + const fiatAmount = !isErc20Transfer ? formatFiatAmount(amount || '0', currencyRate) : undefined; - const paymentFiatAmount = !isCSPR - ? formatFiatAmount(paymentAmount || '0', currencyRate) + const paymentFiatAmount = isErc20Transfer + ? formatFiatAmount(paymentAmountFieldValue || '0', currencyRate) : undefined; + useEffect(() => { + const isAmountFormButtonDisabled = calculateSubmitButtonDisabled({ + isValid: formState.isValid + }); + setIsAmountFormButtonDisabled(!!isAmountFormButtonDisabled); + }, [formState.isValid, setIsAmountFormButtonDisabled]); + return ( @@ -96,7 +152,7 @@ export const AmountStep = ({ amountForm, symbol, isCSPR }: AmountStepProps) => { - {isCSPR && disabled && ( + {!isErc20Transfer && disabled && ( { monotype type="number" placeholder={t('0.00')} - suffixText={symbol} + suffixText={selectedToken?.symbol} {...register('amount')} - disabled={isCSPR && disabled} + disabled={!isErc20Transfer && disabled} onChange={e => { onChangeCSPRAmount(e); - - if (isChecked) { - setIsChecked(false); - } + setAmount(e.target.value); }} onKeyDown={handleNumericInput} error={!!errors?.amount} @@ -129,29 +182,27 @@ export const AmountStep = ({ amountForm, symbol, isCSPR }: AmountStepProps) => { /> - {isCSPR && ( - - { - if (isChecked) { - setValue('amount', motesToCSPR(TRANSFER_MIN_AMOUNT_MOTES)); - } else { - setValue('amount', motesToCSPR(maxAmountMotes)); - } + {!isErc20Transfer && ( + + { + setAmount(motesToCSPR(maxAmountMotes)); + setValue('amount', motesToCSPR(maxAmountMotes)); + trigger('amount'); - setIsChecked(!isChecked); }} - /> - + > + Send max + + )} {/** transferIdMemo is only relevant for CSPR */} - {isCSPR ? ( + {!isErc20Transfer ? ( { // replace all non-numeric characters e.target.value = e.target.value.replace(/[^0-9]/g, ''); onChangeTransferIdMemo(e); + setTransferIdMemo(e.target.value); }} error={!!errors?.transferIdMemo} validationText={errors?.transferIdMemo?.message} @@ -176,6 +228,10 @@ export const AmountStep = ({ amountForm, symbol, isCSPR }: AmountStepProps) => { suffixText={'CSPR'} {...register('paymentAmount')} error={!!errors?.paymentAmount} + onChange={e => { + onChangePaymentAmount(e); + setPaymentAmount(e.target.value); + }} onKeyDown={handleNumericInput} validationText={ errors?.paymentAmount?.message || @@ -184,6 +240,13 @@ export const AmountStep = ({ amountForm, symbol, isCSPR }: AmountStepProps) => { /> )} + {!isErc20Transfer && ( + + + + )} ); }; diff --git a/src/apps/popup/pages/transfer/components/token-row.tsx b/src/apps/popup/pages/transfer/components/token-row.tsx new file mode 100644 index 000000000..22f6a2fdd --- /dev/null +++ b/src/apps/popup/pages/transfer/components/token-row.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import styled from 'styled-components'; + +import { TokenType } from '@hooks/use-casper-token'; + +import { + AlignedFlexRow, + AlignedSpaceBetweenFlexRow, + SpacingSize +} from '@libs/layout'; +import { Checkbox, SvgIcon, Typography } from '@libs/ui/components'; + +const Container = styled(AlignedSpaceBetweenFlexRow)` + padding: 16px; + + cursor: pointer; +`; + +const LogoImg = styled.img` + width: 24px; + height: 24px; + + border-radius: ${({ theme }) => theme.borderRadius.hundred}px; +`; + +interface TokenRowProps { + handleSelect: (e: React.MouseEvent) => void; + isSelected: boolean; + token: TokenType; +} + +export const TokenRow = ({ + handleSelect, + isSelected, + token +}: TokenRowProps) => { + const tokenIconFormat = token?.icon?.split('.').pop(); + const isTokenIconJPG = tokenIconFormat === 'jpg'; + + return ( + + + {isTokenIconJPG ? ( + + ) : ( + + )} + {token.name} + + + + ); +}; diff --git a/src/apps/popup/pages/transfer/components/token-swticher-row.tsx b/src/apps/popup/pages/transfer/components/token-swticher-row.tsx new file mode 100644 index 000000000..607496f2d --- /dev/null +++ b/src/apps/popup/pages/transfer/components/token-swticher-row.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import styled from 'styled-components'; + +import { + AlignedFlexRow, + FlexColumn, + ParagraphContainer, + SpaceBetweenFlexRow, + SpacingSize +} from '@libs/layout'; +import { SvgIcon, Tile, Typography } from '@libs/ui/components'; + +const Container = styled(SpaceBetweenFlexRow)` + padding: 16px; +`; + +const RowContainer = styled(FlexColumn)` + width: 100%; +`; + +interface TokenSwitcherRowProps { + tokenName: string | undefined; + iconUrl: string | null | undefined; +} + +export const TokenSwitcherRow = ({ + iconUrl, + tokenName +}: TokenSwitcherRowProps) => { + const { t } = useTranslation(); + + return ( + + + + Token + + + + + + + + + {tokenName} + + + + Change + + + + + ); +}; diff --git a/src/apps/popup/pages/transfer/confirm-step.tsx b/src/apps/popup/pages/transfer/confirm-step.tsx index 1a6e2fbd5..41e915ada 100644 --- a/src/apps/popup/pages/transfer/confirm-step.tsx +++ b/src/apps/popup/pages/transfer/confirm-step.tsx @@ -9,6 +9,7 @@ import { TRANSFER_COST_MOTES } from '@src/constants'; import { selectAccountCurrencyRate } from '@background/redux/account-info/selectors'; import { + AmountContainer, ContentContainer, ParagraphContainer, SpaceBetweenFlexRow, @@ -17,14 +18,13 @@ import { } from '@libs/layout'; import { ActiveAccountPlate, - AmountContainer, List, RecipientPlate, Typography } from '@libs/ui/components'; import { formatFiatAmount, formatNumber, motesToCSPR } from '@libs/ui/utils'; -export const ListItemContainer = styled(SpaceBetweenFlexRow)` +const ListItemContainer = styled(SpaceBetweenFlexRow)` padding: 12px 16px; `; @@ -33,7 +33,7 @@ interface ConfirmStepProps { amount: string; balance: string | null; symbol: string | null; - isCSPR: boolean; + isErc20Transfer: boolean; paymentAmount: string; recipientName?: string; } @@ -42,7 +42,7 @@ export const ConfirmStep = ({ amount, balance, symbol, - isCSPR, + isErc20Transfer, paymentAmount, recipientName }: ConfirmStepProps) => { @@ -51,7 +51,7 @@ export const ConfirmStep = ({ const currencyRate = useSelector(selectAccountCurrencyRate); let transactionDataRows; - if (isCSPR) { + if (!isErc20Transfer) { const transferCostInCSPR = formatNumber(motesToCSPR(TRANSFER_COST_MOTES), { precision: { max: 5 } }); diff --git a/src/apps/popup/pages/transfer/content.tsx b/src/apps/popup/pages/transfer/content.tsx index 5b3d1d7bf..e69de29bb 100644 --- a/src/apps/popup/pages/transfer/content.tsx +++ b/src/apps/popup/pages/transfer/content.tsx @@ -1,76 +0,0 @@ -import React, { useState } from 'react'; -import { UseFormReturn } from 'react-hook-form'; - -import { ILedgerEvent } from '@libs/services/ledger'; -import { LedgerEventView, TransferSuccessScreen } from '@libs/ui/components'; -import { - TransferAmountFormValues, - TransferRecipientFormValues -} from '@libs/ui/forms/transfer'; - -import { AmountStep } from './amount-step'; -import { ConfirmStep } from './confirm-step'; -import { RecipientStep } from './recipient-step'; -import { TransactionSteps } from './utils'; - -interface TransferPageContentProps { - transferStep: TransactionSteps; - recipientPublicKey: string; - amountForm: UseFormReturn; - recipientForm: UseFormReturn; - amount: string; - balance: string | null; - symbol: string | null; - paymentAmount: string; - LedgerEventStatus: ILedgerEvent; -} - -export const TransferPageContent = ({ - transferStep, - recipientPublicKey, - recipientForm, - amountForm, - amount, - balance, - symbol, - paymentAmount, - LedgerEventStatus -}: TransferPageContentProps) => { - const [recipientName, setRecipientName] = useState(''); - - const isCSPR = symbol === 'CSPR'; - - const getContent = { - [TransactionSteps.Recipient]: ( - - ), - [TransactionSteps.Amount]: ( - - ), - [TransactionSteps.Confirm]: ( - - ), - [TransactionSteps.ConfirmWithLedger]: ( - - ), - [TransactionSteps.Success]: ( - - ) - }; - - return getContent[transferStep]; -}; diff --git a/src/apps/popup/pages/transfer/index.tsx b/src/apps/popup/pages/transfer/index.tsx index 0f83091e0..24955ad32 100644 --- a/src/apps/popup/pages/transfer/index.tsx +++ b/src/apps/popup/pages/transfer/index.tsx @@ -2,19 +2,13 @@ import { DeployUtil } from 'casper-js-sdk'; import React, { useEffect, useMemo, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; -import { useParams } from 'react-router-dom'; +import styled from 'styled-components'; -import { - ERC20_PAYMENT_AMOUNT_AVERAGE_MOTES, - TRANSFER_COST_MOTES -} from '@src/constants'; -import { useActiveAccountErc20Tokens } from '@src/hooks/use-active-account-erc20-tokens'; +import { ERC20_PAYMENT_AMOUNT_AVERAGE_MOTES } from '@src/constants'; import { fetchAndDispatchExtendedDeployInfo } from '@src/utils'; -import { TransferPageContent } from '@popup/pages/transfer/content'; import { RouterPath, useTypedLocation, useTypedNavigate } from '@popup/router'; -import { selectAccountBalance } from '@background/redux/account-info/selectors'; import { selectAllPublicKeys } from '@background/redux/contacts/selectors'; import { ledgerDeployChanged, @@ -32,18 +26,21 @@ import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; +import { TokenType, useCasperToken } from '@hooks/use-casper-token'; import { useLedger } from '@hooks/use-ledger'; +import { useSubmitButton } from '@hooks/use-submit-button'; import { createAsymmetricKey } from '@libs/crypto/create-asymmetric-key'; import { AlignedFlexRow, + CenteredFlexRow, ErrorPath, FooterButtonsContainer, HeaderPopup, HeaderSubmenuBarNavLink, PopupLayout, - SpaceBetweenFlexRow, SpacingSize, + VerticalSpaceContainer, createErrorLocationState } from '@libs/layout'; import { @@ -56,30 +53,46 @@ import { HardwareWalletType } from '@libs/types/account'; import { Button, HomePageTabsId, + LedgerEventView, SvgIcon, + TransferSuccessScreen, Typography, renderLedgerFooter } from '@libs/ui/components'; -import { calculateSubmitButtonDisabled } from '@libs/ui/forms/get-submit-button-state-from-validation'; -import { useTransferForm } from '@libs/ui/forms/transfer'; -import { - CSPRtoMotes, - divideErc20Balance, - formatNumber, - motesToCSPR -} from '@libs/ui/utils'; - -import { TransactionSteps, getIsErc20Transfer } from './utils'; +import { CSPRtoMotes, motesToCSPR } from '@libs/ui/utils'; + +import { AmountStep } from './amount-step'; +import { ConfirmStep } from './confirm-step'; +import { RecipientStep } from './recipient-step'; +import { TokenStep } from './token-step'; +import { TransactionSteps } from './utils'; + +const ScrollContainer = styled(VerticalSpaceContainer)<{ + isHidden: boolean; +}>` + opacity: ${({ isHidden }) => (isHidden ? '0' : '1')}; + height: ${({ isHidden }) => (isHidden ? '0' : '24px')}; + visibility: ${({ isHidden }) => (isHidden ? 'hidden' : 'visible')}; + transition: + opacity 0.2s ease-in-out, + height 0.5s ease-in-out; +`; + +const ConfirmButtonContainer = styled(FooterButtonsContainer)<{ + isHidden: boolean; +}>` + gap: ${({ isHidden }) => (isHidden ? '0' : '16px')}; + transition: gap 0.5s ease-in-out; +`; export const TransferPage = () => { const { t } = useTranslation(); const navigate = useTypedNavigate(); const location = useTypedLocation(); - const { tokenContractPackageHash, tokenContractHash } = useParams(); - - const isErc20Transfer = getIsErc20Transfer(tokenContractPackageHash); - + const [isErc20Transfer, setIsErc20Transfer] = useState(false); + const [selectedToken, setSelectedToken] = useState(); + const [recipientName, setRecipientName] = useState(''); const [recipientPublicKey, setRecipientPublicKey] = useState(''); const [amount, setAmount] = useState(''); const [paymentAmount, setPaymentAmount] = useState( @@ -87,9 +100,12 @@ export const TransferPage = () => { ); const [transferIdMemo, setTransferIdMemo] = useState(''); const [transferStep, setTransferStep] = useState( - TransactionSteps.Recipient + TransactionSteps.Token ); - const [isSubmitButtonDisable, setIsSubmitButtonDisable] = useState(true); + const [isRecipientFormButtonDisabled, setIsRecipientFormButtonDisabled] = + useState(true); + const [isAmountFormButtonDisabled, setIsAmountFormButtonDisabled] = + useState(false); const activeAccount = useSelector(selectVaultActiveAccount); const isActiveAccountFromLedger = useSelector( @@ -98,104 +114,50 @@ export const TransferPage = () => { const { networkName, nodeUrl } = useSelector( selectApiConfigBasedOnActiveNetwork ); - const csprBalance = useSelector(selectAccountBalance); const contactPublicKeys = useSelector(selectAllPublicKeys); const ratedInStore = useSelector(selectRatedInStore); const askForReviewAfter = useSelector(selectAskForReviewAfter); - const { tokens } = useActiveAccountErc20Tokens(); - - const token = tokens?.find(token => token.id === tokenContractPackageHash); - const symbol = isErc20Transfer - ? token?.symbol || location.state?.tokenData?.symbol || null - : 'CSPR'; - const erc20Decimals = - token?.decimals || location.state?.tokenData?.decimals || null; - const erc20Balance = - (token?.balance && divideErc20Balance(token?.balance, erc20Decimals)) || - null; - const balance = isErc20Transfer - ? erc20Balance - : csprBalance.liquidMotes && motesToCSPR(csprBalance.liquidMotes); - const formattedBalance = formatNumber(balance || '', { - precision: { max: 5 } - }); - const isRecipientPublicKeyInContact = useMemo( - () => contactPublicKeys.includes(recipientPublicKey), - [contactPublicKeys, recipientPublicKey] - ); + const casperToken = useCasperToken(); - const { amountForm, recipientForm } = useTransferForm( - erc20Balance, - isErc20Transfer, - csprBalance.liquidMotes, - paymentAmount - ); + useEffect(() => { + const tokenData = location?.state?.tokenData; - const { - formState: amountFormState, - getValues: getValuesAmountForm, - trigger - } = amountForm; - const { formState: recipientFormState, getValues: getValuesRecipientForm } = - recipientForm; + if (selectedToken) { + return; + } - useEffect(() => { - if (amountFormState.touchedFields.amount) { - trigger(); + if (tokenData) { + if (tokenData.name === 'Casper') { + setIsErc20Transfer(false); + } else { + setIsErc20Transfer(true); + } + setSelectedToken(tokenData); + } else { + setIsErc20Transfer(false); + setSelectedToken(casperToken); } - }, [ - networkName, - activeAccount?.publicKey, - trigger, - amountFormState.touchedFields.amount, - erc20Balance, - csprBalance.liquidMotes, - paymentAmount - ]); - - // event listener for enable/disable submit button + }, [casperToken, location?.state?.tokenData, selectedToken]); + useEffect(() => { - const layoutContentContainer = document.querySelector('#ms-container'); - - // if the content is not scrollable, we can enable the submit button - if ( - layoutContentContainer && - layoutContentContainer.clientHeight === - layoutContentContainer.scrollHeight && - transferStep === TransactionSteps.Confirm && - isSubmitButtonDisable - ) { - setIsSubmitButtonDisable(false); + if (isErc20Transfer) { + setIsAmountFormButtonDisabled(false); + } else { + setIsAmountFormButtonDisabled(true); } + }, [isErc20Transfer, setIsAmountFormButtonDisabled]); - const handleScroll = () => { - if ( - layoutContentContainer && - transferStep === TransactionSteps.Confirm && - isSubmitButtonDisable - ) { - const bottom = - Math.ceil( - layoutContentContainer.clientHeight + - layoutContentContainer.scrollTop - ) >= layoutContentContainer.scrollHeight; - - if (bottom) { - // we are at the bottom of the page - setIsSubmitButtonDisable(false); - } - } - }; - - // add event listener to the scrollable container - layoutContentContainer?.addEventListener('scroll', handleScroll); + const isRecipientPublicKeyInContact = useMemo( + () => contactPublicKeys.includes(recipientPublicKey), + [contactPublicKeys, recipientPublicKey] + ); - // remove event listener on cleanup - return () => { - layoutContentContainer?.removeEventListener('scroll', handleScroll); - }; - }, [isSubmitButtonDisable, transferStep]); + const { + isSubmitButtonDisable, + setIsSubmitButtonDisable, + isAdditionalTextVisible + } = useSubmitButton(transferStep === TransactionSteps.Confirm); const sendDeploy = (signDeploy: DeployUtil.Deploy) => { sendSignDeploy(signDeploy, nodeUrl) @@ -243,6 +205,8 @@ export const TransferPage = () => { }; const onSubmitSending = async () => { + setIsSubmitButtonDisable(true); + if (activeAccount) { const KEYS = createAsymmetricKey( activeAccount.publicKey, @@ -254,11 +218,11 @@ export const TransferPage = () => { const deploy = await makeCep18TransferDeploy( nodeUrl, networkName, - tokenContractHash, - tokenContractPackageHash, + selectedToken?.contractHash, + selectedToken?.id, recipientPublicKey, amount, - erc20Decimals, + selectedToken?.decimals || null, paymentAmount, activeAccount ); @@ -294,11 +258,11 @@ export const TransferPage = () => { const deploy = await makeCep18TransferDeploy( nodeUrl, networkName, - tokenContractHash, - tokenContractPackageHash, + selectedToken?.contractHash, + selectedToken?.id, recipientPublicKey, amount, - erc20Decimals, + selectedToken?.decimals || null, paymentAmount, activeAccount ); @@ -336,57 +300,180 @@ export const TransferPage = () => { beforeLedgerActionCb }); - const getButtonProps = () => { - const isRecipientFormButtonDisabled = calculateSubmitButtonDisabled({ - isValid: recipientFormState.isValid - }); - const isAmountFormButtonDisabled = calculateSubmitButtonDisabled({ - isValid: amountFormState.isValid - }); - - switch (transferStep) { - case TransactionSteps.Recipient: { - return { - disabled: isRecipientFormButtonDisabled, - onClick: () => { - const { recipientPublicKey } = getValuesRecipientForm(); + const content = { + [TransactionSteps.Token]: ( + + ), + [TransactionSteps.Recipient]: ( + + ), + [TransactionSteps.Amount]: ( + + ), + [TransactionSteps.ConfirmWithLedger]: ( + + ), + [TransactionSteps.Confirm]: ( + + ), + [TransactionSteps.Success]: ( + + ) + }; + + const headerButtons = { + [TransactionSteps.Token]: ( + + ), + [TransactionSteps.Recipient]: ( + setTransferStep(TransactionSteps.Token)} + /> + ), + [TransactionSteps.Amount]: ( + setTransferStep(TransactionSteps.Recipient)} + /> + ), + [TransactionSteps.Confirm]: ( + setTransferStep(TransactionSteps.Amount)} + /> + ), + [TransactionSteps.ConfirmWithLedger]: ( + setTransferStep(TransactionSteps.Confirm)} + /> + ) + }; + + const ledgerFooterButton = renderLedgerFooter({ + onConnect: makeSubmitLedgerAction, + event: ledgerEventStatusToRender, + onErrorCtaPressed: () => { + setTransferStep(TransactionSteps.Confirm); + } + }); + const footerButtons = { + [TransactionSteps.Token]: ( + + + + ), + [TransactionSteps.Recipient]: ( + + + + ), + [TransactionSteps.Amount]: ( + + + + ), + [TransactionSteps.ConfirmWithLedger]: ledgerFooterButton ? ( + ledgerFooterButton() + ) : ( + <> + ), + [TransactionSteps.Confirm]: ( + + + + + Scroll down to check all details + + + + + + ), + [TransactionSteps.Success]: ( + + - {transferStep === TransactionSteps.Success && - !isRecipientPublicKeyInContact && ( - + {!isRecipientPublicKeyInContact && ( + - )} - - ); - }; + }) + } + > + Add recipient to list of contacts + + )} + + ) }; return ( @@ -513,23 +522,15 @@ export const TransferPage = () => { withNetworkSwitcher withMenu withConnectionStatus - renderSubmenuBarItems={getBackButton[transferStep]} - /> - )} - renderContent={() => ( - headerButtons[transferStep] + } /> )} - renderFooter={renderFooter()} + renderContent={() => content[transferStep]} + renderFooter={() => footerButtons[transferStep]} /> ); }; diff --git a/src/apps/popup/pages/transfer/recipient-step.tsx b/src/apps/popup/pages/transfer/recipient-step.tsx index 12ec13f9e..d0a50e584 100644 --- a/src/apps/popup/pages/transfer/recipient-step.tsx +++ b/src/apps/popup/pages/transfer/recipient-step.tsx @@ -1,5 +1,4 @@ -import React from 'react'; -import { UseFormReturn } from 'react-hook-form'; +import React, { useEffect } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { @@ -7,30 +6,40 @@ import { ParagraphContainer, SpacingSize } from '@libs/layout'; -import { - ActiveAccountPlate, - RecipientDropdownInput, - Typography -} from '@libs/ui/components'; -import { TransferRecipientFormValues } from '@libs/ui/forms/transfer'; +import { Typography } from '@libs/ui/components'; +import { RecipientTabs } from '@libs/ui/components/recipient-tabs/recipient-tabs'; +import { calculateSubmitButtonDisabled } from '@libs/ui/forms/get-submit-button-state-from-validation'; +import { useTransferRecipientForm } from '@libs/ui/forms/transfer'; interface RecipientStepProps { - recipientForm: UseFormReturn; - balance: string | null; - symbol: string | null; setRecipientName: React.Dispatch>; recipientName: string; + setRecipientPublicKey: (value: React.SetStateAction) => void; + setIsRecipientFormButtonDisabled: React.Dispatch< + React.SetStateAction + >; } export const RecipientStep = ({ - recipientForm, - balance, - symbol, setRecipientName, - recipientName + recipientName, + setIsRecipientFormButtonDisabled, + setRecipientPublicKey }: RecipientStepProps) => { const { t } = useTranslation(); + const recipientForm = useTransferRecipientForm(); + + const { formState } = recipientForm; + + useEffect(() => { + const isRecipientFormButtonDisabled = calculateSubmitButtonDisabled({ + isValid: formState.isValid + }); + + setIsRecipientFormButtonDisabled(!!isRecipientFormButtonDisabled); + }, [formState.isValid, setIsRecipientFormButtonDisabled]); + return ( @@ -38,12 +47,12 @@ export const RecipientStep = ({ Specify recipient - - ); diff --git a/src/apps/popup/pages/transfer/token-step.tsx b/src/apps/popup/pages/transfer/token-step.tsx new file mode 100644 index 000000000..7e3e80ad9 --- /dev/null +++ b/src/apps/popup/pages/transfer/token-step.tsx @@ -0,0 +1,110 @@ +import React, { useEffect, useState } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import { useActiveAccountErc20Tokens } from '@hooks/use-active-account-erc20-tokens'; +import { TokenType, useCasperToken } from '@hooks/use-casper-token'; + +import { + ContentContainer, + ParagraphContainer, + SpacingSize +} from '@libs/layout'; +import { + ActiveAccountPlate, + List, + Modal, + ModalSwitcher, + Typography +} from '@libs/ui/components'; + +import { TokenRow } from './components/token-row'; +import { TokenSwitcherRow } from './components/token-swticher-row'; + +interface TokenStepProps { + setSelectedToken: React.Dispatch< + React.SetStateAction + >; + setIsErc20Transfer: (value: React.SetStateAction) => void; + selectedToken: TokenType | null | undefined; +} + +export const TokenStep = ({ + setSelectedToken, + setIsErc20Transfer, + selectedToken +}: TokenStepProps) => { + const [tokenList, setTokenList] = useState([]); + const { t } = useTranslation(); + + const casperToken = useCasperToken(); + const { tokens } = useActiveAccountErc20Tokens(); + + useEffect(() => { + const tokensList: TokenType[] = []; + + if (casperToken) { + tokensList.push(casperToken); + } + + if (tokens) { + tokensList.push(...tokens); + } + + setTokenList(tokensList); + }, [casperToken, tokens]); + + return ( + + + + Select token and account + + + + ( + + { + const isSelected = token.name === selectedToken?.name; + + return ( + { + setSelectedToken(token); + if (token.name === 'Casper') { + setIsErc20Transfer(false); + } else { + setIsErc20Transfer(true); + } + closeModal(e); + }} + /> + ); + }} + marginLeftForItemSeparatorLine={54} + /> + + )} + placement="fullBottom" + children={() => ( + + )} + /> + + + + ); +}; diff --git a/src/apps/popup/pages/transfer/utils.ts b/src/apps/popup/pages/transfer/utils.ts index 4a6653c12..1d107332e 100644 --- a/src/apps/popup/pages/transfer/utils.ts +++ b/src/apps/popup/pages/transfer/utils.ts @@ -1,15 +1,8 @@ export enum TransactionSteps { + Token = 'token', Recipient = 'recipient', Amount = 'amount', Confirm = 'confirm', ConfirmWithLedger = 'confirm with ledger', Success = 'success' } - -export const getIsErc20Transfer = ( - tokenContractPackageHash: string | undefined -) => { - return ( - tokenContractPackageHash != null && tokenContractPackageHash !== 'Casper' - ); -}; diff --git a/src/apps/popup/pages/wallet-qr-code/index.tsx b/src/apps/popup/pages/wallet-qr-code/index.tsx index 50be011e8..05b585230 100644 --- a/src/apps/popup/pages/wallet-qr-code/index.tsx +++ b/src/apps/popup/pages/wallet-qr-code/index.tsx @@ -10,18 +10,23 @@ import { selectVaultImportedAccounts } from '@background/redux/vault/selectors'; -import { generateSyncWalletQrData } from '@libs/crypto'; import { HeaderPopup, HeaderSubmenuBarNavLink, PopupLayout } from '@libs/layout'; +interface GenerateWalletQrDataMessageEvent extends MessageEvent { + data: { + result: string[]; + }; +} + export const WalletQrCodePage = () => { const [qrStrings, setQrStrings] = useState([]); const [isPasswordConfirmed, setIsPasswordConfirmed] = useState(false); - const [loading, setLoading] = useState(false); + const [isLoading, setIsLoading] = useState(false); const secretPhrase = useSelector(selectSecretPhrase); const derivedAccounts = useSelector(selectVaultDerivedAccounts, shallowEqual); @@ -36,26 +41,39 @@ export const WalletQrCodePage = () => { const generateQRCode = async (password: string) => { if (secretPhrase) { - setLoading(true); - const data = await generateSyncWalletQrData( + setIsLoading(true); + + const worker = new Worker( + new URL( + '@background/workers/generate-sync-wallet-qr-data-worker.ts', + import.meta.url + ) + ); + + worker.postMessage({ password, secretPhrase, derivedAccounts, importedAccounts - ); + }); + + worker.onmessage = (event: GenerateWalletQrDataMessageEvent) => { + const { result } = event.data; + + setQrStrings(result); + setPasswordConfirmed(); + }; - setLoading(false); - setQrStrings(data); + worker.onerror = error => { + console.error(error); + setIsLoading(false); + }; } }; if (!isPasswordConfirmed) { return ( - + ); } diff --git a/src/apps/popup/router/paths.ts b/src/apps/popup/router/paths.ts index 9558b75ad..4778a5822 100644 --- a/src/apps/popup/router/paths.ts +++ b/src/apps/popup/router/paths.ts @@ -13,8 +13,7 @@ export enum RouterPath { ConnectedSites = '/connected-sites', BackupSecretPhrase = '/backup-secret-phrase', DownloadAccountKeys = '/download-account-keys', - Transfer = '/transfer/:tokenContractPackageHash/:tokenContractHash', - TransferNoParams = '/transfer', + Transfer = '/transfer', ActivityDetails = '/activity-details', Token = '/token/:tokenName', Receive = '/receive', diff --git a/src/apps/popup/router/types.ts b/src/apps/popup/router/types.ts index 795020a8e..1d624bf6a 100644 --- a/src/apps/popup/router/types.ts +++ b/src/apps/popup/router/types.ts @@ -2,7 +2,7 @@ import { ActivityType } from '@src/constants'; import { TokenType } from '@hooks/use-casper-token'; -import { ErrorLocationState } from '@libs/layout'; +import { ErrorLocationState } from '@libs/layout/error/types'; export interface LocationState extends ErrorLocationState { showNavigationMenu?: boolean; diff --git a/src/assets/icons/paste.svg b/src/assets/icons/paste.svg new file mode 100644 index 000000000..d5f0d8c07 --- /dev/null +++ b/src/assets/icons/paste.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/background/index.ts b/src/background/index.ts index 3c2fd6513..ec02045d3 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -79,12 +79,13 @@ import { accountImported, accountRemoved, accountRenamed, + accountsAdded, accountsImported, activeAccountChanged, anotherAccountConnected, deployPayloadReceived, deploysReseted, - hideAccountFromListChange, + hideAccountFromListChanged, secretPhraseCreated, siteConnected, siteDisconnected, @@ -158,11 +159,11 @@ import { recipientPublicKeyReseted } from './redux/recent-recipient-public-keys/actions'; import { - changePassword, createAccount, initKeys, initVault, lockVault, + recoverVault, resetVault, unlockVault } from './redux/sagas/actions'; @@ -573,6 +574,7 @@ runtime.onMessage.addListener( case getType(unlockVault): case getType(initKeys): case getType(initVault): + case getType(recoverVault): case getType(createAccount): case getType(deploysReseted): case getType(sessionReseted): @@ -584,10 +586,11 @@ runtime.onMessage.addListener( case getType(accountImported): case getType(accountsImported): case getType(accountAdded): + case getType(accountsAdded): case getType(accountRemoved): case getType(accountRenamed): case getType(activeAccountChanged): - case getType(hideAccountFromListChange): + case getType(hideAccountFromListChanged): case getType(activeTimeoutDurationSettingChanged): case getType(activeNetworkSettingChanged): case getType(vaultSettingsReseted): @@ -632,7 +635,6 @@ runtime.onMessage.addListener( case getType(accountCasperActivityCountChanged): case getType(accountTrackingIdOfSentNftTokensChanged): case getType(accountTrackingIdOfSentNftTokensRemoved): - case getType(changePassword): case getType(newContactAdded): case getType(contactRemoved): case getType(contactEditingPermissionChanged): diff --git a/src/background/open-onboarding-flow.ts b/src/background/open-onboarding-flow.ts index 19e38ab60..3ee4ccd1f 100644 --- a/src/background/open-onboarding-flow.ts +++ b/src/background/open-onboarding-flow.ts @@ -51,7 +51,6 @@ export async function openOnboardingUi() { // create a tab if it does not exist or if it's not an onboarding URL if (!tabExist || !isOnboardingUrl) { - console.log(123); tabs .create({ url: 'onboarding.html', active: true }) .then(tab => { diff --git a/src/background/redux/contacts/types.ts b/src/background/redux/contacts/types.ts index b2ff728af..b753540bf 100644 --- a/src/background/redux/contacts/types.ts +++ b/src/background/redux/contacts/types.ts @@ -12,3 +12,7 @@ export type ContactsState = { export interface EditContactActionType extends Contact { oldName: string; } + +export interface ContactWithId extends Contact { + id: string; +} diff --git a/src/background/redux/sagas/actions.ts b/src/background/redux/sagas/actions.ts index 08134a113..663e14f2b 100644 --- a/src/background/redux/sagas/actions.ts +++ b/src/background/redux/sagas/actions.ts @@ -6,6 +6,7 @@ import { createAction } from 'typesafe-actions'; import { UnlockVault } from '@background/redux/sagas/types'; import { SecretPhrase } from '@libs/crypto'; +import { Account } from '@libs/types/account'; export const startBackground = createAction('START_BACKGROUND_SAGA')(); @@ -23,10 +24,11 @@ export const initVault = createAction('INIT_VAULT_SAGA')<{ secretPhrase: SecretPhrase; }>(); -export const createAccount = createAction('CREATE_ACCOUNT_SAGA')<{ - name?: string; +export const recoverVault = createAction('RECOVER_VAULT_SAGA')<{ + secretPhrase: SecretPhrase; + accounts: Account[]; }>(); -export const changePassword = createAction('CHANGE_PASSWORD_SAGA')<{ - password: string; +export const createAccount = createAction('CREATE_ACCOUNT_SAGA')<{ + name?: string; }>(); diff --git a/src/background/redux/sagas/onboarding-sagas.ts b/src/background/redux/sagas/onboarding-sagas.ts index 558276936..efd071518 100644 --- a/src/background/redux/sagas/onboarding-sagas.ts +++ b/src/background/redux/sagas/onboarding-sagas.ts @@ -26,16 +26,18 @@ import { sagaCall } from '../utils'; import { vaultCipherReseted } from '../vault-cipher/actions'; import { accountAdded, + accountsAdded, deploysReseted, secretPhraseCreated, vaultReseted } from '../vault/actions'; -import { initKeys, initVault, resetVault } from './actions'; +import { initKeys, initVault, recoverVault, resetVault } from './actions'; export function* onboardingSagas() { yield takeLatest(getType(resetVault), resetVaultSaga); yield takeLatest(getType(initKeys), initKeysSage); yield takeLatest(getType(initVault), initVaultSaga); + yield takeLatest(getType(recoverVault), recoverVaultSaga); } /** @@ -117,3 +119,20 @@ function* initVaultSaga(action: ReturnType) { console.error(err); } } + +function* recoverVaultSaga(action: ReturnType) { + try { + const { secretPhrase, accounts } = action.payload; + if (!validateSecretPhrase(secretPhrase)) { + throw Error('Invalid secret phrase.'); + } + + yield put(secretPhraseCreated(secretPhrase)); + yield put(accountsAdded(accounts)); + yield put(vaultUnlocked()); + // cleanup and disabling action handler + disableOnboardingFlow(); + } catch (err) { + console.error(err); + } +} diff --git a/src/background/redux/sagas/vault-sagas.ts b/src/background/redux/sagas/vault-sagas.ts index 1df56ac45..15d8dcee4 100644 --- a/src/background/redux/sagas/vault-sagas.ts +++ b/src/background/redux/sagas/vault-sagas.ts @@ -19,13 +19,8 @@ import { emitSdkEventToActiveTabs } from '@background/utils'; import { sdkEvent } from '@content/sdk-event'; import { deriveKeyPair } from '@libs/crypto'; -import { - deriveEncryptionKey, - encodePassword, - generateRandomSaltHex -} from '@libs/crypto/hashing'; -import { convertBytesToHex } from '@libs/crypto/utils'; import { encryptVault } from '@libs/crypto/vault'; +import { Account } from '@libs/types/account'; import { accountInfoReset } from '../account-info/actions'; import { keysUpdated } from '../keys/actions'; @@ -52,11 +47,13 @@ import { accountImported, accountRemoved, accountRenamed, + accountsAdded, accountsImported, activeAccountChanged, anotherAccountConnected, deployPayloadReceived, deploysReseted, + hideAccountFromListChanged, siteConnected, siteDisconnected, vaultLoaded, @@ -71,7 +68,6 @@ import { } from '../vault/selectors'; import { popupWindowInit } from '../windowManagement/actions'; import { - changePassword, createAccount, lockVault, startBackground, @@ -96,6 +92,7 @@ export function* vaultSagas() { yield takeLatest( [ getType(accountAdded), + getType(accountsAdded), getType(accountImported), getType(accountsImported), getType(accountRemoved), @@ -106,12 +103,12 @@ export function* vaultSagas() { getType(siteDisconnected), getType(activeAccountChanged), getType(activeTimeoutDurationSettingChanged), - getType(deployPayloadReceived) + getType(deployPayloadReceived), + getType(hideAccountFromListChanged) ], updateVaultCipher ); yield takeLatest(getType(createAccount), createAccountSaga); - yield takeLatest(getType(changePassword), changePasswordSaga); } /** @@ -316,59 +313,31 @@ function* createAccountSaga(action: ReturnType) { throw Error('Account name exist'); } - const accountCount = derivedAccounts.length; - const secretPhrase = yield* sagaSelect(selectSecretPhrase); - - const keyPair = deriveKeyPair(secretPhrase, accountCount); - const account = { - ...keyPair, - name, - hidden: false - }; - - yield put(accountAdded(account)); - } catch (err) { - console.error(err); - } -} - -function* changePasswordSaga(action: ReturnType) { - try { - const { password } = action.payload; - - const passwordSaltHash = generateRandomSaltHex(); - const passwordHash = yield* sagaCall(() => - encodePassword(password, passwordSaltHash) - ); - const keyDerivationSaltHash = generateRandomSaltHex(); - const newEncryptionKeyBytes = yield* sagaCall(() => - deriveEncryptionKey(password, keyDerivationSaltHash) - ); - const newEncryptionKeyHash = convertBytesToHex(newEncryptionKeyBytes); - - yield put( - keysUpdated({ - passwordHash, - passwordSaltHash, - keyDerivationSaltHash - }) - ); - yield put( - encryptionKeyHashCreated({ encryptionKeyHash: newEncryptionKeyHash }) - ); - - const vault = yield* sagaSelect(selectVault); + let isAccountAlreadyAdded = true; + let i = 0; - // encrypt cipher with the new key - const newVaultCipher = yield* sagaCall(() => - encryptVault(newEncryptionKeyHash, vault) - ); + const secretPhrase = yield* sagaSelect(selectSecretPhrase); - yield put( - vaultCipherCreated({ - vaultCipher: newVaultCipher - }) - ); + while (isAccountAlreadyAdded) { + const keyPair = deriveKeyPair(secretPhrase, i); + if ( + !derivedAccounts.some( + account => account.publicKey === keyPair.publicKey + ) + ) { + isAccountAlreadyAdded = false; + + const account: Account = { + ...keyPair, + name, + hidden: false, + derivationIndex: i + }; + yield put(accountAdded(account)); + break; + } + i++; + } } catch (err) { console.error(err); } diff --git a/src/background/redux/vault/actions.ts b/src/background/redux/vault/actions.ts index 44c68bb16..2458e02e2 100644 --- a/src/background/redux/vault/actions.ts +++ b/src/background/redux/vault/actions.ts @@ -17,6 +17,8 @@ export const accountImported = createAction('ACCOUNT_IMPORTED')(); export const accountAdded = createAction('ACCOUNT_ADDED')(); +export const accountsAdded = createAction('ACCOUNTS_ADDED')(); + export const accountsImported = createAction('ACCOUNTS_IMPORTED')(); export const accountRemoved = createAction('ACCOUNT_REMOVED')<{ @@ -61,8 +63,8 @@ export const deployPayloadReceived = createAction('DEPLOY_PAYLOAD_RECEIVED')<{ json: string; }>(); -export const hideAccountFromListChange = createAction( - 'HIDE_ACCOUNT_FROM_LIST_CHANGE' +export const hideAccountFromListChanged = createAction( + 'HIDE_ACCOUNT_FROM_LIST_CHANGED' )<{ accountName: string; }>(); diff --git a/src/background/redux/vault/reducer.ts b/src/background/redux/vault/reducer.ts index 5c1d41954..c8bc9d4d1 100644 --- a/src/background/redux/vault/reducer.ts +++ b/src/background/redux/vault/reducer.ts @@ -6,12 +6,13 @@ import { accountImported, accountRemoved, accountRenamed, + accountsAdded, accountsImported, activeAccountChanged, anotherAccountConnected, deployPayloadReceived, deploysReseted, - hideAccountFromListChange, + hideAccountFromListChanged, secretPhraseCreated, siteConnected, siteDisconnected, @@ -88,6 +89,15 @@ export const reducer = createReducer(initialState) state.accounts.length === 0 ? account.name : state.activeAccountName }) ) + .handleAction( + accountsAdded, + (state, { payload: accounts }: ReturnType) => ({ + ...state, + accounts: [...state.accounts, ...accounts], + activeAccountName: + state.accounts.length === 0 ? accounts[0].name : state.activeAccountName + }) + ) .handleAction( accountsImported, (state, { payload: accounts }: ReturnType) => ({ @@ -277,10 +287,12 @@ export const reducer = createReducer(initialState) }) ) .handleAction( - hideAccountFromListChange, + hideAccountFromListChanged, ( state, - { payload: { accountName } }: ReturnType + { + payload: { accountName } + }: ReturnType ) => { const visibleAccounts = state.accounts.filter( account => !account.hidden && account.name !== accountName diff --git a/src/background/workers/create-password-worker.ts b/src/background/workers/create-password-worker.ts new file mode 100644 index 000000000..96f1c9fcf --- /dev/null +++ b/src/background/workers/create-password-worker.ts @@ -0,0 +1,39 @@ +import { VaultState } from '@background/redux/vault/types'; + +import { + deriveEncryptionKey, + encodePassword, + generateRandomSaltHex +} from '@libs/crypto/hashing'; +import { convertBytesToHex } from '@libs/crypto/utils'; +import { encryptVault } from '@libs/crypto/vault'; + +interface CreatePasswordWorkerEvent extends MessageEvent { + data: { + password: string; + vault: VaultState; + }; +} + +onmessage = async function (event: CreatePasswordWorkerEvent) { + const { password, vault } = event.data; + + const passwordSaltHash = generateRandomSaltHex(); + const passwordHash = await encodePassword(password, passwordSaltHash); + const keyDerivationSaltHash = generateRandomSaltHex(); + const newEncryptionKeyBytes = await deriveEncryptionKey( + password, + keyDerivationSaltHash + ); + const newEncryptionKeyHash = convertBytesToHex(newEncryptionKeyBytes); + + const newVaultCipher = await encryptVault(newEncryptionKeyHash, vault); + + postMessage({ + passwordHash, + passwordSaltHash, + newEncryptionKeyHash, + keyDerivationSaltHash, + newVaultCipher + }); +}; diff --git a/src/libs/crypto/sync-wallet-qr.ts b/src/background/workers/generate-sync-wallet-qr-data-worker.ts similarity index 68% rename from src/libs/crypto/sync-wallet-qr.ts rename to src/background/workers/generate-sync-wallet-qr-data-worker.ts index 5c5179b71..14f45ca0d 100644 --- a/src/libs/crypto/sync-wallet-qr.ts +++ b/src/background/workers/generate-sync-wallet-qr-data-worker.ts @@ -3,17 +3,23 @@ import { scryptAsync } from '@noble/hashes/scrypt'; import { randomBytes } from '@noble/hashes/utils'; import { CLPublicKey } from 'casper-js-sdk'; +import { createScryptOptions } from '@libs/crypto/hashing'; +import { convertBytesToBase64 } from '@libs/crypto/utils'; import { Account } from '@libs/types/account'; -import { createScryptOptions } from './hashing'; -import { convertBytesToBase64 } from './utils'; +interface GenerateSyncWalletQrDataEvent extends MessageEvent { + data: { + password: string; + secretPhrase: string[]; + derivedAccounts: Account[]; + importedAccounts: Account[]; + }; +} + +onmessage = async function (event: GenerateSyncWalletQrDataEvent) { + const { password, secretPhrase, derivedAccounts, importedAccounts } = + event.data; -export const generateSyncWalletQrData = async ( - password: string, - secretPhrase: string[], - derivedAccounts: Account[], - importedAccounts: Account[] -) => { const salt = randomBytes(16); const iv = randomBytes(16); @@ -46,7 +52,11 @@ export const generateSyncWalletQrData = async ( const qrData = convertBytesToBase64(qrBytes); const qrDataArray = qrData.match(/.{1,200}/g); - return (qrDataArray ?? [qrData]).map( + const result = (qrDataArray ?? [qrData]).map( (qr, i, arr) => `${i + 1}$${arr.length}$${qr}` ); + + postMessage({ + result + }); }; diff --git a/src/background/workers/unlockVaultWorker.ts b/src/background/workers/unlock-vault-worker.ts similarity index 100% rename from src/background/workers/unlockVaultWorker.ts rename to src/background/workers/unlock-vault-worker.ts diff --git a/src/background/workers/verify-password-worker.ts b/src/background/workers/verify-password-worker.ts new file mode 100644 index 000000000..7b49d8aa6 --- /dev/null +++ b/src/background/workers/verify-password-worker.ts @@ -0,0 +1,22 @@ +import { verifyPasswordAgainstHash } from '@libs/crypto/hashing'; + +interface VerifyPasswordEvent extends MessageEvent { + data: { + passwordHash: string; + passwordSaltHash: string; + password: string; + }; +} + +onmessage = async (event: VerifyPasswordEvent) => { + const { passwordHash, passwordSaltHash, password } = event.data; + const isPasswordCorrect = await verifyPasswordAgainstHash( + passwordHash, + passwordSaltHash, + password + ); + + postMessage({ + isPasswordCorrect + }); +}; diff --git a/src/constants.ts b/src/constants.ts index 8047aae37..3fe79094c 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -15,6 +15,7 @@ export const ERC20_TOKEN_ACTIVITY_REFRESH_RATE = 30 * SECOND; export const VALIDATORS_REFRESH_RATE = 30 * SECOND; export const LOGIN_RETRY_ATTEMPTS_LIMIT = 5; +export const ERROR_DISPLAYED_BEFORE_ATTEMPT_IS_DECREMENTED = 1; export const MOTES_PER_CSPR_RATE = '1000000000'; // 1 000 000 000 MOTES === 1 CSPR export const TRANSFER_COST_MOTES = '100000000'; // 0.1 CSPR diff --git a/src/hooks/use-submit-button.ts b/src/hooks/use-submit-button.ts new file mode 100644 index 000000000..68a994e16 --- /dev/null +++ b/src/hooks/use-submit-button.ts @@ -0,0 +1,51 @@ +import { useEffect, useState } from 'react'; + +export const useSubmitButton = (isConfirmStep: boolean) => { + const [isSubmitButtonDisable, setIsSubmitButtonDisable] = useState(true); + const [isAdditionalTextVisible, setIsAdditionalTextVisible] = useState(true); + + useEffect(() => { + if (!isConfirmStep) return; + + const layoutContentContainer = document.querySelector('#ms-container'); + + // if the content is not scrollable, we can enable the submit button + if ( + layoutContentContainer && + layoutContentContainer.clientHeight === + layoutContentContainer.scrollHeight + ) { + setIsSubmitButtonDisable(false); + setIsAdditionalTextVisible(false); + } + + const handleScroll = () => { + if (layoutContentContainer) { + const bottom = + Math.ceil( + layoutContentContainer.clientHeight + + layoutContentContainer.scrollTop + ) >= layoutContentContainer.scrollHeight; + + if (bottom) { + setIsSubmitButtonDisable(false); + setIsAdditionalTextVisible(false); + } + } + }; + + // add event listener to the scrollable container + layoutContentContainer?.addEventListener('scroll', handleScroll); + + // remove event listener on cleanup + return () => { + layoutContentContainer?.removeEventListener('scroll', handleScroll); + }; + }, [isConfirmStep]); + + return { + isSubmitButtonDisable, + setIsSubmitButtonDisable, + isAdditionalTextVisible + }; +}; diff --git a/src/libs/crypto/index.ts b/src/libs/crypto/index.ts index 95030aebf..b7ec89731 100644 --- a/src/libs/crypto/index.ts +++ b/src/libs/crypto/index.ts @@ -4,4 +4,3 @@ export * from './bip39'; export * from './bip44'; export * from './parse-secret-key-string'; export * from './sign-deploy'; -export * from './sync-wallet-qr'; diff --git a/src/libs/layout/containers.ts b/src/libs/layout/containers.ts index 11a224820..b1cd25ec4 100644 --- a/src/libs/layout/containers.ts +++ b/src/libs/layout/containers.ts @@ -203,16 +203,6 @@ export const FooterButtonsContainer = styled(SpaceBetweenFlexRow)` background-color: ${({ theme }) => theme.color.backgroundPrimary}; `; -/** - * @deprecated to be replaced by FooterButtonsContainer when Popup layout is refactored - * to be the same as window layout - */ -export const FooterButtonsAbsoluteContainer = styled(FooterButtonsContainer)` - position: absolute; - bottom: 0; - left: 0; -`; - export const ListItemClickableContainer = styled(SpaceBetweenFlexRow)` cursor: pointer; @@ -239,7 +229,6 @@ export const TabHeaderContainer = styled(AlignedFlexRow)` export const TabFooterContainer = styled(FooterButtonsContainer)` padding: 24px 32px; - margin-top: 30px; `; export const TabTextContainer = styled.div` @@ -247,7 +236,7 @@ export const TabTextContainer = styled.div` `; export const TabPageContainer = styled.div` - padding: 24px 32px; + padding: 40px 32px 0; `; export const BreakWordContainer = styled.div` @@ -291,11 +280,17 @@ export const BorderContainer = styled.div` export const Overlay = styled.div` position: fixed; z-index: ${({ theme }) => theme.zIndex.modal}; - top: 0; - left: 0; + top: 50%; + left: 50%; + bottom: 0; + right: 0; + + transform: translate(-50%, -50%); + + overflow: auto; height: 100vh; - width: 100vw; + width: 360px; background: ${({ theme }) => hexToRGBA(theme.color.black, '0.32')}; `; @@ -340,3 +335,10 @@ export const DropdownHeader = styled(AlignedSpaceBetweenFlexRow)` background-color: ${({ theme }) => theme.color.backgroundPrimary}; `; + +export const AmountContainer = styled(SpaceBetweenFlexColumn)` + align-items: flex-end; + text-align: end; + + max-width: 120px; +`; diff --git a/src/libs/layout/error/content.tsx b/src/libs/layout/error/content.tsx index d6045848c..ca4dbb705 100644 --- a/src/libs/layout/error/content.tsx +++ b/src/libs/layout/error/content.tsx @@ -14,13 +14,13 @@ import { ErrorContent } from './types'; interface ErrorPageContentProps extends ErrorContent { // TODO: I guess will be better pass it through location state. It require extending of location state type - illustrationType: 'onboarding' | 'general'; + pageType: 'onboarding' | 'general'; } export function ErrorPageContent({ errorContentText, errorHeaderText, - illustrationType + pageType }: ErrorPageContentProps) { if (errorContentText == null || errorHeaderText == null) { throw new Error('Cannot render ErrorPage: not enough props'); @@ -29,7 +29,7 @@ export function ErrorPageContent({ return ( - {illustrationType === 'onboarding' ? ( + {pageType === 'onboarding' ? ( - {errorHeaderText} + + {errorHeaderText} + diff --git a/src/libs/layout/error/tab-page.tsx b/src/libs/layout/error/tab-page.tsx index 69e6bab90..66fce8a02 100644 --- a/src/libs/layout/error/tab-page.tsx +++ b/src/libs/layout/error/tab-page.tsx @@ -31,7 +31,7 @@ export function TabErrorPage({ )} renderFooter={() => ( diff --git a/src/libs/layout/error/window-page.tsx b/src/libs/layout/error/window-page.tsx index 1aaab4223..606c697be 100644 --- a/src/libs/layout/error/window-page.tsx +++ b/src/libs/layout/error/window-page.tsx @@ -43,7 +43,7 @@ export function WindowErrorPage({ )} renderFooter={() => ( diff --git a/src/libs/layout/header/header-submenu-bar-buy-cspr-link.tsx b/src/libs/layout/header/header-submenu-bar-buy-cspr-link.tsx new file mode 100644 index 000000000..485198b88 --- /dev/null +++ b/src/libs/layout/header/header-submenu-bar-buy-cspr-link.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import { RouterPath, useTypedNavigate } from '@popup/router'; + +import { Link, Typography } from '@libs/ui/components'; + +export const HeaderSubmenuBarBuyCSPRLink = () => { + const { t } = useTranslation(); + const navigate = useTypedNavigate(); + + return ( + navigate(RouterPath.BuyCSPR)}> + + Buy CSPR + + + ); +}; diff --git a/src/libs/layout/layout-tab.tsx b/src/libs/layout/layout-tab.tsx index c6a16c95a..52ab75265 100644 --- a/src/libs/layout/layout-tab.tsx +++ b/src/libs/layout/layout-tab.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components'; import { SvgIcon } from '@libs/ui/components'; -import { CenteredFlexColumn } from './containers'; +import { CenteredFlexColumn, FlexColumn } from './containers'; const Container = styled.div` width: 100%; @@ -14,7 +14,7 @@ const Container = styled.div` const AbsoluteCenteredContainer = styled(CenteredFlexColumn)` width: 512px; - gap: 40px; + gap: 60px; position: absolute; top: 50%; @@ -24,9 +24,10 @@ const AbsoluteCenteredContainer = styled(CenteredFlexColumn)` interface ContentContainerProps { layoutContext: 'withIllustration' | 'withStepper'; + minHeight?: number | 'auto'; } -const MainContainer = styled.div` +const MainContainer = styled(FlexColumn)` background-color: ${({ theme, layoutContext }) => layoutContext === 'withIllustration' ? theme.color.backgroundPrimary @@ -36,9 +37,16 @@ const MainContainer = styled.div` height: 100%; width: 100%; + min-height: ${({ minHeight }) => + minHeight === 'auto' ? minHeight : minHeight ? `${minHeight}px` : '560px'}; overflow-y: auto; `; +const ContentContainer = styled.div` + height: 100%; + flex-grow: 1; +`; + interface LayoutProps extends ContentContainerProps { renderHeader?: () => JSX.Element; renderContent: () => JSX.Element; @@ -49,7 +57,8 @@ export function LayoutTab({ layoutContext, renderHeader, renderContent, - renderFooter + renderFooter, + minHeight }: LayoutProps) { return ( @@ -59,9 +68,9 @@ export function LayoutTab({ width={164} height={40} /> - + {renderHeader && renderHeader()} - {renderContent()} + {renderContent()} {renderFooter && renderFooter()} diff --git a/src/libs/layout/locked-router/index.tsx b/src/libs/layout/locked-router/index.tsx index 87b5df7cf..8d6ff2657 100644 --- a/src/libs/layout/locked-router/index.tsx +++ b/src/libs/layout/locked-router/index.tsx @@ -1,13 +1,8 @@ import React from 'react'; import { HashRouter, Route, Routes } from 'react-router-dom'; -import { - LayoutWindow, - PopupLayout, - ResetVaultPageContent, - UnlockVaultPageContent -} from '@libs/layout'; -import { HeaderPopup } from '@libs/layout/header/header-popup'; +import { ResetVaultPage } from '@libs/layout/reset-vault'; +import { UnlockVaultPage } from '@libs/layout/unlock-vault'; export const LockedRouterPath = { Any: '*', @@ -18,43 +13,17 @@ interface LockedRouterProps { popupLayout?: boolean; } -export function LockedRouter({ popupLayout }: LockedRouterProps) { - return ( - - - } - renderContent={() => } - /> - ) : ( - } - renderContent={() => } - /> - ) - } - /> - } - renderContent={() => } - /> - ) : ( - } - renderContent={() => } - /> - ) - } - /> - - - ); -} +export const LockedRouter = ({ popupLayout }: LockedRouterProps) => ( + + + } + /> + } + /> + + +); diff --git a/src/libs/layout/reset-vault/content.tsx b/src/libs/layout/reset-vault/content.tsx new file mode 100644 index 000000000..9f6fa1477 --- /dev/null +++ b/src/libs/layout/reset-vault/content.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import { + ContentContainer, + IllustrationContainer, + ParagraphContainer, + SpacingSize +} from '@libs/layout'; +import { SvgIcon, Typography } from '@libs/ui/components'; + +export function ResetVaultPageContent() { + const { t } = useTranslation(); + + return ( + + + + + + + Are you sure you want to reset your wallet? + + + + + + All accounts will be removed. Make sure you have securely stored + your Casper Wallet’s secret recovery phrase and any legacy secret + key files. Without them you may be unable to access the accounts. + + + + + ); +} diff --git a/src/libs/layout/reset-vault/index.tsx b/src/libs/layout/reset-vault/index.tsx index a9a986d34..96bdc8b77 100644 --- a/src/libs/layout/reset-vault/index.tsx +++ b/src/libs/layout/reset-vault/index.tsx @@ -1,24 +1,29 @@ import React, { useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; - -import { useTypedNavigate } from '@popup/router'; +import { useNavigate } from 'react-router-dom'; import { closeWindowByReloadExtension } from '@background/close-window-by-reload-extension'; import { resetVault } from '@background/redux/sagas/actions'; import { dispatchToMainStore } from '@background/redux/utils'; import { - ContentContainer, - FooterButtonsAbsoluteContainer, - IllustrationContainer, - ParagraphContainer, - SpacingSize + FooterButtonsContainer, + HeaderPopup, + LayoutWindow, + PopupLayout } from '@libs/layout'; -import { Button, Checkbox, SvgIcon, Typography } from '@libs/ui/components'; +import { Button, Checkbox } from '@libs/ui/components'; + +import { ResetVaultPageContent } from './content'; + +interface ResetVaultPageProps { + popupLayout?: boolean; +} -export function ResetVaultPageContent() { +export const ResetVaultPage = ({ popupLayout }: ResetVaultPageProps) => { const [isChecked, setIsChecked] = useState(false); - const navigate = useTypedNavigate(); + + const navigate = useNavigate(); const { t } = useTranslation(); function handleResetVault() { @@ -31,50 +36,39 @@ export function ResetVaultPageContent() { navigate(-1); } - return ( - <> - - - - - - - Are you sure you want to reset your wallet? - - - - - - All accounts will be removed. Make sure you have securely stored - your Casper Wallet’s secret recovery phrase and any legacy secret - key files. Without them you may be unable to access the accounts. - - - - - - { - setIsChecked(value); - }} - /> - - - - + const footer = ( + + { + setIsChecked(value); + }} + /> + + + ); -} + + return popupLayout ? ( + } + renderContent={() => } + renderFooter={() => footer} + /> + ) : ( + } + renderContent={() => } + renderFooter={() => footer} + /> + ); +}; diff --git a/src/libs/layout/unlock-vault/content.tsx b/src/libs/layout/unlock-vault/content.tsx new file mode 100644 index 000000000..43fd05cd4 --- /dev/null +++ b/src/libs/layout/unlock-vault/content.tsx @@ -0,0 +1,114 @@ +import React, { useState } from 'react'; +import { FieldErrors, UseFormRegister } from 'react-hook-form'; +import { Trans, useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; + +import { selectHasLoginRetryLockoutTime } from '@background/redux/login-retry-lockout-time/selectors'; + +import { + ContentContainer, + IllustrationContainer, + InputsContainer, + ParagraphContainer, + SpacingSize +} from '@libs/layout'; +import { + Input, + PasswordInputType, + PasswordVisibilityIcon, + SvgIcon, + Typography +} from '@libs/ui/components'; +import { UnlockWalletFormValues } from '@libs/ui/forms/unlock-wallet'; + +interface UnlockVaultPageContentProps { + register: UseFormRegister; + errors: FieldErrors; +} + +export function UnlockVaultPageContent({ + register, + errors +}: UnlockVaultPageContentProps) { + const [passwordInputType, setPasswordInputType] = + useState('password'); + + const { t } = useTranslation(); + const hasLoginRetryLockoutTime = useSelector(selectHasLoginRetryLockoutTime); + + if (hasLoginRetryLockoutTime) { + return ( + <> + + + + + + + + Please wait before the next attempt to unlock your wallet + + + + + + + You’ve reached the maximum number of unlock attempts. For + security reasons, you will need to wait a brief while before you + can attempt again. + + + + + + + You can try again in 5 mins. + + + + + + ); + } + + return ( + + + + + + + Your wallet is locked + + + + + Please enter your password to unlock. + + + + + } + {...register('password')} + /> + + + ); +} diff --git a/src/libs/layout/unlock-vault/index.tsx b/src/libs/layout/unlock-vault/index.tsx index d9fc924e4..eb9252ece 100644 --- a/src/libs/layout/unlock-vault/index.tsx +++ b/src/libs/layout/unlock-vault/index.tsx @@ -1,15 +1,25 @@ +import * as Yup from 'yup'; import { Player } from '@lottiefiles/react-lottie-player'; import React, { useState } from 'react'; +import { useForm } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { useNavigate } from 'react-router-dom'; +import { AnyObject } from 'yup/es/types'; + +import { + ERROR_DISPLAYED_BEFORE_ATTEMPT_IS_DECREMENTED, + LOGIN_RETRY_ATTEMPTS_LIMIT +} from '@src/constants'; +import { getErrorMessageForIncorrectPassword } from '@src/utils'; import { selectKeyDerivationSaltHash, selectPasswordHash, selectPasswordSaltHash } from '@background/redux/keys/selectors'; -import { selectHasLoginRetryLockoutTime } from '@background/redux/login-retry-lockout-time/selectors'; +import { loginRetryCountIncremented } from '@background/redux/login-retry-count/actions'; +import { selectLoginRetryCount } from '@background/redux/login-retry-count/selectors'; import { unlockVault } from '@background/redux/sagas/actions'; import { UnlockVault } from '@background/redux/sagas/types'; import { dispatchToMainStore } from '@background/redux/utils'; @@ -21,44 +31,52 @@ import { useLockWalletWhenNoMoreRetries } from '@hooks/use-lock-wallet-when-no-m import unlockAnimation from '@libs/animations/unlock_animation.json'; import { AlignedFlexRow, - ContentContainer, - FooterButtonsAbsoluteContainer, - IllustrationContainer, - InputsContainer, + FooterButtonsContainer, + HeaderPopup, + LayoutWindow, LockedRouterPath, - ParagraphContainer, + PopupLayout, SpacingSize } from '@libs/layout'; -import { - Button, - Input, - PasswordInputType, - PasswordVisibilityIcon, - SvgIcon, - Typography -} from '@libs/ui/components'; -import { - UnlockWalletFormValues, - useUnlockWalletForm -} from '@libs/ui/forms/unlock-wallet'; +import { Button, Typography } from '@libs/ui/components'; +import { UnlockWalletFormValues } from '@libs/ui/forms/unlock-wallet'; + +import { UnlockVaultPageContent } from './content'; interface UnlockMessageEvent extends MessageEvent { data: UnlockVault; } -export function UnlockVaultPageContent() { - const { t } = useTranslation(); - const navigate = useNavigate(); +interface UnlockVaultPageProps { + popupLayout?: boolean; +} + +interface VerifyPasswordMessageEvent extends MessageEvent { + data: { + isPasswordCorrect: Yup.StringSchema< + string | undefined, + AnyObject, + string | undefined + >; + }; +} + +export const UnlockVaultPage = ({ popupLayout }: UnlockVaultPageProps) => { const [isLoading, setIsLoading] = useState(false); - const [passwordInputType, setPasswordInputType] = - useState('password'); + const { t } = useTranslation(); + const navigate = useNavigate(); const passwordHash = useSelector(selectPasswordHash); const passwordSaltHash = useSelector(selectPasswordSaltHash); const keyDerivationSaltHash = useSelector(selectKeyDerivationSaltHash); const vaultCipher = useSelector(selectVaultCipher); - const hasLoginRetryLockoutTime = useSelector(selectHasLoginRetryLockoutTime); + const loginRetryCount = useSelector(selectLoginRetryCount); + + const attemptsLeft = + LOGIN_RETRY_ATTEMPTS_LIMIT - + loginRetryCount - + ERROR_DISPLAYED_BEFORE_ATTEMPT_IS_DECREMENTED; if (passwordHash == null || passwordSaltHash == null) { throw Error("Password doesn't exist"); @@ -67,28 +85,56 @@ export function UnlockVaultPageContent() { const { register, handleSubmit, + formState: { errors }, resetField, - formState: { errors } - } = useUnlockWalletForm(passwordHash, passwordSaltHash); + setError + } = useForm({ + defaultValues: { + password: '' + } + }); async function handleUnlockVault({ password }: UnlockWalletFormValues) { if (isLoading) return; setIsLoading(true); + + const verifyPasswordWorker = new Worker( + new URL('@background/workers/verify-password-worker.ts', import.meta.url) + ); const unlockVaultWorker = new Worker( - new URL('@background/workers/unlockVaultWorker.ts', import.meta.url) + new URL('@background/workers/unlock-vault-worker.ts', import.meta.url) ); if (keyDerivationSaltHash == null) { throw Error("Key derivation salt doesn't exist"); } - unlockVaultWorker.postMessage({ - password, - keyDerivationSaltHash, - vaultCipher + verifyPasswordWorker.postMessage({ + passwordHash, + passwordSaltHash, + password }); + verifyPasswordWorker.onmessage = (event: VerifyPasswordMessageEvent) => { + const { isPasswordCorrect } = event.data; + const errorMessage = getErrorMessageForIncorrectPassword(attemptsLeft); + + if (!isPasswordCorrect) { + dispatchToMainStore(loginRetryCountIncremented()); + setError('password', { + message: t(errorMessage) + }); + setIsLoading(false); + } else { + unlockVaultWorker.postMessage({ + password, + keyDerivationSaltHash, + vaultCipher + }); + } + }; + unlockVaultWorker.onmessage = (event: UnlockMessageEvent) => { const { vault, @@ -145,6 +191,11 @@ export function UnlockVaultPageContent() { } }; + verifyPasswordWorker.onerror = error => { + console.error(error); + setIsLoading(false); + }; + unlockVaultWorker.onerror = error => { console.error(error); setIsLoading(false); @@ -153,111 +204,58 @@ export function UnlockVaultPageContent() { useLockWalletWhenNoMoreRetries(resetField); - if (hasLoginRetryLockoutTime) { - return ( - <> - - - + - - - + + ) : ( + + Unlock wallet + + )} + + + ); -} + + return popupLayout ? ( + } + renderContent={() => ( + + )} + renderFooter={() => footer} + /> + ) : ( + } + renderContent={() => ( + + )} + renderFooter={() => footer} + /> + ); +}; diff --git a/src/libs/services/account-activity-service/types.ts b/src/libs/services/account-activity-service/types.ts index 35953cb41..1b02c6011 100644 --- a/src/libs/services/account-activity-service/types.ts +++ b/src/libs/services/account-activity-service/types.ts @@ -242,6 +242,7 @@ export interface Metadata { export type Erc20TransferWithId = { id: string; + amount?: string; deployHash: string; callerPublicKey: string; timestamp: string; diff --git a/src/libs/ui/components/account-activity-plate/account-activity-plate.tsx b/src/libs/ui/components/account-activity-plate/account-activity-plate.tsx index f08843c23..525f9d85f 100644 --- a/src/libs/ui/components/account-activity-plate/account-activity-plate.tsx +++ b/src/libs/ui/components/account-activity-plate/account-activity-plate.tsx @@ -106,22 +106,29 @@ export const AccountActivityPlate = forwardRef( : callerPublicKey; try { - const parsedAmount = - ((typeof args?.amount?.parsed === 'string' || - typeof args?.amount?.parsed === 'number') && - args?.amount?.parsed) || - '-'; - - if (parsedAmount !== '-') { - const stringAmount = - typeof parsedAmount === 'number' - ? parsedAmount.toString() - : parsedAmount; - + if (transactionInfo.amount) { amount = Number.isInteger(decimals) && decimals !== undefined - ? divideErc20Balance(stringAmount, decimals) - : motesToCSPR(stringAmount); + ? divideErc20Balance(transactionInfo.amount, decimals) + : motesToCSPR(transactionInfo.amount); + } else { + const parsedAmount = + ((typeof args?.amount?.parsed === 'string' || + typeof args?.amount?.parsed === 'number') && + args?.amount?.parsed) || + '-'; + + if (parsedAmount !== '-') { + const stringAmount = + typeof parsedAmount === 'number' + ? parsedAmount.toString() + : parsedAmount; + + amount = + Number.isInteger(decimals) && decimals !== undefined + ? divideErc20Balance(stringAmount, decimals) + : motesToCSPR(stringAmount); + } } } catch (error) { console.error(error); diff --git a/src/libs/ui/components/account-popover/account-popover.tsx b/src/libs/ui/components/account-popover/account-popover.tsx index 4cd600ef4..9eaaa8fc8 100644 --- a/src/libs/ui/components/account-popover/account-popover.tsx +++ b/src/libs/ui/components/account-popover/account-popover.tsx @@ -10,7 +10,7 @@ import { RouterPath, useTypedNavigate } from '@popup/router'; import { selectActiveOrigin } from '@background/redux/active-origin/selectors'; import { selectApiConfigBasedOnActiveNetwork } from '@background/redux/settings/selectors'; import { dispatchToMainStore } from '@background/redux/utils'; -import { hideAccountFromListChange } from '@background/redux/vault/actions'; +import { hideAccountFromListChanged } from '@background/redux/vault/actions'; import { selectConnectedAccountNamesWithActiveOrigin, selectIsAnyAccountConnectedWithActiveOrigin @@ -133,7 +133,7 @@ export const AccountActionsMenuPopover = ({ variant="contentAction" onClick={() => { dispatchToMainStore( - hideAccountFromListChange({ accountName: account.name }) + hideAccountFromListChanged({ accountName: account.name }) ); }} > diff --git a/src/libs/ui/components/active-account-plate/active-account-plate.tsx b/src/libs/ui/components/active-account-plate/active-account-plate.tsx index f638d7ae4..bb43999ed 100644 --- a/src/libs/ui/components/active-account-plate/active-account-plate.tsx +++ b/src/libs/ui/components/active-account-plate/active-account-plate.tsx @@ -22,7 +22,7 @@ import { Typography } from '@libs/ui/components'; -export const AmountContainer = styled(SpaceBetweenFlexColumn)` +const AmountContainer = styled(SpaceBetweenFlexColumn)` align-items: flex-end; max-width: 120px; @@ -42,12 +42,14 @@ interface ActiveAccountPlateProps { label: string; balance: string | null; symbol: string | null; + top?: SpacingSize; } export const ActiveAccountPlate = ({ label, symbol, - balance + balance, + top }: ActiveAccountPlateProps) => { const { t } = useTranslation(); @@ -59,7 +61,7 @@ export const ActiveAccountPlate = ({ return ( <> - + {label} diff --git a/src/libs/ui/components/avatar/avatar.tsx b/src/libs/ui/components/avatar/avatar.tsx index c4d851db6..93b616cbc 100644 --- a/src/libs/ui/components/avatar/avatar.tsx +++ b/src/libs/ui/components/avatar/avatar.tsx @@ -69,7 +69,8 @@ export const Avatar = ({ return ( ` - padding: 20px 16px; + padding: 12px 16px; cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')}; `; @@ -59,15 +60,14 @@ const AmountContainer = styled(FlexColumn)` `; interface ListProps { - ledgerAccountsWithBalance: ILedgerAccountListItem[]; + accountsWithBalance: (ILedgerAccountListItem | AccountListRows)[]; setIsButtonDisabled: React.Dispatch>; - selectedAccounts: ILedgerAccountListItem[]; - setSelectedAccounts: React.Dispatch< - React.SetStateAction - >; + selectedAccounts: (ILedgerAccountListItem | AccountListRows)[]; + setSelectedAccounts: React.Dispatch>; maxItemsToRender: number; onLoadMore: () => void; isLoadingMore: boolean; + namePrefix: string; } type FormFields = FieldValues & { @@ -75,14 +75,15 @@ type FormFields = FieldValues & { checkbox: boolean[]; }; -export const LedgerAccountsList = ({ - ledgerAccountsWithBalance, +export const DynamicAccountsListWithSelect = ({ + accountsWithBalance, setIsButtonDisabled, selectedAccounts, setSelectedAccounts, maxItemsToRender, onLoadMore, - isLoadingMore + isLoadingMore, + namePrefix }: ListProps) => { const [accountNames, setAccountNames] = useState<{ name: string }[]>([]); const [checkboxes, setCheckboxes] = useState([]); @@ -113,14 +114,10 @@ export const LedgerAccountsList = ({ }); useEffect(() => { - for ( - let i = inputsFields.length; - i < ledgerAccountsWithBalance.length; - i++ - ) { - append({ name: `Ledger account ${i + 1}` }); + for (let i = inputsFields.length; i < accountsWithBalance.length; i++) { + append({ name: `${namePrefix} ${i + 1}` }); } - }, [append, inputsFields.length, ledgerAccountsWithBalance]); + }, [append, inputsFields.length, accountsWithBalance, namePrefix]); useEffect(() => { setAccountNames(getValues('accountNames')); @@ -148,9 +145,10 @@ export const LedgerAccountsList = ({ return ( - rows={ledgerAccountsWithBalance} + rows={accountsWithBalance} contentTop={SpacingSize.XL} maxItemsToRender={maxItemsToRender} + maxHeight={290} renderRow={(account, index) => { const inputFieldName = `accountNames.${index}.name`; const checkBoxFieldName = `checkbox.${index}`; @@ -185,6 +183,7 @@ export const LedgerAccountsList = ({ control={control} render={({ field: checkboxControllerField }) => ( { if (isAlreadyConnected) return; diff --git a/src/libs/ui/components/erc20-token-activity-list/erc20-token-activity-list.tsx b/src/libs/ui/components/erc20-token-activity-list/erc20-token-activity-list.tsx index 922dc5d56..f0d6ca1e4 100644 --- a/src/libs/ui/components/erc20-token-activity-list/erc20-token-activity-list.tsx +++ b/src/libs/ui/components/erc20-token-activity-list/erc20-token-activity-list.tsx @@ -57,6 +57,7 @@ export const Erc20TokenActivityList = () => { tokenActivity?.tokenActivityList?.map((transaction, index) => { return { id: String(index), + amount: transaction.amount, deployHash: transaction.deploy_hash, callerPublicKey: transaction.deploy?.caller_public_key || '-', timestamp: transaction.deploy?.timestamp || '-', diff --git a/src/libs/ui/components/index.ts b/src/libs/ui/components/index.ts index eaf008ce1..6a133fd89 100644 --- a/src/libs/ui/components/index.ts +++ b/src/libs/ui/components/index.ts @@ -30,6 +30,7 @@ export * from './tabs/tabs'; export * from './token-plate/token-plate'; export * from './deploy-status/deploy-status'; export * from './modal/modal'; +export * from './modal/modal-switcher'; export * from './active-account-plate/active-account-plate'; export * from './recipient-plate/recipient-plate'; export * from './account-casper-activity-plate/account-casper-activity-plate'; @@ -52,9 +53,12 @@ export * from './theme-switcher/theme-switcher'; export * from './identicon/identicon'; export * from './spinner/spinner'; export * from './tips/tips'; +export * from './recipient-tabs/recipient-tabs'; +export * from './transaction-fee-plate/transaction-fee-plate'; export * from './review-with-ledger/review-with-ledger'; export * from './no-connected-ledger/no-connected-ledger'; export * from './ledger-footer/ledger-footer'; export * from './ledger-error-view/ledger-error-view'; export * from './ledger-event-view/ledger-event-view'; export * from './ledger-connection-view/ledger-connection-view'; +export * from './dynamic-accounts-list-with-select/dynamic-accounts-list-with-select'; diff --git a/src/libs/ui/components/input/input.tsx b/src/libs/ui/components/input/input.tsx index 9d4e32ead..40cf71f39 100644 --- a/src/libs/ui/components/input/input.tsx +++ b/src/libs/ui/components/input/input.tsx @@ -6,14 +6,6 @@ import { BaseProps } from '@libs/ui/types'; type Ref = HTMLInputElement; -const getThemeColorByError = (error?: boolean) => { - if (error == null || !error) { - return 'contentDisabled'; - } - - return 'fillCritical'; -}; - const InputContainer = styled('div')( ({ theme, @@ -45,7 +37,9 @@ const InputContainer = styled('div')( path: { fill: oneColoredIcons ? theme.color.contentDisabled - : theme.color[getThemeColorByError(error)] + : error + ? theme.color.fillCritical + : '' }, ...((disabled || readOnly) && { diff --git a/src/apps/popup/pages/buy-cspr/components/switcher.tsx b/src/libs/ui/components/modal/modal-switcher.tsx similarity index 75% rename from src/apps/popup/pages/buy-cspr/components/switcher.tsx rename to src/libs/ui/components/modal/modal-switcher.tsx index aafe4e0ba..30038b4a6 100644 --- a/src/apps/popup/pages/buy-cspr/components/switcher.tsx +++ b/src/libs/ui/components/modal/modal-switcher.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { FieldValues, UseFormRegister } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; import styled from 'styled-components'; @@ -7,20 +6,23 @@ import { AlignedFlexRow, FlexColumn, FooterButtonsContainer, - InputsContainer, ParagraphContainer, SpacingSize } from '@libs/layout'; -import { Button, Input, SvgIcon, Typography } from '@libs/ui/components'; +import { Button, Typography } from '@libs/ui/components'; const Container = styled(FlexColumn)` background-color: ${({ theme }) => theme.color.backgroundSecondary}; border-top-right-radius: ${({ theme }) => theme.borderRadius.sixteen}px; border-top-left-radius: ${({ theme }) => theme.borderRadius.sixteen}px; + + height: 528px; `; const ContentContainer = styled.div` padding: 0 16px; + + flex-grow: 1; `; const HeaderContainer = styled(AlignedFlexRow)` @@ -36,19 +38,15 @@ const CancelButton = styled(Typography)` `; interface SwitcherProps { - label: 'Country' | 'Currency'; + label: string; closeSwitcher: (e: React.MouseEvent) => void; children: React.ReactNode; - register: UseFormRegister; - searchName: 'countryNameSearch' | 'currencySearch'; } -export const Switcher = ({ +export const ModalSwitcher = ({ label, closeSwitcher, - children, - register, - searchName + children }: SwitcherProps) => { const { t } = useTranslation(); @@ -71,14 +69,6 @@ export const Switcher = ({ - - } - placeholder={t('Search')} - {...register(searchName)} - /> - - {children} diff --git a/src/libs/ui/components/modal/modal.tsx b/src/libs/ui/components/modal/modal.tsx index 73f08b69f..eb536cb92 100644 --- a/src/libs/ui/components/modal/modal.tsx +++ b/src/libs/ui/components/modal/modal.tsx @@ -33,7 +33,7 @@ const slideOutToBottom = keyframes` const ModalContainer = styled.div<{ placement: 'top' | 'bottom' | 'fullBottom'; }>(({ theme, placement }) => ({ - position: 'fixed', + position: 'absolute', margin: '0 16px', diff --git a/src/libs/ui/components/recipient-tabs/components/contacts-list.tsx b/src/libs/ui/components/recipient-tabs/components/contacts-list.tsx new file mode 100644 index 000000000..6ea40ade6 --- /dev/null +++ b/src/libs/ui/components/recipient-tabs/components/contacts-list.tsx @@ -0,0 +1,60 @@ +import React, { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; + +import { selectAllContacts } from '@background/redux/contacts/selectors'; +import { ContactWithId } from '@background/redux/contacts/types'; +import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; + +import { SpacingSize, TileContainer } from '@libs/layout'; +import { List, RecipientPlate, Tile, Typography } from '@libs/ui/components'; + +interface ContactsListProps { + handleSelectRecipient: (publicKey: string, name: string) => void; +} + +export const ContactsList = ({ handleSelectRecipient }: ContactsListProps) => { + const [contactsWithId, setContactsWithId] = useState([]); + + const contacts = useSelector(selectAllContacts); + const activeAccount = useSelector(selectVaultActiveAccount); + + useEffect(() => { + const contactsWithId = contacts + .map(contact => ({ + ...contact, + id: contact.name + })) + .filter(account => account.publicKey !== activeAccount?.publicKey); + + setContactsWithId(contactsWithId); + }, [contacts, activeAccount]); + + if (contactsWithId.length === 0) { + return ( + + + + No contacts found + + + + ); + } + + return ( + ( + { + handleSelectRecipient(contact.publicKey, contact.name); + }} + /> + )} + marginLeftForItemSeparatorLine={56} + /> + ); +}; diff --git a/src/libs/ui/components/recipient-tabs/components/my-accounts-list.tsx b/src/libs/ui/components/recipient-tabs/components/my-accounts-list.tsx new file mode 100644 index 000000000..87a8f4404 --- /dev/null +++ b/src/libs/ui/components/recipient-tabs/components/my-accounts-list.tsx @@ -0,0 +1,52 @@ +import React, { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; + +import { + selectVaultAccountsWithBalances, + selectVaultActiveAccount +} from '@background/redux/vault/selectors'; + +import { SpacingSize } from '@libs/layout'; +import { AccountListRows } from '@libs/types/account'; +import { List, RecipientPlate } from '@libs/ui/components'; + +interface MyAccountsListProps { + handleSelectRecipient: (publicKey: string, name: string) => void; +} + +export const MyAccountsList = ({ + handleSelectRecipient +}: MyAccountsListProps) => { + const [accountsWithIds, setAccountsWithIds] = useState([]); + + const accounts = useSelector(selectVaultAccountsWithBalances); + const activeAccount = useSelector(selectVaultActiveAccount); + + useEffect(() => { + const accountsWithIds = accounts + .map(account => ({ + ...account, + id: account.name + })) + .filter(account => account.publicKey !== activeAccount?.publicKey); + + setAccountsWithIds(accountsWithIds); + }, [accounts, setAccountsWithIds, activeAccount]); + + return ( + ( + { + handleSelectRecipient(account.publicKey, account.name); + }} + /> + )} + marginLeftForItemSeparatorLine={56} + /> + ); +}; diff --git a/src/libs/ui/components/recipient-tabs/components/recent-list.tsx b/src/libs/ui/components/recipient-tabs/components/recent-list.tsx new file mode 100644 index 000000000..9c5ff5059 --- /dev/null +++ b/src/libs/ui/components/recipient-tabs/components/recent-list.tsx @@ -0,0 +1,82 @@ +import React, { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; + +import { selectAllContacts } from '@background/redux/contacts/selectors'; +import { selectRecentRecipientPublicKeys } from '@background/redux/recent-recipient-public-keys/selectors'; +import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; + +import { SpacingSize, TileContainer } from '@libs/layout'; +import { List, RecipientPlate, Tile, Typography } from '@libs/ui/components'; + +interface MyAccountsListProps { + handleSelectRecipient: (publicKey: string, name: string) => void; +} + +interface RecentListState { + publicKey: string; + id: string; + name: string; +} + +export const RecentList = ({ handleSelectRecipient }: MyAccountsListProps) => { + const [accountsWithIds, setAccountsWithIds] = useState([]); + + const recentRecipientPublicKeys = useSelector( + selectRecentRecipientPublicKeys + ); + const contacts = useSelector(selectAllContacts); + const activeAccount = useSelector(selectVaultActiveAccount); + + useEffect(() => { + const recentRecipient = recentRecipientPublicKeys + .map(publicKey => { + const contact = contacts.find( + contact => contact.publicKey === publicKey + ); + if (contact) { + return { + name: contact.name, + publicKey: publicKey, + id: publicKey + }; + } + return { + name: '', + publicKey: publicKey, + id: publicKey + }; + }) + .filter(account => account.publicKey !== activeAccount?.publicKey); + + setAccountsWithIds(recentRecipient); + }, [contacts, recentRecipientPublicKeys, setAccountsWithIds, activeAccount]); + + if (!accountsWithIds.length) { + return ( + + + + No recent recipients were found. + + + + ); + } + + return ( + ( + { + handleSelectRecipient(recent.publicKey, recent.name); + }} + /> + )} + marginLeftForItemSeparatorLine={56} + /> + ); +}; diff --git a/src/libs/ui/components/recipient-tabs/recipient-tabs.tsx b/src/libs/ui/components/recipient-tabs/recipient-tabs.tsx new file mode 100644 index 000000000..51a10d932 --- /dev/null +++ b/src/libs/ui/components/recipient-tabs/recipient-tabs.tsx @@ -0,0 +1,157 @@ +import React, { useEffect, useState } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +import { useClickAway } from '@hooks/use-click-away'; + +import { SpacingSize, VerticalSpaceContainer } from '@libs/layout'; +import { Input, RecipientPlate, SvgIcon, Tab, Tabs } from '@libs/ui/components'; +import { TransferRecipientFormValues } from '@libs/ui/forms/transfer'; +import { TransferNftRecipientFormValues } from '@libs/ui/forms/transfer-nft'; + +import { ContactsList } from './components/contacts-list'; +import { MyAccountsList } from './components/my-accounts-list'; +import { RecentList } from './components/recent-list'; +import { RecipientTabName } from './utils'; + +interface RecipientTabsProps { + recipientForm: UseFormReturn< + TransferRecipientFormValues | TransferNftRecipientFormValues + >; + setRecipientPublicKey?: (value: React.SetStateAction) => void; + setRecipientName: React.Dispatch>; + recipientName: string; +} + +export const RecipientTabs = ({ + recipientForm, + setRecipientPublicKey, + setRecipientName, + recipientName +}: RecipientTabsProps) => { + const [showRecipientPlate, setShowRecipientPlate] = useState(false); + + const { t } = useTranslation(); + + const { register, trigger, control, formState, setValue } = recipientForm; + const { errors } = formState; + const { onChange } = register('recipientPublicKey'); + + const inputValue = useWatch({ + control: control, + name: 'recipientPublicKey' + }); + + const { ref: clickAwayRef } = useClickAway({ + callback: () => { + if (formState.isValid) { + setShowRecipientPlate(true); + } + } + }); + + useEffect(() => { + if (formState.isValid) { + setShowRecipientPlate(true); + } + // This should trigger only once + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const handleIconClick = () => { + navigator.clipboard + .readText() + .then(clipboardText => { + setValue('recipientPublicKey', clipboardText); + if (setRecipientPublicKey) { + setRecipientPublicKey(clipboardText); + } + + trigger('recipientPublicKey').then(isValid => { + if (isValid) { + setShowRecipientPlate(true); + } + }); + }) + .catch(err => { + console.error('Could not read clipboard: ', err); + }); + }; + + const handleSelectRecipient = (publicKey: string, name: string) => { + if (setRecipientPublicKey) { + setRecipientPublicKey(publicKey); + } + setValue('recipientPublicKey', publicKey); + + setShowRecipientPlate(true); + setRecipientName(name); + + trigger('recipientPublicKey'); + }; + + return showRecipientPlate ? ( + + { + setShowRecipientPlate(false); + + trigger('recipientPublicKey'); + }} + /> + + ) : ( + { + setShowRecipientPlate(false); + }} + > + } + suffixIcon={ + !!errors?.recipientPublicKey ? null : ( + + ) + } + placeholder={t('Public key')} + {...register('recipientPublicKey')} + onChange={e => { + onChange(e); + + if (setRecipientPublicKey) { + setRecipientPublicKey(e.target.value); + } + }} + error={!!errors?.recipientPublicKey} + validationText={errors?.recipientPublicKey?.message} + autoComplete="off" + /> + + + + + + + + + + + + + + + ); +}; diff --git a/src/libs/ui/components/recipient-tabs/utils.ts b/src/libs/ui/components/recipient-tabs/utils.ts new file mode 100644 index 000000000..77f9bdbe3 --- /dev/null +++ b/src/libs/ui/components/recipient-tabs/utils.ts @@ -0,0 +1,5 @@ +export enum RecipientTabName { + Recent = 'Recent', + MyAccounts = 'My accounts', + Contacts = 'Contacts' +} diff --git a/src/libs/ui/components/tabs/tabs.tsx b/src/libs/ui/components/tabs/tabs.tsx index fd673d01c..fa4cbc589 100644 --- a/src/libs/ui/components/tabs/tabs.tsx +++ b/src/libs/ui/components/tabs/tabs.tsx @@ -63,7 +63,7 @@ export function Tabs({ children, preferActiveTabId }: TabsProps) { return ( <> - + {children.map((tab, index) => { const { tabName } = tab.props; diff --git a/src/libs/ui/components/token-plate/token-plate.tsx b/src/libs/ui/components/token-plate/token-plate.tsx index 66fdc2c5f..636f35904 100644 --- a/src/libs/ui/components/token-plate/token-plate.tsx +++ b/src/libs/ui/components/token-plate/token-plate.tsx @@ -46,6 +46,7 @@ export const TokenPlate = ({ }: TokenPlateProps) => { const tokenIconFormat = token?.icon?.split('.').pop(); const isTokenIconJPG = tokenIconFormat === 'jpg'; + const isTokenIconPNG = tokenIconFormat === 'png'; return ( - {isTokenIconJPG ? ( + {isTokenIconJPG || isTokenIconPNG ? ( { + const { t } = useTranslation(); + + const currencyRate = useSelector(selectAccountCurrencyRate); + + return ( + + + + Transaction fee + + + + {formatNumber(paymentAmount, { + precision: { max: 5 } + })}{' '} + CSPR + + + {formatFiatAmount( + motesToCSPR(TRANSFER_COST_MOTES) || '0', + currencyRate, + 3 + )} + + + + + ); +}; diff --git a/src/libs/ui/components/typography/typography.tsx b/src/libs/ui/components/typography/typography.tsx index c1f03e74f..a2d2bb4fe 100644 --- a/src/libs/ui/components/typography/typography.tsx +++ b/src/libs/ui/components/typography/typography.tsx @@ -9,6 +9,7 @@ type Ref = HTMLSpanElement | HTMLHeadingElement; export type TypographyType = | 'header' + | 'headerBig' | 'body' | 'bodySemiBold' | 'bodyHash' @@ -220,21 +221,37 @@ const StyledTypography = styled('span').withConfig({ const StyledHeader = styled('h1').withConfig({ shouldForwardProp: (prop, defaultValidatorFn) => !['loading'].includes(prop) && defaultValidatorFn(prop) -})(({ theme, ...props }) => { +})(({ theme, type, ...props }) => { const body = getBodyStyles(theme, props); - return { - ...body, - fontWeight: theme.typography.fontWeight.bold, - fontSize: '2.4rem', - lineHeight: '2.8rem' - }; + + switch (type) { + case 'header': { + return { + ...body, + fontWeight: theme.typography.fontWeight.bold, + fontSize: '2.4rem', + lineHeight: '2.8rem' + }; + } + case 'headerBig': { + return { + ...body, + fontWeight: theme.typography.fontWeight.bold, + fontSize: '2.8rem', + lineHeight: '3.6rem' + }; + } + } }); export const Typography = forwardRef(function Typography( { dataTestId, ...props }, ref ) { - const Component = props.type !== 'header' ? StyledTypography : StyledHeader; + const Component = + props.type !== 'header' && props.type !== 'headerBig' + ? StyledTypography + : StyledHeader; if (props.loading) { return ( diff --git a/src/libs/ui/forms/create-account.ts b/src/libs/ui/forms/create-account.ts index 63826a7fc..382166e93 100644 --- a/src/libs/ui/forms/create-account.ts +++ b/src/libs/ui/forms/create-account.ts @@ -2,6 +2,9 @@ import * as Yup from 'yup'; import { yupResolver } from '@hookform/resolvers/yup/dist/yup'; import { UseFormProps, useForm } from 'react-hook-form'; +import { SecretPhrase, deriveKeyPair } from '@libs/crypto'; +import { AccountWithBalance } from '@libs/types/account'; + import { useAccountNameRule } from './form-validation-rules'; export type CreateAccountFormValues = { @@ -30,12 +33,28 @@ export function useCreateAccountForm( } export function getDefaultName( - derivedAccountsCount: number, - existingAccountNames: string[] + existingAccountNames: string[], + derivedAccounts: AccountWithBalance[], + secretPhrase: SecretPhrase | null ) { const accountString = 'Account'; - let sequenceNumber = derivedAccountsCount + 1; + let isAccountAlreadyAdded = true; + let i = 0; + + while (isAccountAlreadyAdded) { + const keyPair = deriveKeyPair(secretPhrase, i); + + if ( + !derivedAccounts.some(account => account.publicKey === keyPair.publicKey) + ) { + isAccountAlreadyAdded = false; + break; + } + i++; + } + + let sequenceNumber = i + 1; let defaultName = `${accountString} ${sequenceNumber}`; while (existingAccountNames.includes(defaultName)) { diff --git a/src/libs/ui/forms/form-validation-rules.ts b/src/libs/ui/forms/form-validation-rules.ts index 0e5f3a20e..75d9e4a61 100644 --- a/src/libs/ui/forms/form-validation-rules.ts +++ b/src/libs/ui/forms/form-validation-rules.ts @@ -6,13 +6,19 @@ import { useSelector } from 'react-redux'; import { AuctionManagerEntryPoint, DELEGATION_MIN_AMOUNT_MOTES, + ERROR_DISPLAYED_BEFORE_ATTEMPT_IS_DECREMENTED, LOGIN_RETRY_ATTEMPTS_LIMIT, MAX_DELEGATORS, STAKE_COST_MOTES, TRANSFER_COST_MOTES, TRANSFER_MIN_AMOUNT_MOTES } from '@src/constants'; -import { isValidPublicKey, isValidSecretKeyHash, isValidU64 } from '@src/utils'; +import { + getErrorMessageForIncorrectPassword, + isValidPublicKey, + isValidSecretKeyHash, + isValidU64 +} from '@src/utils'; import { loginRetryCountIncremented } from '@background/redux/login-retry-count/actions'; import { selectLoginRetryCount } from '@background/redux/login-retry-count/selectors'; @@ -23,8 +29,6 @@ import { CSPRtoMotes, motesToCSPR } from '@libs/ui/utils/formatters'; export const minPasswordLength = 16; -const ERROR_DISPLAYED_BEFORE_ATTEMPT_IS_DECREMENTED = 1; - export function useCreatePasswordRule() { const { t } = useTranslation(); const passwordAmountCharactersMessage = t( @@ -38,7 +42,6 @@ export function useVerifyPasswordAgainstHashRule( passwordHash: string, passwordSaltHash: string ) { - const { t } = useTranslation(); const loginRetryCount = useSelector(selectLoginRetryCount); const attemptsLeft = @@ -46,14 +49,7 @@ export function useVerifyPasswordAgainstHashRule( loginRetryCount - ERROR_DISPLAYED_BEFORE_ATTEMPT_IS_DECREMENTED; - const errorMessage = - attemptsLeft === 1 - ? t( - 'Password is incorrect. You’ve got last attempt, after that you’ll have to wait for 5 mins' - ) - : t( - `Password is incorrect. You’ve got ${attemptsLeft} attempts, after that you’ll have to wait for 5 mins` - ); + const errorMessage = getErrorMessageForIncorrectPassword(attemptsLeft); return Yup.string().test('authenticate', errorMessage, async password => { const result = await verifyPasswordAgainstHash( @@ -189,10 +185,25 @@ export const useCSPRTransferAmountRule = (amountMotes: string | null) => { message: t( 'Your account balance is not high enough. Enter a smaller amount.' ) + }) + .test({ + name: 'decimalPartLength', + test: amountInputValue => { + if (amountInputValue) { + const decimalPart = amountInputValue.split('.')[1]; + return decimalPart == null || decimalPart.length <= 9; + } + + return false; + }, + message: t('No more than 9 decimals') }); }; -export const useErc20AmountRule = (amount: string | null) => { +export const useErc20AmountRule = ( + amount: string | null, + decimals: number | undefined +) => { const { t } = useTranslation(); const maxAmount: string = amount == null ? '0' : Big(amount).toFixed(); @@ -233,6 +244,22 @@ export const useErc20AmountRule = (amount: string | null) => { message: t( 'Your account balance is not high enough. Enter a smaller amount.' ) + }) + .test({ + name: 'decimalPartLength', + test: amountInputValue => { + if (amountInputValue) { + const decimalPart = amountInputValue.split('.')[1]; + if (decimals || decimals === 0) { + return decimalPart == null || decimalPart.length <= decimals; + } + + return true; + } + + return false; + }, + message: t(`No more than ${decimals} decimals`) }); }; @@ -403,8 +430,9 @@ export const useCSPRStakeAmountRule = ( }; export const useValidatorPublicKeyRule = ( - stakesType: AuctionManagerEntryPoint, - delegatorsNumber?: number + stakeType: AuctionManagerEntryPoint, + delegatorsNumber?: number, + hasDelegationToSelectedValidator?: boolean ) => { const { t } = useTranslation(); @@ -419,16 +447,16 @@ export const useValidatorPublicKeyRule = ( name: 'maxDelegators', test: () => { if ( - stakesType === AuctionManagerEntryPoint.undelegate || - stakesType === AuctionManagerEntryPoint.redelegate + stakeType === AuctionManagerEntryPoint.undelegate || + stakeType === AuctionManagerEntryPoint.redelegate ) { return true; } - if (delegatorsNumber) { + if (delegatorsNumber && !hasDelegationToSelectedValidator) { return delegatorsNumber < MAX_DELEGATORS; } - return false; + return !!hasDelegationToSelectedValidator; }, message: t( 'This validator has reached the network limit for total delegators and therefore cannot be delegated to by new accounts. Please select another validator with fewer than 1200 total delegators' @@ -436,7 +464,10 @@ export const useValidatorPublicKeyRule = ( }); }; -export const useNewValidatorPublicKeyRule = (delegatorsNumber?: number) => { +export const useNewValidatorPublicKeyRule = ( + delegatorsNumber?: number, + hasDelegationToSelectedNewValidator?: boolean +) => { const { t } = useTranslation(); return Yup.string() @@ -449,11 +480,11 @@ export const useNewValidatorPublicKeyRule = (delegatorsNumber?: number) => { .test({ name: 'maxDelegators', test: () => { - if (delegatorsNumber) { + if (delegatorsNumber && !hasDelegationToSelectedNewValidator) { return delegatorsNumber < MAX_DELEGATORS; } - return false; + return !!hasDelegationToSelectedNewValidator; }, message: t( 'This validator has reached the network limit for total delegators and therefore cannot be delegated to by new accounts. Please select another validator with fewer than 1200 total delegators' diff --git a/src/libs/ui/forms/stakes-form.ts b/src/libs/ui/forms/stakes-form.ts index 4ffe878ce..4b40c5660 100644 --- a/src/libs/ui/forms/stakes-form.ts +++ b/src/libs/ui/forms/stakes-form.ts @@ -24,13 +24,19 @@ export type StakeAmountFormValues = { export const useStakesForm = ( amountMotes: string | null, - stakesType: AuctionManagerEntryPoint, + stakeType: AuctionManagerEntryPoint, stakeAmountMotes: string, delegatorsNumber?: number, - delegatorsNumberForNewValidator?: number + delegatorsNumberForNewValidator?: number, + hasDelegationToSelectedValidator?: boolean, + hasDelegationToSelectedNewValidator?: boolean ) => { const validatorFormSchema = Yup.object().shape({ - validatorPublicKey: useValidatorPublicKeyRule(stakesType, delegatorsNumber) + validatorPublicKey: useValidatorPublicKeyRule( + stakeType, + delegatorsNumber, + hasDelegationToSelectedValidator + ) }); const validatorFormOptions: UseFormProps = { @@ -41,7 +47,8 @@ export const useStakesForm = ( const newValidatorFromSchema = Yup.object().shape({ newValidatorPublicKey: useNewValidatorPublicKeyRule( - delegatorsNumberForNewValidator + delegatorsNumberForNewValidator, + hasDelegationToSelectedNewValidator ) }); @@ -52,7 +59,7 @@ export const useStakesForm = ( }; const amountFormSchema = Yup.object().shape({ - amount: useCSPRStakeAmountRule(amountMotes, stakesType, stakeAmountMotes) + amount: useCSPRStakeAmountRule(amountMotes, stakeType, stakeAmountMotes) }); const amountFormOptions: UseFormProps = { diff --git a/src/libs/ui/forms/transfer.ts b/src/libs/ui/forms/transfer.ts index db4e9e604..3724d6145 100644 --- a/src/libs/ui/forms/transfer.ts +++ b/src/libs/ui/forms/transfer.ts @@ -23,12 +23,7 @@ export type TransferAmountFormValues = { transferIdMemo: string; }; -export function useTransferForm( - erc20Balance: string | null, - isErc20: boolean, - amountMotes: string | null, - paymentAmount: string -) { +export const useTransferRecipientForm = () => { const recipientFormSchema = Yup.object().shape({ recipientPublicKey: useRecipientPublicKeyRule() }); @@ -40,8 +35,18 @@ export function useTransferForm( delayError: 500 }; + return useForm(recipientFormOptions); +}; + +export const useTransferAmountForm = ( + erc20Balance: string | null, + isErc20: boolean, + amountMotes: string | null, + paymentAmount: string, + decimals: number | undefined +) => { const erc20AmountFormSchema = Yup.object().shape({ - amount: useErc20AmountRule(erc20Balance), + amount: useErc20AmountRule(erc20Balance, decimals), paymentAmount: usePaymentAmountRule(amountMotes), transferIdMemo: useTransferIdMemoRule() }); @@ -68,8 +73,5 @@ export function useTransferForm( } }; - return { - recipientForm: useForm(recipientFormOptions), - amountForm: useForm(amountFormOptions) - }; -} + return useForm(amountFormOptions); +}; diff --git a/src/manifest.v3.json b/src/manifest.v3.json index b2b5122f5..01519554c 100644 --- a/src/manifest.v3.json +++ b/src/manifest.v3.json @@ -11,7 +11,9 @@ "management", "storage", "tabs", - "declarativeNetRequest" + "declarativeNetRequest", + "clipboardRead", + "clipboardWrite" ], "declarative_net_request": { "rule_resources": [ diff --git a/src/utils.ts b/src/utils.ts index 63a7cf3b3..a1b0afefb 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -399,3 +399,8 @@ export const fetchAndDispatchExtendedDeployInfo = (deployHash: string) => { // Note: this timeout is needed because the deploy is not immediately visible in the explorer }, 2000); }; + +export const getErrorMessageForIncorrectPassword = (attemptsLeft: number) => + attemptsLeft === 1 + ? 'Password is incorrect. You’ve got last attempt, after that you’ll have to wait for 5 mins' + : `Password is incorrect. You’ve got ${attemptsLeft} attempts, after that you’ll have to wait for 5 mins`; diff --git a/xcode-project/Casper Wallet/Casper Wallet.xcodeproj/project.pbxproj b/xcode-project/Casper Wallet/Casper Wallet.xcodeproj/project.pbxproj index 4f85ea8b2..67ac52993 100644 --- a/xcode-project/Casper Wallet/Casper Wallet.xcodeproj/project.pbxproj +++ b/xcode-project/Casper Wallet/Casper Wallet.xcodeproj/project.pbxproj @@ -20,7 +20,6 @@ 2439D7C029F0757C00F07C90 /* connectToApp.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7AA29F0757C00F07C90 /* connectToApp.bundle.js */; }; 2439D7C129F0757C00F07C90 /* logo16.png in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7AB29F0757C00F07C90 /* logo16.png */; }; 2439D7C229F0757C00F07C90 /* connect-to-app.html in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7AC29F0757C00F07C90 /* connect-to-app.html */; }; - 2439D7C329F0757C00F07C90 /* 909.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7AD29F0757C00F07C90 /* 909.bundle.js */; }; 2439D7C429F0757C00F07C90 /* locales in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7AE29F0757C00F07C90 /* locales */; }; 2439D7C529F0757C00F07C90 /* import-account-with-file.html in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7AF29F0757C00F07C90 /* import-account-with-file.html */; }; 2439D7C629F0757C00F07C90 /* sdk.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7B029F0757C00F07C90 /* sdk.bundle.js */; }; @@ -31,7 +30,6 @@ 2439D7CB29F0757C00F07C90 /* logo64.png in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7B529F0757C00F07C90 /* logo64.png */; }; 2439D7CC29F0757C00F07C90 /* signatureRequest.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7B629F0757C00F07C90 /* signatureRequest.bundle.js */; }; 2439D7CD29F0757C00F07C90 /* importAccountWithFile.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7B729F0757C00F07C90 /* importAccountWithFile.bundle.js */; }; - 2439D7CE29F0757C00F07C90 /* 125.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7B829F0757C00F07C90 /* 125.bundle.js */; }; 2439D7CF29F0757C00F07C90 /* manifest.json in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7B929F0757C00F07C90 /* manifest.json */; }; 2439D7D029F0757C00F07C90 /* onboarding.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7BA29F0757C00F07C90 /* onboarding.bundle.js */; }; 2439D7D129F0757C00F07C90 /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7BB29F0757C00F07C90 /* assets */; }; @@ -40,6 +38,13 @@ 2439D7D429F0757C00F07C90 /* onboarding.html in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7BE29F0757C00F07C90 /* onboarding.html */; }; 2439D7D529F0757C00F07C90 /* signature-request.html in Resources */ = {isa = PBXBuildFile; fileRef = 2439D7BF29F0757C00F07C90 /* signature-request.html */; }; B3867CAE2A7B038000BD01B7 /* declarative_net_request_rules.json in Resources */ = {isa = PBXBuildFile; fileRef = B3867CAC2A7B038000BD01B7 /* declarative_net_request_rules.json */; }; + B3C8A9332C21AF6D006B8DA3 /* 314.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = B3C8A92C2C21AF6C006B8DA3 /* 314.bundle.js */; }; + B3C8A9342C21AF6D006B8DA3 /* 355.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = B3C8A92D2C21AF6C006B8DA3 /* 355.bundle.js */; }; + B3C8A9352C21AF6D006B8DA3 /* 83.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = B3C8A92E2C21AF6C006B8DA3 /* 83.bundle.js */; }; + B3C8A9362C21AF6D006B8DA3 /* 176.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = B3C8A92F2C21AF6C006B8DA3 /* 176.bundle.js */; }; + B3C8A9372C21AF6D006B8DA3 /* 878.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = B3C8A9302C21AF6C006B8DA3 /* 878.bundle.js */; }; + B3C8A9382C21AF6D006B8DA3 /* 564.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = B3C8A9312C21AF6C006B8DA3 /* 564.bundle.js */; }; + B3C8A9392C21AF6D006B8DA3 /* 467.bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = B3C8A9322C21AF6C006B8DA3 /* 467.bundle.js */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -86,7 +91,6 @@ 2439D7AA29F0757C00F07C90 /* connectToApp.bundle.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = connectToApp.bundle.js; path = ../../../build/firefox/connectToApp.bundle.js; sourceTree = ""; }; 2439D7AB29F0757C00F07C90 /* logo16.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = logo16.png; path = ../../../build/firefox/logo16.png; sourceTree = ""; }; 2439D7AC29F0757C00F07C90 /* connect-to-app.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = "connect-to-app.html"; path = "../../../build/firefox/connect-to-app.html"; sourceTree = ""; }; - 2439D7AD29F0757C00F07C90 /* 909.bundle.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = 909.bundle.js; path = ../../../build/firefox/909.bundle.js; sourceTree = ""; }; 2439D7AE29F0757C00F07C90 /* locales */ = {isa = PBXFileReference; lastKnownFileType = folder; name = locales; path = ../../../build/firefox/locales; sourceTree = ""; }; 2439D7AF29F0757C00F07C90 /* import-account-with-file.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = "import-account-with-file.html"; path = "../../../build/firefox/import-account-with-file.html"; sourceTree = ""; }; 2439D7B029F0757C00F07C90 /* sdk.bundle.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = sdk.bundle.js; path = ../../../build/firefox/sdk.bundle.js; sourceTree = ""; }; @@ -97,7 +101,6 @@ 2439D7B529F0757C00F07C90 /* logo64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = logo64.png; path = ../../../build/firefox/logo64.png; sourceTree = ""; }; 2439D7B629F0757C00F07C90 /* signatureRequest.bundle.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = signatureRequest.bundle.js; path = ../../../build/firefox/signatureRequest.bundle.js; sourceTree = ""; }; 2439D7B729F0757C00F07C90 /* importAccountWithFile.bundle.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = importAccountWithFile.bundle.js; path = ../../../build/firefox/importAccountWithFile.bundle.js; sourceTree = ""; }; - 2439D7B829F0757C00F07C90 /* 125.bundle.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = 125.bundle.js; path = ../../../build/firefox/125.bundle.js; sourceTree = ""; }; 2439D7B929F0757C00F07C90 /* manifest.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = manifest.json; path = ../../../build/firefox/manifest.json; sourceTree = ""; }; 2439D7BA29F0757C00F07C90 /* onboarding.bundle.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = onboarding.bundle.js; path = ../../../build/firefox/onboarding.bundle.js; sourceTree = ""; }; 2439D7BB29F0757C00F07C90 /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = assets; path = ../../../build/firefox/assets; sourceTree = ""; }; @@ -106,6 +109,13 @@ 2439D7BE29F0757C00F07C90 /* onboarding.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = onboarding.html; path = ../../../build/firefox/onboarding.html; sourceTree = ""; }; 2439D7BF29F0757C00F07C90 /* signature-request.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = "signature-request.html"; path = "../../../build/firefox/signature-request.html"; sourceTree = ""; }; B3867CAC2A7B038000BD01B7 /* declarative_net_request_rules.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = declarative_net_request_rules.json; path = ../../../src/declarative_net_request_rules.json; sourceTree = ""; }; + B3C8A92C2C21AF6C006B8DA3 /* 314.bundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = 314.bundle.js; path = ../../../build/firefox/314.bundle.js; sourceTree = ""; }; + B3C8A92D2C21AF6C006B8DA3 /* 355.bundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = 355.bundle.js; path = ../../../build/firefox/355.bundle.js; sourceTree = ""; }; + B3C8A92E2C21AF6C006B8DA3 /* 83.bundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = 83.bundle.js; path = ../../../build/firefox/83.bundle.js; sourceTree = ""; }; + B3C8A92F2C21AF6C006B8DA3 /* 176.bundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = 176.bundle.js; path = ../../../build/firefox/176.bundle.js; sourceTree = ""; }; + B3C8A9302C21AF6C006B8DA3 /* 878.bundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = 878.bundle.js; path = ../../../build/firefox/878.bundle.js; sourceTree = ""; }; + B3C8A9312C21AF6C006B8DA3 /* 564.bundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = 564.bundle.js; path = ../../../build/firefox/564.bundle.js; sourceTree = ""; }; + B3C8A9322C21AF6C006B8DA3 /* 467.bundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = 467.bundle.js; path = ../../../build/firefox/467.bundle.js; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -184,11 +194,17 @@ 2439D7A929F0757C00F07C90 /* Resources */ = { isa = PBXGroup; children = ( + B3C8A92E2C21AF6C006B8DA3 /* 83.bundle.js */, + B3C8A92F2C21AF6C006B8DA3 /* 176.bundle.js */, + B3C8A92C2C21AF6C006B8DA3 /* 314.bundle.js */, + B3C8A92D2C21AF6C006B8DA3 /* 355.bundle.js */, + B3C8A9322C21AF6C006B8DA3 /* 467.bundle.js */, + B3C8A9312C21AF6C006B8DA3 /* 564.bundle.js */, + B3C8A9302C21AF6C006B8DA3 /* 878.bundle.js */, B3867CAC2A7B038000BD01B7 /* declarative_net_request_rules.json */, 2439D7AA29F0757C00F07C90 /* connectToApp.bundle.js */, 2439D7AB29F0757C00F07C90 /* logo16.png */, 2439D7AC29F0757C00F07C90 /* connect-to-app.html */, - 2439D7AD29F0757C00F07C90 /* 909.bundle.js */, 2439D7AE29F0757C00F07C90 /* locales */, 2439D7AF29F0757C00F07C90 /* import-account-with-file.html */, 2439D7B029F0757C00F07C90 /* sdk.bundle.js */, @@ -199,7 +215,6 @@ 2439D7B529F0757C00F07C90 /* logo64.png */, 2439D7B629F0757C00F07C90 /* signatureRequest.bundle.js */, 2439D7B729F0757C00F07C90 /* importAccountWithFile.bundle.js */, - 2439D7B829F0757C00F07C90 /* 125.bundle.js */, 2439D7B929F0757C00F07C90 /* manifest.json */, 2439D7BA29F0757C00F07C90 /* onboarding.bundle.js */, 2439D7BB29F0757C00F07C90 /* assets */, @@ -310,25 +325,30 @@ 2439D7CB29F0757C00F07C90 /* logo64.png in Resources */, 2439D7D229F0757C00F07C90 /* popup.bundle.js in Resources */, 2439D7C029F0757C00F07C90 /* connectToApp.bundle.js in Resources */, - 2439D7CE29F0757C00F07C90 /* 125.bundle.js in Resources */, 2439D7D129F0757C00F07C90 /* assets in Resources */, 2439D7C129F0757C00F07C90 /* logo16.png in Resources */, 2439D7C729F0757C00F07C90 /* contentScript.bundle.js in Resources */, 2439D7D329F0757C00F07C90 /* logo192.png in Resources */, 2439D7C429F0757C00F07C90 /* locales in Resources */, 2439D7D029F0757C00F07C90 /* onboarding.bundle.js in Resources */, + B3C8A9352C21AF6D006B8DA3 /* 83.bundle.js in Resources */, + B3C8A9342C21AF6D006B8DA3 /* 355.bundle.js in Resources */, 2439D7C829F0757C00F07C90 /* popup.html in Resources */, 2439D7C929F0757C00F07C90 /* background.bundle.js in Resources */, 2439D7C529F0757C00F07C90 /* import-account-with-file.html in Resources */, - 2439D7C329F0757C00F07C90 /* 909.bundle.js in Resources */, 2439D7C629F0757C00F07C90 /* sdk.bundle.js in Resources */, 2439D7CD29F0757C00F07C90 /* importAccountWithFile.bundle.js in Resources */, + B3C8A9392C21AF6D006B8DA3 /* 467.bundle.js in Resources */, + B3C8A9362C21AF6D006B8DA3 /* 176.bundle.js in Resources */, 2439D7CF29F0757C00F07C90 /* manifest.json in Resources */, + B3C8A9332C21AF6D006B8DA3 /* 314.bundle.js in Resources */, 2439D7CC29F0757C00F07C90 /* signatureRequest.bundle.js in Resources */, B3867CAE2A7B038000BD01B7 /* declarative_net_request_rules.json in Resources */, + B3C8A9382C21AF6D006B8DA3 /* 564.bundle.js in Resources */, 2439D7CA29F0757C00F07C90 /* logo128.png in Resources */, 2439D7C229F0757C00F07C90 /* connect-to-app.html in Resources */, 2439D7D529F0757C00F07C90 /* signature-request.html in Resources */, + B3C8A9372C21AF6D006B8DA3 /* 878.bundle.js in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -564,7 +584,7 @@ CODE_SIGN_ENTITLEMENTS = "Casper Wallet/Casper Wallet.entitlements"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 64; + CURRENT_PROJECT_VERSION = 69; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "Casper Wallet/Info.plist"; @@ -578,7 +598,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.10.0; + MARKETING_VERSION = 1.11.0; OTHER_LDFLAGS = ( "-framework", SafariServices, @@ -601,7 +621,7 @@ CODE_SIGN_ENTITLEMENTS = "Casper Wallet/Casper Wallet.entitlements"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 64; + CURRENT_PROJECT_VERSION = 69; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "Casper Wallet/Info.plist"; @@ -615,7 +635,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.10.0; + MARKETING_VERSION = 1.11.0; OTHER_LDFLAGS = ( "-framework", SafariServices,